Skip to content

Commit

Permalink
ensure to not downgrade an auto-upgraded cluster (#95)
Browse files Browse the repository at this point in the history
* ensure to not downgrade an auto-upgraded cluster
  • Loading branch information
LochanRn authored Sep 27, 2023
1 parent b6cce35 commit 5a53d64
Show file tree
Hide file tree
Showing 18 changed files with 673 additions and 40 deletions.
42 changes: 42 additions & 0 deletions azure/scope/managedcontrolplane.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ func (s *ManagedControlPlaneScope) ManagedClusterAnnotations() map[string]string
return s.ControlPlane.Annotations
}

// IsLocalAcountsDisabled checks if local accounts have been disabled.
func (s *ManagedControlPlaneScope) IsLocalAcountsDisabled() bool {
if s.IsAadEnabled() &&
s.ControlPlane.Spec.DisableLocalAccounts != nil &&
Expand All @@ -423,13 +424,54 @@ func (s *ManagedControlPlaneScope) IsLocalAcountsDisabled() bool {
return false
}

// IsAadEnabled checks if aad is enabled.
func (s *ManagedControlPlaneScope) IsAadEnabled() bool {
if s.ControlPlane.Spec.AADProfile != nil && s.ControlPlane.Spec.AADProfile.Managed {
return true
}
return false
}

// SetAutoUpgradeVersionStatus sets the auto upgrade version in status
func (s *ManagedControlPlaneScope) SetAutoUpgradeVersionStatus(version string) {
s.ControlPlane.Status.AutoUpgradeVersion = version
}

// IsManagedVersionUpgrade checks if auto upgrade profile is set and if the upgradeChannel is of the type patch, stable or rapid.
func (s *ManagedControlPlaneScope) IsManagedVersionUpgrade() bool {
return s.IsPatchAutoUpgrade() || s.IsStableAutoUpgrade() || s.IsRapidAutoUpgrade()
}

// IsAutoUpgradeStable checks if auto upgrade channel is stable.
func (s *ManagedControlPlaneScope) IsStableAutoUpgrade() bool {
if s.ControlPlane.Spec.AutoUpgradeProfile != nil {
if upgradeChannel := s.ControlPlane.Spec.AutoUpgradeProfile.UpgradeChannel; upgradeChannel == infrav1exp.UpgradeChannelStable {
return true
}
}
return false
}

// IsAutoUpgradeStable checks if auto upgrade channel is rapid.
func (s *ManagedControlPlaneScope) IsRapidAutoUpgrade() bool {
if s.ControlPlane.Spec.AutoUpgradeProfile != nil {
if upgradeChannel := s.ControlPlane.Spec.AutoUpgradeProfile.UpgradeChannel; upgradeChannel == infrav1exp.UpgradeChannelRapid {
return true
}
}
return false
}

// IsAutoUpgradeStable checks if auto upgrade channel is patch.
func (s *ManagedControlPlaneScope) IsPatchAutoUpgrade() bool {
if s.ControlPlane.Spec.AutoUpgradeProfile != nil {
if upgradeChannel := s.ControlPlane.Spec.AutoUpgradeProfile.UpgradeChannel; upgradeChannel == infrav1exp.UpgradeChannelPatch {
return true
}
}
return false
}

// ManagedClusterSpec returns the managed cluster spec.
func (s *ManagedControlPlaneScope) ManagedClusterSpec(ctx context.Context) azure.ResourceSpecGetter {
managedClusterSpec := managedclusters.ManagedClusterSpec{
Expand Down
215 changes: 215 additions & 0 deletions azure/scope/managedcontrolplane_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -792,3 +792,218 @@ func TestIsLocalAcountsDisabled(t *testing.T) {
})
}
}

func Test_IsManagedVersionUpgrade(t *testing.T) {
scheme := runtime.NewScheme()
_ = capiv1exp.AddToScheme(scheme)
_ = infrav1.AddToScheme(scheme)

cases := []struct {
Name string
Input ManagedControlPlaneScopeParams
Expected bool
}{
{
Name: "Upgrade channel not set",
Input: ManagedControlPlaneScopeParams{
AzureClients: AzureClients{
Authorizer: autorest.NullAuthorizer{},
},
Cluster: &clusterv1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster1",
Namespace: "default",
},
},
ControlPlane: &infrav1.AzureManagedControlPlane{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster1",
Namespace: "default",
},
Spec: infrav1.AzureManagedControlPlaneSpec{
SubscriptionID: "00000000-0000-0000-0000-000000000000",
},
},
ManagedMachinePools: []ManagedMachinePool{
{
MachinePool: getMachinePool("pool0"),
InfraMachinePool: getAzureMachinePool("pool0", infrav1.NodePoolModeSystem),
},
},
},
Expected: false,
},
{
Name: "Upgradechannel is set rapid",
Input: ManagedControlPlaneScopeParams{
AzureClients: AzureClients{
Authorizer: autorest.NullAuthorizer{},
},
Cluster: &clusterv1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster1",
Namespace: "default",
},
},
ControlPlane: &infrav1.AzureManagedControlPlane{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster1",
Namespace: "default",
},
Spec: infrav1.AzureManagedControlPlaneSpec{
AutoUpgradeProfile: &infrav1.ManagedClusterAutoUpgradeProfile{
UpgradeChannel: infrav1.UpgradeChannelRapid,
},
},
},
ManagedMachinePools: []ManagedMachinePool{
{
MachinePool: getMachinePool("pool0"),
InfraMachinePool: getAzureMachinePool("pool0", infrav1.NodePoolModeSystem),
},
},
},
Expected: true,
},
{
Name: "Upgradechannel is set patch",
Input: ManagedControlPlaneScopeParams{
AzureClients: AzureClients{
Authorizer: autorest.NullAuthorizer{},
},
Cluster: &clusterv1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster1",
Namespace: "default",
},
},
ControlPlane: &infrav1.AzureManagedControlPlane{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster1",
Namespace: "default",
},
Spec: infrav1.AzureManagedControlPlaneSpec{
AutoUpgradeProfile: &infrav1.ManagedClusterAutoUpgradeProfile{
UpgradeChannel: infrav1.UpgradeChannelPatch,
},
},
},
ManagedMachinePools: []ManagedMachinePool{
{
MachinePool: getMachinePool("pool0"),
InfraMachinePool: getAzureMachinePool("pool0", infrav1.NodePoolModeSystem),
},
},
},
Expected: true,
},
{
Name: "Upgradechannel is set stable",
Input: ManagedControlPlaneScopeParams{
AzureClients: AzureClients{
Authorizer: autorest.NullAuthorizer{},
},
Cluster: &clusterv1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster1",
Namespace: "default",
},
},
ControlPlane: &infrav1.AzureManagedControlPlane{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster1",
Namespace: "default",
},
Spec: infrav1.AzureManagedControlPlaneSpec{
AutoUpgradeProfile: &infrav1.ManagedClusterAutoUpgradeProfile{
UpgradeChannel: infrav1.UpgradeChannelStable,
},
},
},
ManagedMachinePools: []ManagedMachinePool{
{
MachinePool: getMachinePool("pool0"),
InfraMachinePool: getAzureMachinePool("pool0", infrav1.NodePoolModeSystem),
},
},
},
Expected: true,
},
{
Name: "Upgradechannel is set none",
Input: ManagedControlPlaneScopeParams{
AzureClients: AzureClients{
Authorizer: autorest.NullAuthorizer{},
},
Cluster: &clusterv1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster1",
Namespace: "default",
},
},
ControlPlane: &infrav1.AzureManagedControlPlane{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster1",
Namespace: "default",
},
Spec: infrav1.AzureManagedControlPlaneSpec{
AutoUpgradeProfile: &infrav1.ManagedClusterAutoUpgradeProfile{
UpgradeChannel: infrav1.UpgradeChannelNone,
},
},
},
ManagedMachinePools: []ManagedMachinePool{
{
MachinePool: getMachinePool("pool0"),
InfraMachinePool: getAzureMachinePool("pool0", infrav1.NodePoolModeSystem),
},
},
},
Expected: false,
},
{
Name: "Upgradechannel is set node-image",
Input: ManagedControlPlaneScopeParams{
AzureClients: AzureClients{
Authorizer: autorest.NullAuthorizer{},
},
Cluster: &clusterv1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster1",
Namespace: "default",
},
},
ControlPlane: &infrav1.AzureManagedControlPlane{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster1",
Namespace: "default",
},
Spec: infrav1.AzureManagedControlPlaneSpec{
AutoUpgradeProfile: &infrav1.ManagedClusterAutoUpgradeProfile{
UpgradeChannel: infrav1.UpgradeChannelNodeImage,
},
},
},
ManagedMachinePools: []ManagedMachinePool{
{
MachinePool: getMachinePool("pool0"),
InfraMachinePool: getAzureMachinePool("pool0", infrav1.NodePoolModeSystem),
},
},
},
Expected: false,
},
}
for _, c := range cases {
c := c
t.Run(c.Name, func(t *testing.T) {
g := NewWithT(t)
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(c.Input.ControlPlane).Build()
c.Input.Client = fakeClient
s, err := NewManagedControlPlaneScope(context.TODO(), c.Input)
g.Expect(err).To(Succeed())
autoUpgrade := s.IsManagedVersionUpgrade()
g.Expect(autoUpgrade).To(Equal(c.Expected))
})
}
}
8 changes: 8 additions & 0 deletions azure/services/managedclusters/managedclusters.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package managedclusters

import (
"context"
"fmt"

"github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2021-05-01/containerservice"
"github.com/Azure/go-autorest/autorest/to"
Expand Down Expand Up @@ -51,6 +52,8 @@ type ManagedClusterScope interface {
SetUserKubeConfigData([]byte)
IsAadEnabled() bool
IsLocalAcountsDisabled() bool
SetAutoUpgradeVersionStatus(version string)
IsManagedVersionUpgrade() bool
}

// Service provides operations on azure resources.
Expand Down Expand Up @@ -108,6 +111,11 @@ func (s *Service) Reconcile(ctx context.Context) error {
return errors.Wrap(err, "error while reconciling adminKubeConfigData")
}

if s.Scope.IsManagedVersionUpgrade() && managedCluster.KubernetesVersion != nil {
kubernetesVersion := fmt.Sprintf("v%s", *managedCluster.KubernetesVersion)
s.Scope.SetAutoUpgradeVersionStatus(kubernetesVersion)
}

s.Scope.SetAdminKubeConfigData(adminKubeConfigData)
s.Scope.SetUserKubeConfigData(userKubeConfigData)
}
Expand Down
29 changes: 29 additions & 0 deletions azure/services/managedclusters/managedclusters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,34 @@ func TestReconcile(t *testing.T) {
m.GetCredentials(gomockinternal.AContext(), "my-rg", "my-managedcluster").Return([]byte("credentials"), nil)
s.SetAdminKubeConfigData([]byte("credentials"))
s.SetUserKubeConfigData(userKubeConfigData)
s.IsManagedVersionUpgrade().Return(false)
s.UpdatePutStatus(infrav1.ManagedClusterRunningCondition, serviceName, nil)
},
},
{
name: "create managed cluster succeeds, update autoupgrade status",
expectedError: "",
expect: func(m *mock_managedclusters.MockCredentialGetterMockRecorder, s *mock_managedclusters.MockManagedClusterScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) {
var userKubeConfigData []byte
s.ManagedClusterSpec(gomockinternal.AContext()).Return(fakeManagedClusterSpec)
r.CreateResource(gomockinternal.AContext(), fakeManagedClusterSpec, serviceName).Return(containerservice.ManagedCluster{
ManagedClusterProperties: &containerservice.ManagedClusterProperties{
Fqdn: pointer.String("my-managedcluster-fqdn"),
ProvisioningState: pointer.String("Succeeded"),
KubernetesVersion: pointer.String("1.27.3"),
},
}, nil)
s.SetControlPlaneEndpoint(clusterv1.APIEndpoint{
Host: "my-managedcluster-fqdn",
Port: 443,
})
s.IsAadEnabled().Return(false)
s.IsLocalAcountsDisabled().Return(false)
m.GetCredentials(gomockinternal.AContext(), "my-rg", "my-managedcluster").Return([]byte("credentials"), nil)
s.SetAdminKubeConfigData([]byte("credentials"))
s.SetUserKubeConfigData(userKubeConfigData)
s.IsManagedVersionUpgrade().Return(true)
s.SetAutoUpgradeVersionStatus("v1.27.3")
s.UpdatePutStatus(infrav1.ManagedClusterRunningCondition, serviceName, nil)
},
},
Expand Down Expand Up @@ -113,6 +141,7 @@ func TestReconcile(t *testing.T) {
m.GetUserCredentials(gomockinternal.AContext(), "my-rg", "my-managedcluster").Return([]byte("credentials-user"), nil)
s.SetAdminKubeConfigData([]byte("credentials"))
s.SetUserKubeConfigData([]byte("credentials-user"))
s.IsManagedVersionUpgrade().Return(false)
s.UpdatePutStatus(infrav1.ManagedClusterRunningCondition, serviceName, nil)
},
},
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5a53d64

Please sign in to comment.