@@ -17,16 +17,21 @@ limitations under the License.
17
17
package action
18
18
19
19
import (
20
+ "bytes"
20
21
"path"
21
22
"regexp"
23
+ "sort"
24
+ "strings"
22
25
"time"
23
26
24
27
"github.com/pkg/errors"
25
28
"k8s.io/apimachinery/pkg/api/meta"
26
29
"k8s.io/client-go/discovery"
30
+ "k8s.io/client-go/kubernetes"
27
31
"k8s.io/client-go/rest"
28
32
29
33
"helm.sh/helm/pkg/chartutil"
34
+ "helm.sh/helm/pkg/hooks"
30
35
"helm.sh/helm/pkg/kube"
31
36
"helm.sh/helm/pkg/registry"
32
37
"helm.sh/helm/pkg/release"
@@ -110,6 +115,15 @@ func (c *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
110
115
return c .Capabilities , nil
111
116
}
112
117
118
+ func (c * Configuration ) KubernetesClientSet () (kubernetes.Interface , error ) {
119
+ conf , err := c .RESTClientGetter .ToRESTConfig ()
120
+ if err != nil {
121
+ return nil , errors .Wrap (err , "unable to generate config for kubernetes client" )
122
+ }
123
+
124
+ return kubernetes .NewForConfig (conf )
125
+ }
126
+
113
127
// Now generates a timestamp
114
128
//
115
129
// If the configuration has a Timestamper on it, that will be used.
@@ -190,3 +204,73 @@ type RESTClientGetter interface {
190
204
ToDiscoveryClient () (discovery.CachedDiscoveryInterface , error )
191
205
ToRESTMapper () (meta.RESTMapper , error )
192
206
}
207
+
208
+ // execHooks is a method for exec-ing all hooks of the given type. This is to
209
+ // avoid duplicate code in various actions
210
+ func execHooks (client kube.Interface , hs []* release.Hook , hook string , timeout time.Duration ) error {
211
+ executingHooks := []* release.Hook {}
212
+
213
+ for _ , h := range hs {
214
+ for _ , e := range h .Events {
215
+ if string (e ) == hook {
216
+ executingHooks = append (executingHooks , h )
217
+ }
218
+ }
219
+ }
220
+
221
+ sort .Sort (hookByWeight (executingHooks ))
222
+ for _ , h := range executingHooks {
223
+ if err := deleteHookByPolicy (client , h , hooks .BeforeHookCreation ); err != nil {
224
+ return err
225
+ }
226
+
227
+ resources , err := client .Build (bytes .NewBufferString (h .Manifest ))
228
+ if err != nil {
229
+ return errors .Wrapf (err , "unable to build kubernetes object for %s hook %s" , hook , h .Path )
230
+ }
231
+ if _ , err := client .Create (resources ); err != nil {
232
+ return errors .Wrapf (err , "warning: Hook %s %s failed" , hook , h .Path )
233
+ }
234
+
235
+ if err := client .WatchUntilReady (resources , timeout ); err != nil {
236
+ // If a hook is failed, checkout the annotation of the hook to determine whether the hook should be deleted
237
+ // under failed condition. If so, then clear the corresponding resource object in the hook
238
+ if err := deleteHookByPolicy (client , h , hooks .HookFailed ); err != nil {
239
+ return err
240
+ }
241
+ return err
242
+ }
243
+ }
244
+
245
+ // If all hooks are succeeded, checkout the annotation of each hook to determine whether the hook should be deleted
246
+ // under succeeded condition. If so, then clear the corresponding resource object in each hook
247
+ for _ , h := range executingHooks {
248
+ if err := deleteHookByPolicy (client , h , hooks .HookSucceeded ); err != nil {
249
+ return err
250
+ }
251
+ h .LastRun = time .Now ()
252
+ }
253
+
254
+ return nil
255
+ }
256
+
257
+ // deleteHookByPolicy deletes a hook if the hook policy instructs it to
258
+ func deleteHookByPolicy (client kube.Interface , h * release.Hook , policy string ) error {
259
+ if hookHasDeletePolicy (h , policy ) {
260
+ resources , err := client .Build (bytes .NewBufferString (h .Manifest ))
261
+ if err != nil {
262
+ return errors .Wrapf (err , "unable to build kubernetes object for deleting hook %s" , h .Path )
263
+ }
264
+ _ , errs := client .Delete (resources )
265
+ return errors .New (joinErrors (errs ))
266
+ }
267
+ return nil
268
+ }
269
+
270
+ func joinErrors (errs []error ) string {
271
+ es := make ([]string , 0 , len (errs ))
272
+ for _ , e := range errs {
273
+ es = append (es , e .Error ())
274
+ }
275
+ return strings .Join (es , "; " )
276
+ }
0 commit comments