diff --git a/pkg/cluster/arooperator.go b/pkg/cluster/arooperator.go index 3b85ee7c6cc..0f17500eb6b 100644 --- a/pkg/cluster/arooperator.go +++ b/pkg/cluster/arooperator.go @@ -53,3 +53,7 @@ func (m *manager) ensureAROOperatorRunningDesiredVersion(ctx context.Context) (b func (m *manager) renewMDSDCertificate(ctx context.Context) error { return m.aroOperatorDeployer.RenewMDSDCertificate(ctx) } + +func (m *manager) restartAROOperatorMaster(ctx context.Context) error { + return m.aroOperatorDeployer.Restart(ctx, []string{"aro-operator-master"}) +} diff --git a/pkg/cluster/install.go b/pkg/cluster/install.go index a20ba9a5593..bf6736be94a 100644 --- a/pkg/cluster/install.go +++ b/pkg/cluster/install.go @@ -201,6 +201,8 @@ func (m *manager) Update(ctx context.Context) error { steps.Action(m.renewMDSDCertificate), steps.Action(m.updateOpenShiftSecret), steps.Action(m.updateAROSecret), + steps.Action(m.restartAROOperatorMaster), // depends on m.updateOpenShiftSecret; the point of restarting is to pick up any changes made to the secret + steps.Condition(m.aroDeploymentReady, 20*time.Minute, true), steps.Action(m.reconcileLoadBalancerProfile), } diff --git a/pkg/operator/deploy/deploy.go b/pkg/operator/deploy/deploy.go index 082327e0fbe..f2e147bb96f 100644 --- a/pkg/operator/deploy/deploy.go +++ b/pkg/operator/deploy/deploy.go @@ -37,6 +37,7 @@ import ( aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned" "github.com/Azure/ARO-RP/pkg/operator/controllers/genevalogging" "github.com/Azure/ARO-RP/pkg/util/dynamichelper" + kubeutil "github.com/Azure/ARO-RP/pkg/util/kubernetes" utilpem "github.com/Azure/ARO-RP/pkg/util/pem" "github.com/Azure/ARO-RP/pkg/util/pullsecret" "github.com/Azure/ARO-RP/pkg/util/ready" @@ -49,6 +50,7 @@ var embeddedFiles embed.FS type Operator interface { CreateOrUpdate(context.Context) error IsReady(context.Context) (bool, error) + Restart(context.Context, []string) error IsRunningDesiredVersion(context.Context) (bool, error) RenewMDSDCertificate(context.Context) error } @@ -405,6 +407,18 @@ func (o *operator) IsReady(ctx context.Context) (bool, error) { return true, nil } +func (o *operator) Restart(ctx context.Context, deploymentNames []string) error { + for _, dn := range deploymentNames { + err := kubeutil.Restart(ctx, o.kubernetescli.AppsV1().Deployments(pkgoperator.Namespace), dn) + + if err != nil { + return err + } + } + + return nil +} + func checkOperatorDeploymentVersion(ctx context.Context, cli appsv1client.DeploymentInterface, name string, desiredVersion string) (bool, error) { d, err := cli.Get(ctx, name, metav1.GetOptions{}) switch { diff --git a/pkg/util/kubernetes/deployments.go b/pkg/util/kubernetes/deployments.go new file mode 100644 index 00000000000..33e7cb7f916 --- /dev/null +++ b/pkg/util/kubernetes/deployments.go @@ -0,0 +1,35 @@ +package kubernetes + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "context" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1" +) + +// Restart restarts the given Deployment by performing the equivalent of a `kubectl rollout restart deployment ` against it. +func Restart(ctx context.Context, cli appsv1client.DeploymentInterface, deploymentName string) error { + d, err := cli.Get(ctx, deploymentName, metav1.GetOptions{}) + + if err != nil { + return err + } + + if d.Spec.Template.ObjectMeta.Annotations == nil { + d.Spec.Template.ObjectMeta.Annotations = make(map[string]string) + } + + d.Spec.Template.ObjectMeta.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339) + + _, err = cli.Update(ctx, d, metav1.UpdateOptions{}) + + if err != nil { + return err + } + + return nil +} diff --git a/pkg/util/mocks/operator/deploy/deploy.go b/pkg/util/mocks/operator/deploy/deploy.go index 4b42b560909..df12bfbb3b8 100644 --- a/pkg/util/mocks/operator/deploy/deploy.go +++ b/pkg/util/mocks/operator/deploy/deploy.go @@ -91,3 +91,17 @@ func (mr *MockOperatorMockRecorder) RenewMDSDCertificate(arg0 interface{}) *gomo mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenewMDSDCertificate", reflect.TypeOf((*MockOperator)(nil).RenewMDSDCertificate), arg0) } + +// Restart mocks base method. +func (m *MockOperator) Restart(arg0 context.Context, arg1 []string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Restart", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Restart indicates an expected call of Restart. +func (mr *MockOperatorMockRecorder) Restart(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Restart", reflect.TypeOf((*MockOperator)(nil).Restart), arg0, arg1) +}