cloudstack-k8s-upgrader/pkg/upgrader/post-upgrade.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)
}
}