@@ -5,14 +5,20 @@ 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"
21
+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
16
22
17
23
kappipkg "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/packaging/v1alpha1"
18
24
@@ -23,6 +29,11 @@ import (
23
29
"github.com/vmware-tanzu/tanzu-framework/pkg/v1/tkg/tkgpackagedatamodel"
24
30
)
25
31
32
+ const (
33
+ addonsManagerName = "addons-manager"
34
+ addonFinalizer = "tkg.tanzu.vmware.com/addon"
35
+ )
36
+
26
37
// ClusterOptions specifies cluster configuration
27
38
type ClusterOptions struct {
28
39
Kubeconfig string
@@ -49,18 +60,183 @@ type ManagementComponentsInstallOptions struct {
49
60
ManagementPackageRepositoryOptions ManagementPackageRepositoryOptions
50
61
}
51
62
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
+
52
208
// InstallManagementComponents installs the management component to cluster
53
209
func InstallManagementComponents (mcip * ManagementComponentsInstallOptions ) error {
54
210
clusterClient , err := clusterclient .NewClient (mcip .ClusterOptions .Kubeconfig , mcip .ClusterOptions .Kubecontext , clusterclient.Options {})
55
211
if err != nil {
56
212
return errors .Wrap (err , "unable to get cluster client" )
57
213
}
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
+ }
58
221
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 )
61
233
if err != nil {
62
234
return err
63
235
}
236
+
237
+ // create package client
238
+ pkgClient , err := tkgpackageclient .NewTKGPackageClientForContext (mcip .ClusterOptions .Kubeconfig , mcip .ClusterOptions .Kubecontext )
239
+
64
240
if err = InstallManagementPackages (pkgClient , mcip .ManagementPackageRepositoryOptions ); err != nil {
65
241
// instead of throwing error here, wait for some additional time for packages to get reconciled successfully
66
242
// error will be thrown at the next step if packages are not reconciled after timeout value
0 commit comments