Skip to content

Commit

Permalink
support for auto upgrade channel
Browse files Browse the repository at this point in the history
  • Loading branch information
LochanRn committed Oct 26, 2023
1 parent 1b6df41 commit 99bd115
Show file tree
Hide file tree
Showing 17 changed files with 898 additions and 51 deletions.
68 changes: 67 additions & 1 deletion api/v1beta1/azuremanagedcontrolplane_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,50 @@ const (
PrivateDNSZoneModeNone string = "None"
)

// UpgradeChannel determines the type of upgrade channel for automatically upgrading the cluster.
type UpgradeChannel string

const (
// UpgradeChannelNodeImage automatically upgrades the node image to the latest version available.
// Consider using nodeOSUpgradeChannel instead as that allows you to configure node OS patching separate from Kubernetes version patching.
UpgradeChannelNodeImage UpgradeChannel = "node-image"
// UpgradeChannelNone disables auto-upgrades and keeps the cluster at its current version of Kubernetes.
UpgradeChannelNone UpgradeChannel = "none"
// UpgradeChannelPatch automatically upgrade the cluster to the latest supported patch version when it becomes available
// while keeping the minor version the same. For example, if a cluster is running version 1.17.7 and versions 1.17.9, 1.18.4,
// 1.18.6, and 1.19.1 are available, your cluster is upgraded to 1.17.9.
UpgradeChannelPatch UpgradeChannel = "patch"
// UpgradeChannelRapid automatically upgrade the cluster to the latest supported patch release on the latest supported minor
// version. In cases where the cluster is at a version of Kubernetes that is at an N-2 minor version where N is the latest
// supported minor version, the cluster first upgrades to the latest supported patch version on N-1 minor version. For example,
// if a cluster is running version 1.17.7 and versions 1.17.9, 1.18.4, 1.18.6, and 1.19.1 are available, your cluster first
// is upgraded to 1.18.6, then is upgraded to 1.19.1.
UpgradeChannelRapid UpgradeChannel = "rapid"
// UpgradeChannelStable automatically upgrade the cluster to the latest supported patch release on minor version N-1, where
// N is the latest supported minor version. For example, if a cluster is running version 1.17.7 and versions 1.17.9, 1.18.4,
// 1.18.6, and 1.19.1 are available, your cluster is upgraded to 1.18.6.
UpgradeChannelStable UpgradeChannel = "stable"
)

// NodeOSUpgradeChannel determines the manner in which the OS on your nodes is updated. The default is NodeImage.
type NodeOSUpgradeChannel string

const (
// NodeOSUpgradeChannelNodeImage channel instructs AKS to update the nodes with a newly patched VHD containing security fixes and bugfixes
// on a weekly cadence. With the VHD update machines will be rolling reimaged to that VHD following maintenance windows and
// surge settings. No extra VHD cost is incurred when choosing this option as AKS hosts the images.
NodeOSUpgradeChannelNodeImage NodeOSUpgradeChannel = "NodeImage"
// NodeOSUpgradeChannelNone channel instructs AKS to not perform update on your machines OS, either by OS or by rolling VHDs. This means
// you are responsible for your security updates.
NodeOSUpgradeChannelNone NodeOSUpgradeChannel = "None"
// NodeOSUpgradeChannelUnmanaged channel instructs AKS to apply OS updates automatically through the OS built-in patching infrastructure.
// Newly scaled in machines will be unpatched initially and will be patched at some point by the OS's infrastructure. Behavior
// of this option depends on the OS in question. Ubuntu and Mariner apply security patches through unattended upgrade roughly
// once a day around 06:00 UTC. Windows does not apply security patches automatically and so for them this option is equivalent
// to None till further notice.
NodeOSUpgradeChannelUnmanaged NodeOSUpgradeChannel = "Unmanaged"
)

// ManagedControlPlaneOutboundType enumerates the values for the managed control plane OutboundType.
type ManagedControlPlaneOutboundType string

Expand Down Expand Up @@ -77,7 +121,7 @@ const (
// AzureManagedControlPlaneSpec defines the desired state of AzureManagedControlPlane.
type AzureManagedControlPlaneSpec struct {
// Version defines the desired Kubernetes version.
// +kubebuilder:validation:MinLength:=2
// +kubebuilder:validation:MinLength=2
Version string `json:"version"`

// ResourceGroupName is the name of the Azure resource group for this AKS Cluster.
Expand Down Expand Up @@ -221,6 +265,23 @@ type AzureManagedControlPlaneSpec struct {
// DisableLocalAccounts disables getting static credentials for this cluster when set. Expected to only be used for AAD clusters.
// +optional
DisableLocalAccounts *bool `json:"disableLocalAccounts,omitempty"`

// AutoUpgradeProfile - Profile of auto upgrade configuration.
// +optional
AutoUpgradeProfile *ManagedClusterAutoUpgradeProfile `json:"autoUpgradeProfile,omitempty"`
}

// ManagedClusterAutoUpgradeProfile - Auto upgrade profile for a managed cluster.
type ManagedClusterAutoUpgradeProfile struct {
// NodeOSUpgradeChannel is a manner in which the OS on your nodes is updated. The default is NodeImage. Possible values include: NodeImage,Unmanaged,None
// +kubebuilder:validation:Enum=NodeImage;Unmanaged;None
// +optional
NodeOSUpgradeChannel *NodeOSUpgradeChannel `json:"nodeOSUpgradeChannel,omitempty"`

// UpgradeChannel upgrade channel for auto upgrade. Possible values include: 'node-image','none','patch','rapid','stable'
// +kubebuilder:validation:Enum=node-image;none;patch;rapid;stable
// +optional
UpgradeChannel *UpgradeChannel `json:"upgradeChannel,omitempty"`
}

// HTTPProxyConfig is the HTTP proxy configuration for the cluster.
Expand Down Expand Up @@ -364,6 +425,11 @@ type ManagedControlPlaneSubnet struct {

// AzureManagedControlPlaneStatus defines the observed state of AzureManagedControlPlane.
type AzureManagedControlPlaneStatus struct {
// AutoUpgradeVersion is the Kubernetes version populated after autoupgrade based on the upgrade channel.
// +kubebuilder:validation:MinLength=2
// +optional
AutoUpgradeVersion string `json:"autoUpgradeVersion,omitempty"`

// Ready is true when the provider resource is ready.
// +optional
Ready bool `json:"ready,omitempty"`
Expand Down
39 changes: 39 additions & 0 deletions api/v1beta1/azuremanagedcontrolplane_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ import (
"strings"
"time"

semverv4 "github.com/blang/semver"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
kerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/utils/ptr"
"sigs.k8s.io/cluster-api-provider-azure/feature"
"sigs.k8s.io/cluster-api-provider-azure/util/versions"
webhookutils "sigs.k8s.io/cluster-api-provider-azure/util/webhook"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
capifeature "sigs.k8s.io/cluster-api/feature"
Expand Down Expand Up @@ -256,6 +258,10 @@ func (mw *azureManagedControlPlaneWebhook) ValidateUpdate(ctx context.Context, o
allErrs = append(allErrs, errs...)
}

if errs := m.validateAutoUpgradeProfile(old); len(errs) > 0 {
allErrs = append(allErrs, errs...)
}

if errs := m.validateOIDCIssuerProfileUpdate(old); len(errs) > 0 {
allErrs = append(allErrs, errs...)
}
Expand Down Expand Up @@ -332,6 +338,10 @@ func (m *AzureManagedControlPlane) validateVersion(_ client.Client) error {
return errors.New("must be a valid semantic version")
}

if _, err := semverv4.ParseTolerant(m.Spec.Version); err != nil {
return errors.Join(err, errors.New("must be a valid semantic version"))
}

Check warning on line 343 in api/v1beta1/azuremanagedcontrolplane_webhook.go

View check run for this annotation

Codecov / codecov/patch

api/v1beta1/azuremanagedcontrolplane_webhook.go#L342-L343

Added lines #L342 - L343 were not covered by tests

return nil
}

Expand Down Expand Up @@ -492,6 +502,35 @@ func (m *AzureManagedControlPlane) validateManagedClusterNetwork(cli client.Clie
return nil
}

// validateAutoUpgradeProfile validates auto upgrade profile.
func (m *AzureManagedControlPlane) validateAutoUpgradeProfile(old *AzureManagedControlPlane) field.ErrorList {
var allErrs field.ErrorList
if old.Spec.AutoUpgradeProfile != nil && m.Spec.AutoUpgradeProfile == nil {
// Prevent AutoUpgradeProfile to be set to nil.
// Unsetting the field is not allowed.
allErrs = append(allErrs,
field.Invalid(
field.NewPath("Spec", "AutoUpgradeProfile"),
m.Spec.AutoUpgradeProfile,
"field cannot be set to nil, to disable auto upgrades set the channel to none."))
}

Check warning on line 516 in api/v1beta1/azuremanagedcontrolplane_webhook.go

View check run for this annotation

Codecov / codecov/patch

api/v1beta1/azuremanagedcontrolplane_webhook.go#L509-L516

Added lines #L509 - L516 were not covered by tests

if hv := versions.GetHigherK8sVersion(m.Spec.Version, old.Spec.Version); hv != m.Spec.Version {
allErrs = append(allErrs, field.Invalid(field.NewPath("Spec", "Version"),
m.Spec.Version, "field version cannot be downgraded"),
)
}

if old.Status.AutoUpgradeVersion != "" && m.Spec.Version != old.Spec.Version {
if hv := versions.GetHigherK8sVersion(m.Spec.Version, old.Status.AutoUpgradeVersion); hv != m.Spec.Version {
allErrs = append(allErrs, field.Invalid(field.NewPath("Spec", "Version"),
m.Spec.Version, "version is auto-upgraded to "+old.Status.AutoUpgradeVersion+",cannot be downgraded"),
)
}
}
return allErrs
}

// validateAPIServerAccessProfileUpdate validates update to APIServerAccessProfile.
func (m *AzureManagedControlPlane) validateAPIServerAccessProfileUpdate(old *AzureManagedControlPlane) field.ErrorList {
var allErrs field.ErrorList
Expand Down
111 changes: 111 additions & 0 deletions api/v1beta1/azuremanagedcontrolplane_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,117 @@ func TestAzureManagedControlPlane_ValidateUpdate(t *testing.T) {
amcp: createAzureManagedControlPlane("192.168.0.10", "1.999.9", generateSSHPublicKey(true)),
wantErr: true,
},
{
name: "AzureManagedControlPlane invalid version downgrade change",
oldAMCP: &AzureManagedControlPlane{
Spec: AzureManagedControlPlaneSpec{
DNSServiceIP: ptr.To("192.168.0.0"),
Version: "v1.18.0",
},
},
amcp: &AzureManagedControlPlane{
Spec: AzureManagedControlPlaneSpec{
DNSServiceIP: ptr.To("192.168.0.0"),
Version: "v1.17.0",
},
},
wantErr: true,
},
{
name: "AzureManagedControlPlane invalid version downgrade change",
oldAMCP: &AzureManagedControlPlane{
Spec: AzureManagedControlPlaneSpec{
DNSServiceIP: ptr.To("192.168.0.0"),
Version: "v1.18.0",
},
Status: AzureManagedControlPlaneStatus{
AutoUpgradeVersion: "v1.18.3",
},
},
amcp: &AzureManagedControlPlane{
Spec: AzureManagedControlPlaneSpec{
DNSServiceIP: ptr.To("192.168.0.0"),
Version: "v1.18.1",
},
},
wantErr: true,
},
{
name: "AzureManagedControlPlane invalid version downgrade change",
oldAMCP: &AzureManagedControlPlane{
Spec: AzureManagedControlPlaneSpec{
DNSServiceIP: ptr.To("192.168.0.0"),
Version: "v1.18.0",
},
Status: AzureManagedControlPlaneStatus{
AutoUpgradeVersion: "1.19.3",
},
},
amcp: &AzureManagedControlPlane{
Spec: AzureManagedControlPlaneSpec{
DNSServiceIP: ptr.To("192.168.0.0"),
Version: "v1.18.6",
},
},
wantErr: true,
},
{
name: "AzureManagedControlPlane no version change",
oldAMCP: &AzureManagedControlPlane{
Spec: AzureManagedControlPlaneSpec{
DNSServiceIP: ptr.To("192.168.0.0"),
Version: "v1.18.0",
},
Status: AzureManagedControlPlaneStatus{
AutoUpgradeVersion: "1.19.3",
},
},
amcp: &AzureManagedControlPlane{
Spec: AzureManagedControlPlaneSpec{
DNSServiceIP: ptr.To("192.168.0.0"),
Version: "v1.18.0",
},
},
wantErr: false,
},
{
name: "AzureManagedControlPlane valid version upgrade change",
oldAMCP: &AzureManagedControlPlane{
Spec: AzureManagedControlPlaneSpec{
DNSServiceIP: ptr.To("192.168.0.0"),
Version: "v1.18.0",
},
Status: AzureManagedControlPlaneStatus{
AutoUpgradeVersion: "1.19.3",
},
},
amcp: &AzureManagedControlPlane{
Spec: AzureManagedControlPlaneSpec{
DNSServiceIP: ptr.To("192.168.0.0"),
Version: "v1.19.5",
},
},
wantErr: false,
},
{
name: "AzureManagedControlPlane valid version change",
oldAMCP: &AzureManagedControlPlane{
Spec: AzureManagedControlPlaneSpec{
DNSServiceIP: ptr.To("192.168.0.0"),
Version: "v1.18.0",
},
Status: AzureManagedControlPlaneStatus{
AutoUpgradeVersion: "1.19.3",
},
},
amcp: &AzureManagedControlPlane{
Spec: AzureManagedControlPlaneSpec{
DNSServiceIP: ptr.To("192.168.0.0"),
Version: "v1.19.3",
},
},
wantErr: false,
},
{
name: "AzureManagedControlPlane SubscriptionID is immutable",
oldAMCP: &AzureManagedControlPlane{
Expand Down
30 changes: 30 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

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

27 changes: 27 additions & 0 deletions azure/scope/managedcontrolplane.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,23 @@ func (s *ManagedControlPlaneScope) IsAADEnabled() bool {
return false
}

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

Check warning on line 483 in azure/scope/managedcontrolplane.go

View check run for this annotation

Codecov / codecov/patch

azure/scope/managedcontrolplane.go#L482-L483

Added lines #L482 - L483 were not covered by tests
}

// IsManagedVersionUpgrade checks if version is auto managed by AKS.
func (s *ManagedControlPlaneScope) IsManagedVersionUpgrade() bool {
return isManagedVersionUpgrade(s.ControlPlane)

Check warning on line 488 in azure/scope/managedcontrolplane.go

View check run for this annotation

Codecov / codecov/patch

azure/scope/managedcontrolplane.go#L487-L488

Added lines #L487 - L488 were not covered by tests
}

func isManagedVersionUpgrade(managedControlPlane *infrav1.AzureManagedControlPlane) bool {
return managedControlPlane.Spec.AutoUpgradeProfile != nil &&
managedControlPlane.Spec.AutoUpgradeProfile.UpgradeChannel != nil &&
(*managedControlPlane.Spec.AutoUpgradeProfile.UpgradeChannel != infrav1.UpgradeChannelNone &&
*managedControlPlane.Spec.AutoUpgradeProfile.UpgradeChannel != infrav1.UpgradeChannelNodeImage)

Check warning on line 495 in azure/scope/managedcontrolplane.go

View check run for this annotation

Codecov / codecov/patch

azure/scope/managedcontrolplane.go#L491-L495

Added lines #L491 - L495 were not covered by tests
}

// ManagedClusterSpec returns the managed cluster spec.
func (s *ManagedControlPlaneScope) ManagedClusterSpec() azure.ResourceSpecGetter {
managedClusterSpec := managedclusters.ManagedClusterSpec{
Expand Down Expand Up @@ -609,6 +626,16 @@ func (s *ManagedControlPlaneScope) ManagedClusterSpec() azure.ResourceSpecGetter
}
}

if s.ControlPlane.Spec.AutoUpgradeProfile != nil {
managedClusterSpec.AutoUpgradeProfile = &managedclusters.ManagedClusterAutoUpgradeProfile{}
if s.ControlPlane.Spec.AutoUpgradeProfile.UpgradeChannel != nil {
managedClusterSpec.AutoUpgradeProfile.UpgradeChannel = s.ControlPlane.Spec.AutoUpgradeProfile.UpgradeChannel
}
if s.ControlPlane.Spec.AutoUpgradeProfile.NodeOSUpgradeChannel != nil {
managedClusterSpec.AutoUpgradeProfile.NodeOSUpgradeChannel = s.ControlPlane.Spec.AutoUpgradeProfile.NodeOSUpgradeChannel
}

Check warning on line 636 in azure/scope/managedcontrolplane.go

View check run for this annotation

Codecov / codecov/patch

azure/scope/managedcontrolplane.go#L635-L636

Added lines #L635 - L636 were not covered by tests
}

return &managedClusterSpec
}

Expand Down
Loading

0 comments on commit 99bd115

Please sign in to comment.