115 lines
3.4 KiB
Go
115 lines
3.4 KiB
Go
package upgrader
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os/exec"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
yaml "gopkg.in/yaml.v3"
|
|
|
|
"git.csclub.uwaterloo.ca/cloud/cloudstack-k8s-upgrader/pkg/cloudstack"
|
|
)
|
|
|
|
var controlPlaneEndpointPattern = regexp.MustCompile("^https://([0-9.]+):6443/?$")
|
|
|
|
func getControlPlaneIP(endpoint string) string {
|
|
matches := controlPlaneEndpointPattern.FindStringSubmatch(endpoint)
|
|
if matches == nil {
|
|
panic("Could not determine control plane IP from endpoint: " + endpoint)
|
|
}
|
|
return matches[1]
|
|
}
|
|
|
|
func (u *Upgrader) prepareSSHCommand(ipAddress string, args ...string) *exec.Cmd {
|
|
const user = "core"
|
|
log.Debug().
|
|
Str("user", user).
|
|
Str("address", ipAddress).
|
|
Msg("Running `" + strings.Join(args, " ") + "`")
|
|
args = append(
|
|
[]string{
|
|
"-i", u.cfg.SSHKeyPath,
|
|
"-o", "PasswordAuthentication=no",
|
|
"-o", "IdentitiesOnly=yes",
|
|
"-o", "StrictHostKeyChecking=no",
|
|
"-o", "CheckHostIP=no",
|
|
"-o", "UserKnownHostsFile=/dev/null",
|
|
"-o", "LogLevel=ERROR",
|
|
// -T is needed to pipe stdin
|
|
"-T",
|
|
user + "@" + ipAddress,
|
|
},
|
|
args...,
|
|
)
|
|
return exec.Command("ssh", args...)
|
|
}
|
|
|
|
const kubeControllerManagerConfigPath = "/etc/kubernetes/manifests/kube-controller-manager.yaml"
|
|
const clusterSigningDurationArg = "--cluster-signing-duration=87600h"
|
|
|
|
func interfaceSliceToStringSlice(arr1 []interface{}) []string {
|
|
arr2 := make([]string, len(arr1))
|
|
for i, elem := range arr1 {
|
|
arr2[i] = elem.(string)
|
|
}
|
|
return arr2
|
|
}
|
|
|
|
func (u *Upgrader) editKubeControllerManagerFlags(cluster *cloudstack.KubernetesClusterResponse) {
|
|
controlPlaneIP := getControlPlaneIP(cluster.Endpoint)
|
|
cmd := u.prepareSSHCommand(controlPlaneIP, "sudo", "cat", kubeControllerManagerConfigPath)
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
manifest := make(map[string]interface{})
|
|
err = yaml.Unmarshal(output, &manifest)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
containers := manifest["spec"].(map[string]interface{})["containers"].([]interface{})
|
|
if len(containers) != 1 {
|
|
panic(fmt.Sprintf("Expected one command to be present, found %d", len(containers)))
|
|
}
|
|
container := containers[0].(map[string]interface{})
|
|
kubeControllerManagerArgv := interfaceSliceToStringSlice(container["command"].([]interface{}))
|
|
if kubeControllerManagerArgv[0] != "kube-controller-manager" {
|
|
panic(fmt.Sprintf("Expected argv[0] to be kube-controller-manager, got %s", kubeControllerManagerArgv[0]))
|
|
}
|
|
for _, arg := range kubeControllerManagerArgv[1:] {
|
|
if arg == clusterSigningDurationArg {
|
|
log.Debug().Msg("Found cluster signing duration arg, skipping")
|
|
return
|
|
}
|
|
}
|
|
// Insert the arg but keep argv[1:] sorted
|
|
newArgv := make([]string, 0, len(kubeControllerManagerArgv)+1)
|
|
newArgv = append(newArgv, kubeControllerManagerArgv[0])
|
|
insertedNewArg := false
|
|
for _, arg := range kubeControllerManagerArgv[1:] {
|
|
if !insertedNewArg && strings.Compare(arg, clusterSigningDurationArg) > 0 {
|
|
newArgv = append(newArgv, clusterSigningDurationArg)
|
|
insertedNewArg = true
|
|
}
|
|
newArgv = append(newArgv, arg)
|
|
}
|
|
if !insertedNewArg {
|
|
newArgv = append(newArgv, clusterSigningDurationArg)
|
|
}
|
|
container["command"] = newArgv
|
|
marshalledNewManifest, err := yaml.Marshal(&manifest)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
cmd = u.prepareSSHCommand(controlPlaneIP, "sudo", "tee", kubeControllerManagerConfigPath)
|
|
cmd.Stdin = bytes.NewReader(marshalledNewManifest)
|
|
cmd.Stdout = nil
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|