cmd/rotatecerts/operations.go (107 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. package rotatecerts import ( "fmt" "math/rand" "time" "github.com/Azure/aks-engine-azurestack/cmd/rotatecerts/internal" "github.com/Azure/aks-engine-azurestack/pkg/api/common" "github.com/pkg/errors" log "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // PauseClusterAutoscaler scales to zero the replica count of the cluster autoscaler deployment // and returns a function that scales back to the original replica count. // // It NOPs if the original replica count is zero. func PauseClusterAutoscaler(client internal.KubeClient) (func() error, error) { name := common.ClusterAutoscalerAddonName deploy, err := client.GetDeployment(metav1.NamespaceSystem, name) if err != nil && !apierrors.IsNotFound(err) { e := errors.Wrapf(err, "getting %s deployment", name) return func() error { return e }, e } if apierrors.IsNotFound(err) || *deploy.Spec.Replicas == 0 { // autoscaler not present or no replicas, NOP return func() error { return nil }, nil } // autoscaler present patch := func(msg string, count int32) error { log.Info(msg) json := fmt.Sprintf(`{"spec":{"replicas": %d}}`, count) if _, err = client.PatchDeployment(metav1.NamespaceSystem, name, json); err != nil { return errors.Wrapf(err, "applying patch to %s deployment", name) } return nil } // pause autoscaler if err := patch(fmt.Sprintf("Pausing %s, setting replica count to 0", name), 0); err != nil { return func() error { return err }, err } // resume autoscaler func return func() error { c := *deploy.Spec.Replicas err := patch(fmt.Sprintf("Resuming %s, setting replica count to %d", name, c), c) log.Warnln("Run \"aks-engine-azurestack upgrade\" to refresh the cluster-autoscaler node template") if err != nil { return err } return nil }, nil } // RotateServiceAccountTokens deletes all service account tokens and // triggers a forced rollout of all daemonsets and deployments. // // Service account tokens are signed by the cluster CA, // deleting them after the CA is rotated ensures that KCM will regenerate tokens signed by the new CA. func RotateServiceAccountTokens(client internal.KubeClient) error { if err := deleteSATokens(client); err != nil { return err } if err := rolloutDeployments(client); err != nil { return err } if err := rolloutDaemonSets(client); err != nil { return err } // TODO rolloutStatefulSets return nil } func rolloutDeployments(client internal.KubeClient) error { random := rand.New(rand.NewSource(time.Now().UnixNano())) patch := fmt.Sprintf(`{"spec":{"template":{"metadata":{"annotations":{"ca-rotation":"%d"}}}}}`, random.Int31()) deployList, err := client.ListDeployments(metav1.NamespaceAll, metav1.ListOptions{}) if err != nil { return errors.Wrapf(err, "listing cluster deployments") } for _, deploy := range deployList.Items { // trigger rollout so the deploy replicas mount the newly generated sa token if _, err := client.PatchDeployment(deploy.Namespace, deploy.Name, patch); err != nil { return errors.Wrapf(err, "patching %s deployment %s", deploy.Namespace, deploy.Name) } } return nil } func rolloutDaemonSets(client internal.KubeClient) error { random := rand.New(rand.NewSource(time.Now().UnixNano())) patch := fmt.Sprintf(`{"spec":{"template":{"metadata":{"annotations":{"ca-rotation":"%d"}}}}}`, random.Int31()) dsList, err := client.ListDaemonSets(metav1.NamespaceAll, metav1.ListOptions{}) if err != nil { return errors.Wrapf(err, "listing cluster daemonsets") } for _, ds := range dsList.Items { // trigger rollout so the ds replicas mount the newly generated sa token if _, err = client.PatchDaemonSet(ds.Namespace, ds.Name, patch); err != nil { return errors.Wrapf(err, "patching %s daemonset %s", ds.Namespace, ds.Name) } } return nil } func deleteSATokens(client internal.KubeClient) error { saList, err := client.ListServiceAccounts(metav1.NamespaceAll, metav1.ListOptions{}) if err != nil { return errors.Wrapf(err, "listing cluster service accounts") } if len(saList.Items) == 0 { return nil } for _, sa := range saList.Items { for _, s := range sa.Secrets { err := client.DeleteSecret(&v1.Secret{ ObjectMeta: metav1.ObjectMeta{ Namespace: sa.Namespace, Name: s.Name, }, }) if err != nil && !apierrors.IsNotFound(err) { return errors.Wrapf(err, "deleting %s secret %s", s.Namespace, s.Name) } } } return nil }