663 lines
21 KiB
Go
663 lines
21 KiB
Go
package rancher
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/mcuadros/go-version"
|
|
"github.com/rancher/norman/condition"
|
|
v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
|
|
rkev1 "github.com/rancher/rancher/pkg/apis/rke.cattle.io/v1"
|
|
"github.com/rancher/rancher/pkg/auth/tokens"
|
|
"github.com/rancher/rancher/pkg/capr"
|
|
"github.com/rancher/rancher/pkg/controllers/management/clusterprovisioner"
|
|
"github.com/rancher/rancher/pkg/features"
|
|
v3 "github.com/rancher/rancher/pkg/generated/controllers/management.cattle.io/v3"
|
|
rancherversion "github.com/rancher/rancher/pkg/version"
|
|
"github.com/rancher/rancher/pkg/wrangler"
|
|
"github.com/rancher/wrangler/v3/pkg/data"
|
|
"github.com/rancher/wrangler/v3/pkg/data/convert"
|
|
controllerv1 "github.com/rancher/wrangler/v3/pkg/generated/controllers/core/v1"
|
|
"github.com/rancher/wrangler/v3/pkg/summary"
|
|
"github.com/sirupsen/logrus"
|
|
"golang.org/x/mod/semver"
|
|
v1 "k8s.io/api/core/v1"
|
|
k8serror "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/client-go/util/retry"
|
|
capi "sigs.k8s.io/cluster-api/api/v1beta1"
|
|
)
|
|
|
|
const (
|
|
cattleNamespace = "cattle-system"
|
|
forceUpgradeLogoutConfig = "forceupgradelogout"
|
|
forceLocalSystemAndDefaultProjectCreation = "forcelocalprojectcreation"
|
|
forceSystemNamespacesAssignment = "forcesystemnamespaceassignment"
|
|
migrateFromMachineToPlanSecret = "migratefrommachinetoplanesecret"
|
|
migrateEncryptionKeyRotationLeaderToStatus = "migrateencryptionkeyrotationleadertostatus"
|
|
migrateDynamicSchemaToMachinePools = "migratedynamicschematomachinepools"
|
|
migrateRKEClusterState = "migraterkeclusterstate"
|
|
migrateSystemAgentVarDirToDataDirectory = "migratesystemagentvardirtodatadirectory"
|
|
rancherVersionKey = "rancherVersion"
|
|
projectsCreatedKey = "projectsCreated"
|
|
namespacesAssignedKey = "namespacesAssigned"
|
|
capiMigratedKey = "capiMigrated"
|
|
encryptionKeyRotationStatusMigratedKey = "encryptionKeyRotationStatusMigrated"
|
|
dynamicSchemaMachinePoolsMigratedKey = "dynamicSchemaMachinePoolsMigrated"
|
|
rkeClustersAnnotatedForMigrationKey = "rkeClustersAnnotatedForMigration"
|
|
systemAgentVarDirMigratedKey = "systemAgentVarDirMigrated"
|
|
)
|
|
|
|
func runMigrations(wranglerContext *wrangler.Context) error {
|
|
if err := forceUpgradeLogout(wranglerContext.Core.ConfigMap(), wranglerContext.Mgmt.Token(), "v2.6.0"); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := forceSystemAndDefaultProjectCreation(wranglerContext.Core.ConfigMap(), wranglerContext.Mgmt.Cluster()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if features.MCM.Enabled() {
|
|
if err := forceSystemNamespaceAssignment(wranglerContext.Core.ConfigMap(), wranglerContext.Mgmt.Project()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if features.RKE2.Enabled() {
|
|
// must migrate system agent data directory first, since update requests will be rejected by webhook if
|
|
// "CATTLE_AGENT_VAR_DIR" is set within AgentEnvVars.
|
|
if err := migrateSystemAgentDataDirectory(wranglerContext); err != nil {
|
|
return err
|
|
}
|
|
if err := migrateCAPIMachineLabelsAndAnnotationsToPlanSecret(wranglerContext); err != nil {
|
|
return err
|
|
}
|
|
if err := migrateEncryptionKeyRotationLeader(wranglerContext); err != nil {
|
|
return err
|
|
}
|
|
if err := migrateMachinePoolsDynamicSchemaLabel(wranglerContext); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return migrateRKEClusterStates(wranglerContext)
|
|
}
|
|
|
|
func getConfigMap(configMapController controllerv1.ConfigMapController, configMapName string) (*v1.ConfigMap, error) {
|
|
cm, err := configMapController.Cache().Get(cattleNamespace, configMapName)
|
|
if err != nil && !k8serror.IsNotFound(err) {
|
|
return nil, err
|
|
}
|
|
|
|
// if this is the first ever migration initialize the configmap
|
|
if cm == nil {
|
|
cm = &v1.ConfigMap{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: configMapName,
|
|
Namespace: cattleNamespace,
|
|
},
|
|
Data: make(map[string]string, 1),
|
|
}
|
|
}
|
|
|
|
// we do not migrate in development environments
|
|
if rancherversion.Version == "dev" {
|
|
return nil, nil
|
|
}
|
|
|
|
return cm, nil
|
|
}
|
|
|
|
func createOrUpdateConfigMap(configMapClient controllerv1.ConfigMapClient, cm *v1.ConfigMap) error {
|
|
var err error
|
|
if cm.ObjectMeta.GetResourceVersion() != "" {
|
|
_, err = configMapClient.Update(cm)
|
|
} else {
|
|
_, err = configMapClient.Create(cm)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// forceUpgradeLogout will delete all dashboard tokens forcing a logout. This is useful when there is a major frontend
|
|
// upgrade and we want all users to be sent to a central point. This function will check for the `forceUpgradeLogoutConfig`
|
|
// configuration map and only run if the last migrated version is lower than the given `migrationVersion`.
|
|
func forceUpgradeLogout(configMapController controllerv1.ConfigMapController, tokenController v3.TokenController, migrationVersion string) error {
|
|
cm, err := getConfigMap(configMapController, forceUpgradeLogoutConfig)
|
|
if err != nil || cm == nil {
|
|
return err
|
|
}
|
|
|
|
// if no last migration is found we always run force logout
|
|
if lastMigration, ok := cm.Data[rancherVersionKey]; ok {
|
|
|
|
// if a valid sem ver is found we only migrate if the version is less than the target version
|
|
if semver.IsValid(lastMigration) && semver.IsValid(rancherversion.Version) && version.Compare(migrationVersion, lastMigration, "<=") {
|
|
return nil
|
|
}
|
|
|
|
// if an unknown format is given we migrate any time the current version does not equal the last migration
|
|
if lastMigration == rancherversion.Version {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
logrus.Infof("Detected %s upgrade, forcing logout for all web users", migrationVersion)
|
|
|
|
// list all tokens that were created for the dashboard
|
|
allTokens, err := tokenController.Cache().List(labels.SelectorFromSet(labels.Set{tokens.TokenKindLabel: "session"}))
|
|
if err != nil {
|
|
logrus.Error("Failed to list tokens for upgrade forced logout")
|
|
return err
|
|
}
|
|
|
|
// log out all the dashboard users forcing them to be redirected to the login page
|
|
for _, token := range allTokens {
|
|
err = tokenController.Delete(token.ObjectMeta.Name, &metav1.DeleteOptions{})
|
|
if err != nil && !k8serror.IsNotFound(err) {
|
|
logrus.Errorf("Failed to delete token [%s] for upgrade forced logout", token.Name)
|
|
}
|
|
}
|
|
|
|
cm.Data[rancherVersionKey] = rancherversion.Version
|
|
return createOrUpdateConfigMap(configMapController, cm)
|
|
}
|
|
|
|
// forceSystemAndDefaultProjectCreation will set the corresponding conditions on the local cluster object,
|
|
// if it exists, to Unknown. This will force the corresponding controller to check that the projects exist
|
|
// and create them, if necessary.
|
|
func forceSystemAndDefaultProjectCreation(configMapController controllerv1.ConfigMapController, clusterClient v3.ClusterClient) error {
|
|
cm, err := getConfigMap(configMapController, forceLocalSystemAndDefaultProjectCreation)
|
|
if err != nil || cm == nil {
|
|
return err
|
|
}
|
|
|
|
if cm.Data[projectsCreatedKey] == "true" {
|
|
return nil
|
|
}
|
|
|
|
if err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
|
localCluster, err := clusterClient.Get("local", metav1.GetOptions{})
|
|
if err != nil {
|
|
if k8serror.IsNotFound(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
v32.ClusterConditionDefaultProjectCreated.Unknown(localCluster)
|
|
v32.ClusterConditionSystemProjectCreated.Unknown(localCluster)
|
|
|
|
_, err = clusterClient.Update(localCluster)
|
|
return err
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
cm.Data[projectsCreatedKey] = "true"
|
|
return createOrUpdateConfigMap(configMapController, cm)
|
|
}
|
|
|
|
func forceSystemNamespaceAssignment(configMapController controllerv1.ConfigMapController, projectClient v3.ProjectClient) error {
|
|
cm, err := getConfigMap(configMapController, forceSystemNamespacesAssignment)
|
|
if err != nil || cm == nil {
|
|
return err
|
|
}
|
|
|
|
if cm.Data[namespacesAssignedKey] == rancherversion.Version {
|
|
return nil
|
|
}
|
|
|
|
err = applyProjectConditionForNamespaceAssignment("authz.management.cattle.io/system-project=true", v32.ProjectConditionSystemNamespacesAssigned, projectClient)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = applyProjectConditionForNamespaceAssignment("authz.management.cattle.io/default-project=true", v32.ProjectConditionDefaultNamespacesAssigned, projectClient)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cm.Data[namespacesAssignedKey] = rancherversion.Version
|
|
return createOrUpdateConfigMap(configMapController, cm)
|
|
}
|
|
|
|
func applyProjectConditionForNamespaceAssignment(label string, condition condition.Cond, projectClient v3.ProjectClient) error {
|
|
projects, err := projectClient.List("", metav1.ListOptions{LabelSelector: label})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i := range projects.Items {
|
|
if err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
|
p := &projects.Items[i]
|
|
p, err = projectClient.Get(p.Namespace, p.Name, metav1.GetOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
condition.Unknown(p)
|
|
_, err = projectClient.Update(p)
|
|
return err
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// migrateRKEClusterStates sets the `RKEForceUpdate` annotation on all management cluster objects for
|
|
// RKE-provisioned clusters.
|
|
func migrateRKEClusterStates(w *wrangler.Context) error {
|
|
cm, err := getConfigMap(w.Core.ConfigMap(), migrateRKEClusterState)
|
|
if err != nil {
|
|
return fmt.Errorf("error getting configmap %s: %w", migrateRKEClusterState, err)
|
|
} else if cm == nil {
|
|
return nil
|
|
}
|
|
|
|
// Check if this migration has already run.
|
|
if cm.Data[rkeClustersAnnotatedForMigrationKey] == "true" {
|
|
return nil
|
|
}
|
|
|
|
// Collect all RKE clusters that need migration.
|
|
mgmtClusters, err := w.Mgmt.Cluster().List(metav1.ListOptions{})
|
|
if err != nil {
|
|
return fmt.Errorf("error listing management clusters: %w", err)
|
|
}
|
|
|
|
// Mark all RKE clusters for migration.
|
|
for _, cluster := range mgmtClusters.Items {
|
|
// Skip this cluster if it's not RKE-provisioned.
|
|
if cluster.Spec.RancherKubernetesEngineConfig == nil {
|
|
continue
|
|
}
|
|
|
|
// Retry the cluster object status update on conflict in case something else is updating it at the same time.
|
|
if err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
|
c, err := w.Mgmt.Cluster().Get(cluster.Name, metav1.GetOptions{})
|
|
if err != nil {
|
|
return fmt.Errorf("error getting cluster %s: %w", cluster.Name, err)
|
|
}
|
|
|
|
clusterCopy := c.DeepCopy()
|
|
clusterCopy.Annotations[clusterprovisioner.RKEForceUpdate] = "true"
|
|
|
|
if _, err = w.Mgmt.Cluster().Update(clusterCopy); err != nil {
|
|
return fmt.Errorf("error updating cluster %s: %w", cluster.Name, err)
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
logrus.Errorf("error updating annotation for cluster %s: %s", cluster.Name, err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
cm.Data = map[string]string{
|
|
rkeClustersAnnotatedForMigrationKey: "true",
|
|
}
|
|
|
|
// Update the configmap that indicates that this migration is complete.
|
|
if err := retry.OnError(retry.DefaultBackoff, func(err error) bool {
|
|
// Retry all errors.
|
|
return true
|
|
}, func() error {
|
|
return createOrUpdateConfigMap(w.Core.ConfigMap(), cm)
|
|
}); err != nil {
|
|
return fmt.Errorf("error updating configmap %s: %w", cm.Name, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func migrateCAPIMachineLabelsAndAnnotationsToPlanSecret(w *wrangler.Context) error {
|
|
cm, err := getConfigMap(w.Core.ConfigMap(), migrateFromMachineToPlanSecret)
|
|
if err != nil || cm == nil {
|
|
return err
|
|
}
|
|
|
|
if cm.Data[capiMigratedKey] == "true" {
|
|
return nil
|
|
}
|
|
|
|
mgmtClusters, err := w.Mgmt.Cluster().List(metav1.ListOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
bootstrapLabelExcludes := map[string]struct{}{
|
|
capr.InitNodeMachineIDLabel: {},
|
|
capr.InitNodeLabel: {},
|
|
}
|
|
|
|
boostrapAnnotationExcludes := map[string]struct{}{
|
|
capr.DrainAnnotation: {},
|
|
capr.DrainDoneAnnotation: {},
|
|
capr.JoinURLAnnotation: {},
|
|
capr.PostDrainAnnotation: {},
|
|
capr.PreDrainAnnotation: {},
|
|
capr.UnCordonAnnotation: {},
|
|
}
|
|
|
|
for _, mgmtCluster := range mgmtClusters.Items {
|
|
provClusters, err := w.Provisioning.Cluster().List(mgmtCluster.Spec.FleetWorkspaceName, metav1.ListOptions{})
|
|
if k8serror.IsNotFound(err) || len(provClusters.Items) == 0 {
|
|
continue
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, provCluster := range provClusters.Items {
|
|
machines, err := w.CAPI.Machine().List(provCluster.Namespace, metav1.ListOptions{LabelSelector: labels.Set{capi.ClusterNameLabel: provCluster.Name}.String()})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
otherMachines, err := w.CAPI.Machine().List(provCluster.Namespace, metav1.ListOptions{LabelSelector: labels.Set{capr.ClusterNameLabel: provCluster.Name}.String()})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
allMachines := append(machines.Items, otherMachines.Items...)
|
|
|
|
for _, machine := range allMachines {
|
|
if machine.Spec.Bootstrap.ConfigRef == nil || machine.Spec.Bootstrap.ConfigRef.APIVersion != capr.RKEAPIVersion {
|
|
continue
|
|
}
|
|
|
|
planSecrets, err := w.Core.Secret().List(machine.Namespace, metav1.ListOptions{LabelSelector: labels.Set{capr.MachineNameLabel: machine.Name}.String()})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(planSecrets.Items) == 0 {
|
|
continue
|
|
}
|
|
|
|
for _, secret := range planSecrets.Items {
|
|
if err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
|
secret, err := w.Core.Secret().Get(secret.Namespace, secret.Name, metav1.GetOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
secret = secret.DeepCopy()
|
|
capr.CopyMap(secret.Labels, machine.Labels)
|
|
capr.CopyMap(secret.Annotations, machine.Annotations)
|
|
_, err = w.Core.Secret().Update(secret)
|
|
return err
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
|
bootstrap, err := w.RKE.RKEBootstrap().Get(machine.Spec.Bootstrap.ConfigRef.Namespace, machine.Spec.Bootstrap.ConfigRef.Name, metav1.GetOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bootstrap = bootstrap.DeepCopy()
|
|
capr.CopyMapWithExcludes(bootstrap.Labels, machine.Labels, bootstrapLabelExcludes)
|
|
capr.CopyMapWithExcludes(bootstrap.Annotations, machine.Annotations, boostrapAnnotationExcludes)
|
|
if bootstrap.Spec.ClusterName == "" {
|
|
// If the bootstrap spec cluster name is blank, we need to update the bootstrap spec to the correct value
|
|
// This is to handle old rkebootstrap objects for unmanaged clusters that did not have the spec properly set
|
|
if v, ok := bootstrap.Labels[capi.ClusterNameLabel]; ok && v != "" {
|
|
bootstrap.Spec.ClusterName = v
|
|
}
|
|
}
|
|
_, err = w.RKE.RKEBootstrap().Update(bootstrap)
|
|
return err
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
if machine.Spec.InfrastructureRef.APIVersion == capr.RKEAPIVersion || machine.Spec.InfrastructureRef.APIVersion == capr.RKEMachineAPIVersion {
|
|
gv, err := schema.ParseGroupVersion(machine.Spec.InfrastructureRef.APIVersion)
|
|
if err != nil {
|
|
// This error should not occur because RKEAPIVersion and RKEMachineAPIVersion are valid
|
|
continue
|
|
}
|
|
|
|
gvk := schema.GroupVersionKind{
|
|
Group: gv.Group,
|
|
Version: gv.Version,
|
|
Kind: machine.Spec.InfrastructureRef.Kind,
|
|
}
|
|
if err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
|
infraMachine, err := w.Dynamic.Get(gvk, machine.Spec.InfrastructureRef.Namespace, machine.Spec.InfrastructureRef.Name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
d, err := data.Convert(infraMachine.DeepCopyObject())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if changed, err := insertOrUpdateCondition(d, summary.NewCondition("Ready", "True", "", "")); err != nil {
|
|
return err
|
|
} else if changed {
|
|
_, err = w.Dynamic.UpdateStatus(&unstructured.Unstructured{Object: d})
|
|
return err
|
|
}
|
|
return err
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cm.Data[capiMigratedKey] = "true"
|
|
return createOrUpdateConfigMap(w.Core.ConfigMap(), cm)
|
|
}
|
|
|
|
func migrateEncryptionKeyRotationLeader(w *wrangler.Context) error {
|
|
cm, err := getConfigMap(w.Core.ConfigMap(), migrateEncryptionKeyRotationLeaderToStatus)
|
|
if err != nil || cm == nil {
|
|
return err
|
|
}
|
|
|
|
if cm.Data[encryptionKeyRotationStatusMigratedKey] == "true" {
|
|
return nil
|
|
}
|
|
|
|
mgmtClusters, err := w.Mgmt.Cluster().List(metav1.ListOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, mgmtCluster := range mgmtClusters.Items {
|
|
if err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
|
cp, err := w.RKE.RKEControlPlane().Get(mgmtCluster.Spec.FleetWorkspaceName, mgmtCluster.Name, metav1.GetOptions{})
|
|
if k8serror.IsNotFound(err) {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
leader := cp.Annotations["rke.cattle.io/encrypt-key-rotation-leader"]
|
|
if leader == "" {
|
|
return nil
|
|
}
|
|
cp = cp.DeepCopy()
|
|
cp.Status.RotateEncryptionKeysLeader = leader
|
|
cp, err = w.RKE.RKEControlPlane().UpdateStatus(cp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cp = cp.DeepCopy()
|
|
delete(cp.Annotations, "rke.cattle.io/encrypt-key-rotation-leader")
|
|
cp, err = w.RKE.RKEControlPlane().Update(cp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
cm.Data[encryptionKeyRotationStatusMigratedKey] = "true"
|
|
return createOrUpdateConfigMap(w.Core.ConfigMap(), cm)
|
|
}
|
|
|
|
func migrateMachinePoolsDynamicSchemaLabel(w *wrangler.Context) error {
|
|
cm, err := getConfigMap(w.Core.ConfigMap(), migrateDynamicSchemaToMachinePools)
|
|
if err != nil || cm == nil {
|
|
return err
|
|
}
|
|
|
|
if cm.Data[dynamicSchemaMachinePoolsMigratedKey] == "true" {
|
|
return nil
|
|
}
|
|
|
|
mgmtClusters, err := w.Mgmt.Cluster().List(metav1.ListOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, mgmtCluster := range mgmtClusters.Items {
|
|
provClusters, err := w.Provisioning.Cluster().List(mgmtCluster.Spec.FleetWorkspaceName, metav1.ListOptions{})
|
|
if k8serror.IsNotFound(err) || len(provClusters.Items) == 0 {
|
|
continue
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
for _, provCluster := range provClusters.Items {
|
|
if provCluster.Spec.RKEConfig == nil {
|
|
continue
|
|
}
|
|
if err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
|
cluster, err := w.Provisioning.Cluster().Get(provCluster.Namespace, provCluster.Name, metav1.GetOptions{})
|
|
if k8serror.IsNotFound(err) {
|
|
return nil
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
cluster = cluster.DeepCopy()
|
|
// search for machine pools without the `dynamic-schema-spec` annotation and apply it
|
|
for i, machinePool := range cluster.Spec.RKEConfig.MachinePools {
|
|
var spec v32.DynamicSchemaSpec
|
|
if machinePool.DynamicSchemaSpec != "" && json.Unmarshal([]byte(machinePool.DynamicSchemaSpec), &spec) == nil {
|
|
continue
|
|
}
|
|
nodeConfig := machinePool.NodeConfig
|
|
if nodeConfig == nil {
|
|
return fmt.Errorf("machine pool node config must not be nil")
|
|
}
|
|
apiVersion := nodeConfig.APIVersion
|
|
if apiVersion != capr.DefaultMachineConfigAPIVersion && apiVersion != "" {
|
|
continue
|
|
}
|
|
ds, err := w.Mgmt.DynamicSchema().Get(strings.ToLower(nodeConfig.Kind), metav1.GetOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
specJSON, err := json.Marshal(ds.Spec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cluster.Spec.RKEConfig.MachinePools[i].DynamicSchemaSpec = string(specJSON)
|
|
}
|
|
_, err = w.Provisioning.Cluster().Update(cluster)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
cm.Data[dynamicSchemaMachinePoolsMigratedKey] = "true"
|
|
return createOrUpdateConfigMap(w.Core.ConfigMap(), cm)
|
|
}
|
|
|
|
func migrateSystemAgentDataDirectory(w *wrangler.Context) error {
|
|
cm, err := getConfigMap(w.Core.ConfigMap(), migrateSystemAgentVarDirToDataDirectory)
|
|
if err != nil || cm == nil {
|
|
return err
|
|
}
|
|
|
|
if cm.Data[systemAgentVarDirMigratedKey] == "true" {
|
|
return nil
|
|
}
|
|
|
|
provClusters, err := w.Provisioning.Cluster().List("", metav1.ListOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, cluster := range provClusters.Items {
|
|
systemAgentDataDir := ""
|
|
envVars := make([]rkev1.EnvVar, 0, len(cluster.Spec.AgentEnvVars))
|
|
for _, e := range cluster.Spec.AgentEnvVars {
|
|
if e.Name == capr.SystemAgentDataDirEnvVar {
|
|
// don't break, the webhook allows duplicate entries and the last one would have been the effective data dir
|
|
systemAgentDataDir = e.Value
|
|
} else {
|
|
envVars = append(envVars, e)
|
|
}
|
|
}
|
|
if systemAgentDataDir == "" {
|
|
continue
|
|
}
|
|
|
|
cluster = *cluster.DeepCopy()
|
|
cluster.Spec.AgentEnvVars = envVars
|
|
cluster.Spec.RKEConfig.DataDirectories.SystemAgent = systemAgentDataDir
|
|
_, err = w.Provisioning.Cluster().Update(&cluster)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
cm.Data[systemAgentVarDirMigratedKey] = "true"
|
|
return createOrUpdateConfigMap(w.Core.ConfigMap(), cm)
|
|
}
|
|
|
|
func insertOrUpdateCondition(d data.Object, desiredCondition summary.Condition) (bool, error) {
|
|
for _, cond := range summary.GetUnstructuredConditions(d) {
|
|
if desiredCondition.Equals(cond) {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
// The conditions must be converted to a map so that DeepCopyJSONValue will
|
|
// recognize it as a map instead of a data.Object.
|
|
newCond, err := convert.EncodeToMap(desiredCondition.Object)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
dConditions := d.Slice("status", "conditions")
|
|
conditions := make([]interface{}, len(dConditions))
|
|
found := false
|
|
for i, cond := range dConditions {
|
|
if cond.String("type") == desiredCondition.Type() {
|
|
conditions[i] = newCond
|
|
found = true
|
|
} else {
|
|
conditions[i], err = convert.EncodeToMap(cond)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
conditions = append(conditions, newCond)
|
|
}
|
|
d.SetNested(conditions, "status", "conditions")
|
|
|
|
return true, nil
|
|
}
|