Skip to content

Commit

Permalink
MachineDeployment rolloutAfter support
Browse files Browse the repository at this point in the history
  • Loading branch information
Yuvaraj Kakaraparthi committed Mar 8, 2023
1 parent 838dcbb commit 7bcac4c
Show file tree
Hide file tree
Showing 17 changed files with 200 additions and 67 deletions.
5 changes: 5 additions & 0 deletions api/v1alpha3/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ func (src *MachineDeployment) ConvertTo(dstRaw conversion.Hub) error {

dst.Spec.Template.Spec.NodeDeletionTimeout = restored.Spec.Template.Spec.NodeDeletionTimeout
dst.Spec.Template.Spec.NodeVolumeDetachTimeout = restored.Spec.Template.Spec.NodeVolumeDetachTimeout
dst.Spec.RolloutAfter = restored.Spec.RolloutAfter
dst.Status.Conditions = restored.Status.Conditions
return nil
}
Expand Down Expand Up @@ -316,6 +317,10 @@ func Convert_v1beta1_MachineSpec_To_v1alpha3_MachineSpec(in *clusterv1.MachineSp
return autoConvert_v1beta1_MachineSpec_To_v1alpha3_MachineSpec(in, out, s)
}

func Convert_v1beta1_MachineDeploymentSpec_To_v1alpha3_MachineDeploymentSpec(in *clusterv1.MachineDeploymentSpec, out *MachineDeploymentSpec, s apiconversion.Scope) error {
return autoConvert_v1beta1_MachineDeploymentSpec_To_v1alpha3_MachineDeploymentSpec(in, out, s)
}

func Convert_v1beta1_MachineDeploymentStatus_To_v1alpha3_MachineDeploymentStatus(in *clusterv1.MachineDeploymentStatus, out *MachineDeploymentStatus, s apiconversion.Scope) error {
// Status.Conditions was introduced in v1alpha4, thus requiring a custom conversion function; the values is going to be preserved in an annotation thus allowing roundtrip without loosing informations
return autoConvert_v1beta1_MachineDeploymentStatus_To_v1alpha3_MachineDeploymentStatus(in, out, s)
Expand Down
16 changes: 6 additions & 10 deletions api/v1alpha3/zz_generated.conversion.go

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

5 changes: 5 additions & 0 deletions api/v1alpha4/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ func (src *MachineDeployment) ConvertTo(dstRaw conversion.Hub) error {

dst.Spec.Template.Spec.NodeDeletionTimeout = restored.Spec.Template.Spec.NodeDeletionTimeout
dst.Spec.Template.Spec.NodeVolumeDetachTimeout = restored.Spec.Template.Spec.NodeVolumeDetachTimeout
dst.Spec.RolloutAfter = restored.Spec.RolloutAfter
return nil
}

Expand Down Expand Up @@ -335,6 +336,10 @@ func Convert_v1beta1_MachineSpec_To_v1alpha4_MachineSpec(in *clusterv1.MachineSp
return autoConvert_v1beta1_MachineSpec_To_v1alpha4_MachineSpec(in, out, s)
}

func Convert_v1beta1_MachineDeploymentSpec_To_v1alpha4_MachineDeploymentSpec(in *clusterv1.MachineDeploymentSpec, out *MachineDeploymentSpec, s apiconversion.Scope) error {
return autoConvert_v1beta1_MachineDeploymentSpec_To_v1alpha4_MachineDeploymentSpec(in, out, s)
}

func Convert_v1beta1_Topology_To_v1alpha4_Topology(in *clusterv1.Topology, out *Topology, s apiconversion.Scope) error {
// spec.topology.variables has been added with v1beta1.
return autoConvert_v1beta1_Topology_To_v1alpha4_Topology(in, out, s)
Expand Down
16 changes: 6 additions & 10 deletions api/v1alpha4/zz_generated.conversion.go

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

8 changes: 8 additions & 0 deletions api/v1beta1/machinedeployment_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ const (
// As a result, we use the hash of the machine template while ignoring all in-place mutable fields, i.e. the
// machine template with only fields that could trigger a rollout for the machine-template-hash, making it
// independent of the changes to any in-place mutable fields.
// A random string is appended at the end of the label value (label value format is "<hash>-<random string>"))
// to distinguish duplicate MachineSets that have the exact same spec but were created as a result of rolloutAfter.
MachineDeploymentUniqueLabel = "machine-template-hash"
)

Expand Down Expand Up @@ -97,6 +99,12 @@ type MachineDeploymentSpec struct {
// +optional
Replicas *int32 `json:"replicas,omitempty"`

// RolloutAfter is a field to indicate a rollout should be performed
// after the specified time even if no changes have been made to the
// MachineDeployment.
// +optional
RolloutAfter *metav1.Time `json:"rolloutAfter,omitempty"`

// Label selector for machines. Existing MachineSets whose machines are
// selected by this will be the ones affected by this deployment.
// It must match the machine template's labels.
Expand Down
4 changes: 4 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.

8 changes: 7 additions & 1 deletion api/v1beta1/zz_generated.openapi.go

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

4 changes: 2 additions & 2 deletions cmd/clusterctl/client/alpha/kubeadmcontrolplane.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ func getKubeadmControlPlane(proxy cluster.Proxy, name, namespace string) (*contr
return kcpObj, nil
}

// setRolloutAfter sets KubeadmControlPlane.spec.rolloutAfter.
func setRolloutAfter(proxy cluster.Proxy, name, namespace string) error {
// setRolloutAfterOnKCP sets KubeadmControlPlane.spec.rolloutAfter.
func setRolloutAfterOnKCP(proxy cluster.Proxy, name, namespace string) error {
patch := client.RawPatch(types.MergePatchType, []byte(fmt.Sprintf(`{"spec":{"rolloutAfter":"%v"}}`, time.Now().Format(time.RFC3339))))
return patchKubeadmControlPlane(proxy, name, namespace, patch)
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/clusterctl/client/alpha/machinedeployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ func getMachineDeployment(proxy cluster.Proxy, name, namespace string) (*cluster
return mdObj, nil
}

// setRestartedAtAnnotation sets the restartedAt annotation in the MachineDeployment's spec.template.objectmeta.
func setRestartedAtAnnotation(proxy cluster.Proxy, name, namespace string) error {
patch := client.RawPatch(types.MergePatchType, []byte(fmt.Sprintf(`{"spec":{"template":{"metadata":{"annotations":{"cluster.x-k8s.io/restartedAt":"%v"}}}}}`, time.Now().Format(time.RFC3339))))
// setRolloutAfterOnMachineDeployment sets MachineDeployment.spec.rolloutAfter.
func setRolloutAfterOnMachineDeployment(proxy cluster.Proxy, name, namespace string) error {
patch := client.RawPatch(types.MergePatchType, []byte(fmt.Sprintf(`{"spec":{"rolloutAfter":"%v"}}`, time.Now().Format(time.RFC3339))))
return patchMachineDeployment(proxy, name, namespace, patch)
}

Expand Down
7 changes: 5 additions & 2 deletions cmd/clusterctl/client/alpha/rollout_restarter.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ func (r *rollout) ObjectRestarter(proxy cluster.Proxy, ref corev1.ObjectReferenc
if deployment.Spec.Paused {
return errors.Errorf("can't restart paused MachineDeployment (run rollout resume first): %v/%v", ref.Kind, ref.Name)
}
if err := setRestartedAtAnnotation(proxy, ref.Name, ref.Namespace); err != nil {
if deployment.Spec.RolloutAfter != nil && deployment.Spec.RolloutAfter.After(time.Now()) {
return errors.Errorf("can't update MachineDeployment (remove 'spec.rolloutAfter' first): %v/%v", ref.Kind, ref.Name)
}
if err := setRolloutAfterOnMachineDeployment(proxy, ref.Name, ref.Namespace); err != nil {
return err
}
case KubeadmControlPlane:
Expand All @@ -51,7 +54,7 @@ func (r *rollout) ObjectRestarter(proxy cluster.Proxy, ref corev1.ObjectReferenc
if kcp.Spec.RolloutAfter != nil && kcp.Spec.RolloutAfter.After(time.Now()) {
return errors.Errorf("can't update KubeadmControlPlane (remove 'spec.rolloutAfter' first): %v/%v", ref.Kind, ref.Name)
}
if err := setRolloutAfter(proxy, ref.Name, ref.Namespace); err != nil {
if err := setRolloutAfterOnKCP(proxy, ref.Name, ref.Namespace); err != nil {
return err
}
default:
Expand Down
37 changes: 32 additions & 5 deletions cmd/clusterctl/client/alpha/rollout_restarter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func Test_ObjectRestarter(t *testing.T) {
wantRollout bool
}{
{
name: "machinedeployment should have restart annotation",
name: "machinedeployment should have rolloutAfter",
fields: fields{
objs: []client.Object{
&clusterv1.MachineDeployment{
Expand All @@ -67,7 +67,7 @@ func Test_ObjectRestarter(t *testing.T) {
wantRollout: true,
},
{
name: "paused machinedeployment should not have restart annotation",
name: "paused machinedeployment should not have rolloutAfter",
fields: fields{
objs: []client.Object{
&clusterv1.MachineDeployment{
Expand All @@ -93,6 +93,33 @@ func Test_ObjectRestarter(t *testing.T) {
wantErr: true,
wantRollout: false,
},
{
name: "machinedeployment with spec.rolloutAfter should not be updatable",
fields: fields{
objs: []client.Object{
&clusterv1.MachineDeployment{
TypeMeta: metav1.TypeMeta{
Kind: "MachineDeployment",
APIVersion: "cluster.x-k8s.io/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "md-1",
},
Spec: clusterv1.MachineDeploymentSpec{
RolloutAfter: &metav1.Time{Time: time.Now().Local().Add(time.Hour)},
},
},
},
ref: corev1.ObjectReference{
Kind: MachineDeployment,
Name: "md-1",
Namespace: "default",
},
},
wantErr: true,
wantRollout: false,
},
{
name: "kubeadmcontrolplane should have rolloutAfter",
fields: fields{
Expand Down Expand Up @@ -193,9 +220,9 @@ func Test_ObjectRestarter(t *testing.T) {
err = cl.Get(context.TODO(), key, md)
g.Expect(err).ToNot(HaveOccurred())
if tt.wantRollout {
g.Expect(md.Spec.Template.Annotations).To(HaveKey("cluster.x-k8s.io/restartedAt"))
g.Expect(md.Spec.RolloutAfter).NotTo(BeNil())
} else {
g.Expect(md.Spec.Template.Annotations).ToNot(HaveKey("cluster.x-k8s.io/restartedAt"))
g.Expect(md.Spec.RolloutAfter).To(BeNil())
}
case *controlplanev1.KubeadmControlPlane:
kcp := &controlplanev1.KubeadmControlPlane{}
Expand All @@ -204,7 +231,7 @@ func Test_ObjectRestarter(t *testing.T) {
if tt.wantRollout {
g.Expect(kcp.Spec.RolloutAfter).NotTo(BeNil())
} else {
g.Expect(kcp.Spec.RolloutAfter).To(nil)
g.Expect(kcp.Spec.RolloutAfter).To(BeNil())
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions config/crd/bases/cluster.x-k8s.io_machinedeployments.yaml

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

20 changes: 7 additions & 13 deletions docs/book/src/tasks/upgrading-clusters.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ and then both the `Version` and `InfrastructureTemplate` should be modified in a

#### How to schedule a machine rollout

A `KubeadmControlPlane` resource has a field `RolloutAfter` that can be set to a timestamp
(RFC-3339) after which a rollout should be triggered regardless of whether there were any changes
to the `KubeadmControlPlane.Spec` or not. This would roll out replacement control plane nodes
which can be useful e.g. to perform certificate rotation, reflect changes to machine templates,
move to new machines, etc.
The `KubeadmControlPlane` and `MachineDepoyment` resources have a field `RolloutAfter` that can be
set to a timestamp (RFC-3339) after which a rollout should be triggered regardless of whether there
were any changes to `KubeadmControlPlane.Spec`/`MachineDeployment.Spec.Template` or not. This would
roll out replacement nodes which can be useful e.g. to perform certificate rotation, reflect changes
to machine templates, move to new machines, etc.

Note that this field can only be used for triggering a rollout, not for delaying one. Specifically,
a rollout can also happen before the time specified in `RolloutAfter` if any changes are made to
Expand All @@ -62,19 +62,13 @@ the spec before that time.
The rollout can be triggered by running the following command:

```shell
# Trigger a KubeadmControlPlane rollout.
clusterctl alpha rollout restart kubeadmcontrollplane/my-kcp
```

To do the same for machines managed by a `MachineDeployment` it's enough to make an arbitrary
change to its `Spec.Template`, one common approach is to run:

``` shell
# Trigger a MachineDeployment rollout.
clusterctl alpha rollout restart machinedeployment/my-md-0
```

This will modify the template by setting an `cluster.x-k8s.io/restartedAt` annotation which will
trigger a rollout.

### Upgrading machines managed by a `MachineDeployment`

Upgrades are not limited to just the control plane. This section is not related to Kubeadm control plane specifically,
Expand Down
Loading

0 comments on commit 7bcac4c

Please sign in to comment.