Skip to content
This repository was archived by the owner on Oct 10, 2023. It is now read-only.

Commit 142aa6f

Browse files
author
Adolfo Duarte
authored
Graceful upgrade of addons-manager (#3229)
Before deploying the addons-manager package checks to see if addons-manager is installed and is moving repositories. If so then: - Pauses lifecycle management of addons-manager package - NoopDelete addons-manager packageinstall - Deletes adoons-manager addon secret
1 parent 6ab1811 commit 142aa6f

File tree

2 files changed

+466
-0
lines changed

2 files changed

+466
-0
lines changed

tkg/managementcomponents/management_component_install.go

+202
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,21 @@ package managementcomponents
55

66
import (
77
"context"
8+
"encoding/json"
9+
"fmt"
810
"os"
911
"time"
1012

1113
"github.com/pkg/errors"
1214
"golang.org/x/sync/errgroup"
15+
corev1 "k8s.io/api/core/v1"
16+
apierrors "k8s.io/apimachinery/pkg/api/errors"
1317
"k8s.io/apimachinery/pkg/labels"
1418
"k8s.io/apimachinery/pkg/selection"
19+
"k8s.io/apimachinery/pkg/types"
20+
"k8s.io/client-go/util/retry"
1521
crtclient "sigs.k8s.io/controller-runtime/pkg/client"
22+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1623

1724
kappipkg "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/packaging/v1alpha1"
1825
"github.com/vmware-tanzu/tanzu-framework/packageclients/pkg/packageclient"
@@ -22,6 +29,11 @@ import (
2229
"github.com/vmware-tanzu/tanzu-framework/tkg/log"
2330
)
2431

32+
const (
33+
addonsManagerName = "addons-manager"
34+
addonFinalizer = "tkg.tanzu.vmware.com/addon"
35+
)
36+
2537
// ClusterOptions specifies cluster configuration
2638
type ClusterOptions struct {
2739
Kubeconfig string
@@ -48,12 +60,195 @@ type ManagementComponentsInstallOptions struct {
4860
ManagementPackageRepositoryOptions ManagementPackageRepositoryOptions
4961
}
5062

63+
func generateAddonSecretName(clusterName, addonName string) string {
64+
return fmt.Sprintf("%s-tanzu-%s-addon", clusterName, addonName)
65+
}
66+
67+
func pauseAddonSecretReconciliation(clusterClient clusterclient.Client, addonSecreteName, namespace string) error {
68+
log.Infof("Pausing reconciliation for %s/%s secret", namespace, addonSecreteName)
69+
secret := &corev1.Secret{}
70+
jsonPatch := []map[string]interface{}{
71+
{
72+
"op": "add",
73+
"path": fmt.Sprintf("/metadata/annotations/tkg.tanzu.vmware.com~1addon-paused"),
74+
"value": "",
75+
},
76+
}
77+
payloadBytes, err := json.Marshal(jsonPatch)
78+
if err != nil {
79+
return errors.Wrap(err, "unable to generate json patch")
80+
}
81+
82+
err = clusterClient.PatchResource(secret, addonSecreteName, namespace, string(payloadBytes), types.JSONPatchType, nil)
83+
if apierrors.IsNotFound(err) {
84+
return nil
85+
}
86+
if err != nil {
87+
return errors.Wrapf(err, "failed to pause %s secret reconciliation", addonSecreteName)
88+
}
89+
return nil
90+
}
91+
92+
func pausePackageInstallReconciliation(clusterClient clusterclient.Client, pkgiName, namespace string) error {
93+
log.Infof("Pausing reconciliation for %s/%s packageinstall", namespace, pkgiName)
94+
pkgi := &kappipkg.PackageInstall{}
95+
jsonPatch := []map[string]interface{}{
96+
{
97+
"op": "add",
98+
"path": "/spec/paused",
99+
"value": true,
100+
},
101+
}
102+
payloadBytes, err := json.Marshal(jsonPatch)
103+
if err != nil {
104+
return errors.Wrap(err, "unable to generate json patch")
105+
}
106+
err = clusterClient.PatchResource(pkgi, pkgiName, namespace, string(payloadBytes), types.JSONPatchType, nil)
107+
if apierrors.IsNotFound(err) {
108+
return nil
109+
}
110+
if err != nil {
111+
return errors.Wrapf(err, "failed to pause %s packageinstall reconciliation", pkgiName)
112+
}
113+
return nil
114+
}
115+
116+
// PausedAddonLifecycleManagement pauses/unpauses the lifecycle management of addon package with given name and namespace
117+
func PauseAddonLifecycleManagement(clusterClient clusterclient.Client, clusterName, addonName, namespace string) error {
118+
log.Infof("Pausing lifecycle management for %s", addonName)
119+
pkgiName := fmt.Sprintf("tanzu-%s", addonsManagerName)
120+
addonSecretName := generateAddonSecretName(clusterName, addonsManagerName)
121+
122+
err := pauseAddonSecretReconciliation(clusterClient, addonSecretName, namespace)
123+
if err != nil {
124+
return err
125+
}
126+
127+
err = pausePackageInstallReconciliation(clusterClient, pkgiName, namespace)
128+
if err != nil {
129+
return err
130+
}
131+
132+
return nil
133+
}
134+
135+
// NoopDeletePackageInstall sets spec.noopdelete = true before deleting the package install
136+
func NoopDeletePackageInstall(clusterClient clusterclient.Client, addonName, namespace string) error {
137+
log.Infof("Deleting %s/%s packageinstall with noopdelete", namespace, addonName)
138+
pkgiName := fmt.Sprintf("tanzu-%s", addonName)
139+
140+
jsonPatch := []map[string]interface{}{
141+
{
142+
"op": "add",
143+
"path": "/spec/noopDelete",
144+
"value": true,
145+
},
146+
}
147+
payloadBytes, err := json.Marshal(jsonPatch)
148+
if err != nil {
149+
return errors.Wrap(err, "unable to generate json patch")
150+
}
151+
pkgi := &kappipkg.PackageInstall{}
152+
err = clusterClient.PatchResource(pkgi, pkgiName, namespace, string(payloadBytes), types.JSONPatchType, nil)
153+
if apierrors.IsNotFound(err) {
154+
return nil
155+
}
156+
if err != nil {
157+
return errors.Wrapf(err, "failed to patch %s packageinstall", pkgiName)
158+
}
159+
160+
pkgi.Name = pkgiName
161+
pkgi.Namespace = namespace
162+
err = clusterClient.DeleteResource(pkgi)
163+
if err != nil && !apierrors.IsNotFound(err) {
164+
return errors.Wrapf(err, "failed to delete PackageInstall resource %s", pkgiName)
165+
}
166+
return nil
167+
}
168+
169+
// DeleteAddonSecret deletes the secrete associated with the addon if present. Return no error if secret not found.
170+
func DeleteAddonSecret(clusterClient clusterclient.Client, clusterName, addonName, namespace string) error {
171+
addonSecret := &corev1.Secret{}
172+
addonSecret.Name = generateAddonSecretName(clusterName, addonName)
173+
addonSecret.Namespace = constants.TkgNamespace
174+
log.Infof("Deleting %s/%s secret", addonSecret.Namespace, addonSecret.Name)
175+
err := clusterClient.GetResource(addonSecret, addonSecret.Name, addonSecret.Namespace, nil, nil)
176+
if apierrors.IsNotFound(err) {
177+
return nil
178+
}
179+
if err != nil {
180+
return err
181+
}
182+
183+
if controllerutil.ContainsFinalizer(addonSecret, addonFinalizer) {
184+
controllerutil.RemoveFinalizer(addonSecret, addonFinalizer)
185+
}
186+
187+
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
188+
return clusterClient.UpdateResource(addonSecret, addonSecret.Name, addonSecret.Namespace)
189+
})
190+
191+
if apierrors.IsNotFound(err) {
192+
return nil
193+
}
194+
if err != nil {
195+
return err
196+
}
197+
198+
err = clusterClient.DeleteResource(addonSecret)
199+
if apierrors.IsNotFound(err) {
200+
return nil
201+
}
202+
if err != nil {
203+
return errors.Wrapf(err, "failed to delete addon addonSecret %s", addonSecret)
204+
}
205+
return nil
206+
}
207+
208+
// AddonSecretExists returns true if given addon is present and was installed from core repository.
209+
func AddonSecretExists(clusterClient clusterclient.Client, clusterName, addonName, namespace string) (bool, error) {
210+
addonSecret := &corev1.Secret{}
211+
addonSecret.Name = generateAddonSecretName(clusterName, addonName)
212+
addonSecret.Namespace = constants.TkgNamespace
213+
214+
err := clusterClient.GetResource(addonSecret, addonSecret.Name, addonSecret.Namespace, nil, nil)
215+
if apierrors.IsNotFound(err) {
216+
return false, nil
217+
}
218+
if err != nil {
219+
return false, err
220+
}
221+
return true, nil
222+
}
223+
51224
// InstallManagementComponents installs the management component to cluster
52225
func InstallManagementComponents(mcip *ManagementComponentsInstallOptions) error {
53226
clusterClient, err := clusterclient.NewClient(mcip.ClusterOptions.Kubeconfig, mcip.ClusterOptions.Kubecontext, clusterclient.Options{})
54227
if err != nil {
55228
return errors.Wrap(err, "unable to get cluster client")
56229
}
230+
clusterName, err := clusterClient.GetCurrentClusterName(mcip.ClusterOptions.Kubecontext)
231+
if err != nil {
232+
return errors.Wrap(err, "unable to get cluster name")
233+
}
234+
235+
// If the addons-manager is moving from core repository to management repository, its lifecycle management
236+
// needs to be paused.
237+
previousAddonsManagerIsFromCoreRepo, err := AddonSecretExists(clusterClient, clusterName, addonsManagerName, constants.TkgNamespace)
238+
if err != nil {
239+
return err
240+
}
241+
if previousAddonsManagerIsFromCoreRepo {
242+
err = PauseAddonLifecycleManagement(clusterClient, clusterName, addonsManagerName, constants.TkgNamespace)
243+
if err != nil {
244+
return err
245+
}
246+
247+
err = NoopDeletePackageInstall(clusterClient, addonsManagerName, constants.TkgNamespace)
248+
if err != nil {
249+
return err
250+
}
251+
}
57252

58253
// create package client
59254
pkgClient, err := packageclient.NewPackageClientForContext(mcip.ClusterOptions.Kubeconfig, mcip.ClusterOptions.Kubecontext)
@@ -83,6 +278,13 @@ func InstallManagementComponents(mcip *ManagementComponentsInstallOptions) error
83278
}
84279
}
85280

281+
if previousAddonsManagerIsFromCoreRepo {
282+
err = DeleteAddonSecret(clusterClient, clusterName, addonsManagerName, constants.TkgNamespace)
283+
if err != nil {
284+
return err
285+
}
286+
}
287+
86288
return nil
87289
}
88290

0 commit comments

Comments
 (0)