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

Commit e50ddfa

Browse files
author
Adolfo Duarte
committed
Graceful upgrade of addons-manager
- Pauses lifecycle management of addons-manager package - NoopDelete addons-manager packageinstall - Deletes adoons-manager addon secret
1 parent 0605dfc commit e50ddfa

File tree

2 files changed

+386
-2
lines changed

2 files changed

+386
-2
lines changed

pkg/v1/tkg/managementcomponents/management_component_install.go

+178-2
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,20 @@ 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"
1520
crtclient "sigs.k8s.io/controller-runtime/pkg/client"
21+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1622

1723
kappipkg "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/packaging/v1alpha1"
1824

@@ -23,6 +29,11 @@ import (
2329
"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/tkgpackagedatamodel"
2430
)
2531

32+
const (
33+
addonsManagerName = "addons-manager"
34+
addonFinalizer = "tkg.tanzu.vmware.com/addon"
35+
)
36+
2637
// ClusterOptions specifies cluster configuration
2738
type ClusterOptions struct {
2839
Kubeconfig string
@@ -49,18 +60,183 @@ type ManagementComponentsInstallOptions struct {
4960
ManagementPackageRepositoryOptions ManagementPackageRepositoryOptions
5061
}
5162

63+
func generateAddonsPkgiName(addonName string) string {
64+
return fmt.Sprintf("tanzu-%s", addonName)
65+
}
66+
67+
func generateAddonSecretName(clusterName, addonName string) string {
68+
return fmt.Sprintf("%s-%s-addon", clusterName, generateAddonsPkgiName(addonName))
69+
}
70+
71+
func pauseAddonSecretReconciliation(ctx context.Context, clusterClient clusterclient.Client, clusterName, addonSecreteName, namespace string) error {
72+
secret := &corev1.Secret{}
73+
jsonPatch := []map[string]interface{}{
74+
{
75+
"op": "add",
76+
"path": fmt.Sprintf("/metadata/annotations/tkg.tanzu.vmware.com~1addon-paused"),
77+
"value": "",
78+
},
79+
}
80+
payloadBytes, err := json.Marshal(jsonPatch)
81+
if err != nil {
82+
return errors.Wrap(err, "unable to generate json patch")
83+
}
84+
85+
err = clusterClient.PatchResource(secret, addonSecreteName, namespace, string(payloadBytes), types.JSONPatchType, nil)
86+
if apierrors.IsNotFound(err) {
87+
return nil
88+
}
89+
if err != nil {
90+
return errors.Wrapf(err, "failed to pause %s secret reconciliation", addonSecreteName)
91+
}
92+
return nil
93+
}
94+
95+
func pausePackageInstallReconciliation(ctx context.Context, clusterClient clusterclient.Client, pkgiName, namespace string) error {
96+
pkgi := &kappipkg.PackageInstall{}
97+
jsonPatch := []map[string]interface{}{
98+
{
99+
"op": "add",
100+
"path": "/spec/paused",
101+
"value": true,
102+
},
103+
}
104+
payloadBytes, err := json.Marshal(jsonPatch)
105+
if err != nil {
106+
return errors.Wrap(err, "unable to generate json patch")
107+
}
108+
err = clusterClient.PatchResource(pkgi, pkgiName, namespace, string(payloadBytes), types.JSONPatchType, nil)
109+
if apierrors.IsNotFound(err) {
110+
return nil
111+
}
112+
if err != nil {
113+
return errors.Wrapf(err, "failed to pause %s packageinstall reconciliation", pkgiName)
114+
}
115+
return nil
116+
}
117+
118+
// PausedAdoonLifecycleManagement pauses/unpauses the lifecycle management of addon package with given name and namespace
119+
func PauseAddonLifecycleManagement(clusterName, addonName, namespace string, clusterClient clusterclient.Client) error {
120+
pkgiName := generateAddonsPkgiName(addonsManagerName)
121+
addonSecretName := generateAddonSecretName(clusterName, addonsManagerName)
122+
123+
err := pauseAddonSecretReconciliation(context.TODO(), clusterClient, clusterName, addonSecretName, namespace)
124+
if err != nil {
125+
return err
126+
}
127+
128+
err = pausePackageInstallReconciliation(context.TODO(), clusterClient, pkgiName, namespace)
129+
if err != nil {
130+
return err
131+
}
132+
133+
return nil
134+
}
135+
136+
// NoopDeletePackageInstall sets spec.noopdelete = true before deleting the package install
137+
func NoopDeletePackageInstall(addonName, namespace string, clusterClient clusterclient.Client) error {
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 apierrors.IsNotFound(err) {
164+
return nil
165+
}
166+
if err != nil {
167+
return errors.Wrapf(err, "failed to delete PackageInstall resource %s", pkgiName)
168+
}
169+
if apierrors.IsNotFound(err) {
170+
return nil
171+
}
172+
return nil
173+
}
174+
175+
func DeleteAddonSecret(clusterName, addonName, namespace string, clusterClient clusterclient.Client) error {
176+
addonSecret := &corev1.Secret{}
177+
addonSecret.Name = generateAddonSecretName(clusterName, addonName)
178+
addonSecret.Namespace = constants.TkgNamespace
179+
err := clusterClient.GetResource(addonSecret, addonSecret.Name, addonSecret.Namespace, nil, nil)
180+
if apierrors.IsNotFound(err) {
181+
return nil
182+
}
183+
if err != nil {
184+
return err
185+
}
186+
187+
if controllerutil.ContainsFinalizer(addonSecret, addonFinalizer) {
188+
controllerutil.RemoveFinalizer(addonSecret, addonFinalizer)
189+
}
190+
err = clusterClient.UpdateResource(addonSecret, addonSecret.Name, addonSecret.Namespace)
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+
52208
// InstallManagementComponents installs the management component to cluster
53209
func InstallManagementComponents(mcip *ManagementComponentsInstallOptions) error {
54210
clusterClient, err := clusterclient.NewClient(mcip.ClusterOptions.Kubeconfig, mcip.ClusterOptions.Kubecontext, clusterclient.Options{})
55211
if err != nil {
56212
return errors.Wrap(err, "unable to get cluster client")
57213
}
214+
clusterName, err := clusterClient.GetCurrentClusterName(mcip.ClusterOptions.Kubecontext)
215+
if err != nil {
216+
return errors.Wrap(err, "unable to get cluster name")
217+
}
218+
if err != nil {
219+
return err
220+
}
58221

59-
// create package client
60-
pkgClient, err := tkgpackageclient.NewTKGPackageClientForContext(mcip.ClusterOptions.Kubeconfig, mcip.ClusterOptions.Kubecontext)
222+
err = PauseAddonLifecycleManagement(clusterName, addonsManagerName, constants.TkgNamespace, clusterClient)
223+
if err != nil {
224+
panic(err)
225+
}
226+
227+
err = NoopDeletePackageInstall(addonsManagerName, constants.TkgNamespace, clusterClient)
228+
if err != nil {
229+
return nil
230+
}
231+
232+
err = DeleteAddonSecret(clusterName, addonsManagerName, constants.TkgNamespace, clusterClient)
61233
if err != nil {
62234
return err
63235
}
236+
237+
// create package client
238+
pkgClient, err := tkgpackageclient.NewTKGPackageClientForContext(mcip.ClusterOptions.Kubeconfig, mcip.ClusterOptions.Kubecontext)
239+
64240
if err = InstallManagementPackages(pkgClient, mcip.ManagementPackageRepositoryOptions); err != nil {
65241
// instead of throwing error here, wait for some additional time for packages to get reconciled successfully
66242
// error will be thrown at the next step if packages are not reconciled after timeout value

0 commit comments

Comments
 (0)