@@ -5,13 +5,18 @@ package managementcomponents
5
5
6
6
import (
7
7
"context"
8
+ "encoding/json"
9
+ "fmt"
8
10
"os"
9
11
"time"
10
12
11
13
"github.com/pkg/errors"
12
14
"golang.org/x/sync/errgroup"
15
+ corev1 "k8s.io/api/core/v1"
16
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
13
17
"k8s.io/apimachinery/pkg/labels"
14
18
"k8s.io/apimachinery/pkg/selection"
19
+ "k8s.io/apimachinery/pkg/types"
15
20
crtclient "sigs.k8s.io/controller-runtime/pkg/client"
16
21
17
22
kappipkg "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/packaging/v1alpha1"
@@ -23,6 +28,8 @@ import (
23
28
"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/tkgpackagedatamodel"
24
29
)
25
30
31
+ const addonsManagerName = "addons-manager"
32
+
26
33
// ClusterOptions specifies cluster configuration
27
34
type ClusterOptions struct {
28
35
Kubeconfig string
@@ -49,6 +56,105 @@ type ManagementComponentsInstallOptions struct {
49
56
ManagementPackageRepositoryOptions ManagementPackageRepositoryOptions
50
57
}
51
58
59
+ func generateAddonSecretName (clusterName , addonName string ) string {
60
+ return fmt .Sprintf ("%s-%s-addon" , clusterName , addonName )
61
+ }
62
+
63
+ func generateAddonsPkgiName (addonName string ) string {
64
+ return fmt .Sprintf ("tanzu-%s" , addonName )
65
+ }
66
+ func pauseAddonSecretReconciliation (ctx context.Context , clusterClient clusterclient.Client , clusterName , addonSecreteName , namespace string ) error {
67
+ secret := & corev1.Secret {}
68
+ jsonPatch := []clusterclient.JSONPatch {
69
+ {
70
+ Op : "add" ,
71
+ Path : fmt .Sprintf ("/metadata/annotations/tkg.tanzu.vmware.com~1addon-paused" ),
72
+ Value : "" ,
73
+ },
74
+ }
75
+ payloadBytes , err := json .Marshal (jsonPatch )
76
+ if err != nil {
77
+ return errors .Wrap (err , "unable to generate json patch" )
78
+ }
79
+
80
+ err = clusterClient .PatchResource (secret , addonSecreteName , namespace , string (payloadBytes ), types .JSONPatchType , nil )
81
+ if apierrors .IsNotFound (err ) {
82
+ return nil
83
+ }
84
+ if err != nil {
85
+ return errors .Wrapf (err , "failed to pause %s secret reconciliation" , addonSecreteName )
86
+ }
87
+ return nil
88
+ }
89
+
90
+ func pausePackageInstallReconciliation (ctx context.Context , clusterClient clusterclient.Client , pkgiName , namespace string ) error {
91
+ pkgi := & kappipkg.PackageInstall {}
92
+ jsonPatch := []map [string ]interface {}{
93
+ {
94
+ "op" : "add" ,
95
+ "path" : "/spec/paused" ,
96
+ "value" : true ,
97
+ },
98
+ }
99
+ payloadBytes , err := json .Marshal (jsonPatch )
100
+ if err != nil {
101
+ return errors .Wrap (err , "unable to generate json patch" )
102
+ }
103
+ err = clusterClient .PatchResource (pkgi , pkgiName , namespace , string (payloadBytes ), types .JSONPatchType , nil )
104
+ if apierrors .IsNotFound (err ) {
105
+ return nil
106
+ }
107
+ if err != nil {
108
+ return errors .Wrapf (err , "failed to pause %s packageinstall reconciliation" , pkgiName )
109
+ }
110
+ return nil
111
+ }
112
+
113
+ // PausedAdoonLifecycleManagement pauses/unpauses the lifecycle management of addon package with given name and namespace
114
+ func PauseAddonLifecycleManagement (mcip * ManagementComponentsInstallOptions , addonName , namespace string ) error {
115
+ clusterClient , err := clusterclient .NewClient (mcip .ClusterOptions .Kubeconfig , mcip .ClusterOptions .Kubecontext , clusterclient.Options {})
116
+ if err != nil {
117
+ return err
118
+ }
119
+ clusterName , err := clusterClient .GetCurrentClusterName (mcip .ClusterOptions .Kubecontext )
120
+ if err != nil {
121
+ return err
122
+ }
123
+ pkgiName := generateAddonsPkgiName (addonsManagerName )
124
+ addonSecretName := generateAddonSecretName (clusterName , addonsManagerName )
125
+
126
+ err = pauseAddonSecretReconciliation (context .TODO (), clusterClient , clusterName , addonSecretName , namespace )
127
+ if err != nil {
128
+ return err
129
+ }
130
+
131
+ err = pausePackageInstallReconciliation (context .TODO (), clusterClient , pkgiName , namespace )
132
+ if err != nil {
133
+ return err
134
+ }
135
+
136
+ return nil
137
+ }
138
+ func DeleteAddonSecret (mcip * ManagementComponentsInstallOptions , addonName , namespace string ) error {
139
+ clusterClient , err := clusterclient .NewClient (mcip .ClusterOptions .Kubeconfig , mcip .ClusterOptions .Kubecontext , clusterclient.Options {})
140
+ if err != nil {
141
+ return err
142
+ }
143
+ clusterName , err := clusterClient .GetCurrentClusterName (mcip .ClusterOptions .Kubecontext )
144
+ if err != nil {
145
+ return err
146
+ }
147
+
148
+ secret := & corev1.Secret {}
149
+ secret .Name = generateAddonSecretName (clusterName , addonName )
150
+ secret .Namespace = constants .TkgNamespace
151
+ err = clusterClient .DeleteResource (secret )
152
+ if err != nil {
153
+ return errors .Wrapf (err , "failed to delete addon secret %s" , secret )
154
+ }
155
+ return nil
156
+ }
157
+
52
158
// InstallManagementComponents installs the management component to cluster
53
159
func InstallManagementComponents (mcip * ManagementComponentsInstallOptions ) error {
54
160
clusterClient , err := clusterclient .NewClient (mcip .ClusterOptions .Kubeconfig , mcip .ClusterOptions .Kubecontext , clusterclient.Options {})
@@ -61,6 +167,22 @@ func InstallManagementComponents(mcip *ManagementComponentsInstallOptions) error
61
167
if err != nil {
62
168
return err
63
169
}
170
+
171
+ err = PauseAddonLifecycleManagement (mcip , addonsManagerName , constants .TkgNamespace )
172
+ if err != nil {
173
+ panic (err )
174
+ }
175
+
176
+ err = NoopDeletePackageInstall (mcip , addonsManagerName , constants .TkgNamespace )
177
+ if err != nil {
178
+ return nil
179
+ }
180
+
181
+ err = DeleteAddonSecret (mcip , addonsManagerName , constants .TkgNamespace )
182
+ if err != nil {
183
+ return err
184
+ }
185
+
64
186
if err = InstallManagementPackages (pkgClient , mcip .ManagementPackageRepositoryOptions ); err != nil {
65
187
// instead of throwing error here, wait for some additional time for packages to get reconciled successfully
66
188
// error will be thrown at the next step if packages are not reconciled after timeout value
@@ -196,3 +318,41 @@ func WaitForManagementPackages(clusterClient clusterclient.Client, packageInstal
196
318
}
197
319
return nil
198
320
}
321
+
322
+ // NoopDeletePackageInstall sets spec.noopdelete = true before deleting the package install
323
+ func NoopDeletePackageInstall (mcip * ManagementComponentsInstallOptions , addonName , namespace string ) error {
324
+ clusterClient , err := clusterclient .NewClient (mcip .ClusterOptions .Kubeconfig , mcip .ClusterOptions .Kubecontext , clusterclient.Options {})
325
+ if err != nil {
326
+ return err
327
+ }
328
+ pkgiName := fmt .Sprintf ("tanzu-%s" , addonName )
329
+
330
+ jsonPatch := []map [string ]interface {}{
331
+ {
332
+ "op" : "add" ,
333
+ "path" : "/spec/noopDelete" ,
334
+ "value" : true ,
335
+ },
336
+ }
337
+ payloadBytes , err := json .Marshal (jsonPatch )
338
+ if err != nil {
339
+ return errors .Wrap (err , "unable to generate json patch" )
340
+ }
341
+ pkgi := & kappipkg.PackageInstall {}
342
+ pkgi .Name = pkgiName
343
+ pkgi .Namespace = namespace
344
+ err = clusterClient .PatchResource (pkgi , pkgiName , namespace , string (payloadBytes ), types .JSONPatchType , nil )
345
+ if apierrors .IsNotFound (err ) {
346
+ return nil
347
+ }
348
+ if err != nil {
349
+ return errors .Wrapf (err , "failed to patch %s packageinstall" , pkgiName )
350
+ }
351
+
352
+ // TODO (adduarter) should we check to make sure noopdelete is configured?
353
+ err = clusterClient .DeleteResource (pkgi )
354
+ if err != nil {
355
+ return errors .Wrapf (err , "failed to delete PackageInstall resource %s" , pkgiName )
356
+ }
357
+ return nil
358
+ }
0 commit comments