github.com/x-helm/helm@v3.0.0-beta.3+incompatible/pkg/action/hooks.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package action
    17  
    18  import (
    19  	"bytes"
    20  	"sort"
    21  	"time"
    22  
    23  	"github.com/pkg/errors"
    24  
    25  	"helm.sh/helm/pkg/release"
    26  )
    27  
    28  // execHook executes all of the hooks for the given hook event.
    29  func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent, timeout time.Duration) error {
    30  	executingHooks := []*release.Hook{}
    31  
    32  	for _, h := range rl.Hooks {
    33  		for _, e := range h.Events {
    34  			if e == hook {
    35  				executingHooks = append(executingHooks, h)
    36  			}
    37  		}
    38  	}
    39  
    40  	sort.Sort(hookByWeight(executingHooks))
    41  
    42  	for _, h := range executingHooks {
    43  		if err := cfg.deleteHookByPolicy(h, release.HookBeforeHookCreation); err != nil {
    44  			return err
    45  		}
    46  
    47  		resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest))
    48  		if err != nil {
    49  			return errors.Wrapf(err, "unable to build kubernetes object for %s hook %s", hook, h.Path)
    50  		}
    51  
    52  		// Record the time at which the hook was applied to the cluster
    53  		h.LastRun = release.HookExecution{
    54  			StartedAt: time.Now(),
    55  			Phase:     release.HookPhaseRunning,
    56  		}
    57  		cfg.recordRelease(rl)
    58  
    59  		// As long as the implementation of WatchUntilReady does not panic, HookPhaseFailed or HookPhaseSucceeded
    60  		// should always be set by this function. If we fail to do that for any reason, then HookPhaseUnknown is
    61  		// the most appropriate value to surface.
    62  		h.LastRun.Phase = release.HookPhaseUnknown
    63  
    64  		// Create hook resources
    65  		if _, err := cfg.KubeClient.Create(resources); err != nil {
    66  			h.LastRun.CompletedAt = time.Now()
    67  			h.LastRun.Phase = release.HookPhaseFailed
    68  			return errors.Wrapf(err, "warning: Hook %s %s failed", hook, h.Path)
    69  		}
    70  
    71  		// Watch hook resources until they have completed
    72  		err = cfg.KubeClient.WatchUntilReady(resources, timeout)
    73  		// Note the time of success/failure
    74  		h.LastRun.CompletedAt = time.Now()
    75  		// Mark hook as succeeded or failed
    76  		if err != nil {
    77  			h.LastRun.Phase = release.HookPhaseFailed
    78  			// If a hook is failed, check the annotation of the hook to determine whether the hook should be deleted
    79  			// under failed condition. If so, then clear the corresponding resource object in the hook
    80  			if err := cfg.deleteHookByPolicy(h, release.HookFailed); err != nil {
    81  				return err
    82  			}
    83  			return err
    84  		}
    85  		h.LastRun.Phase = release.HookPhaseSucceeded
    86  	}
    87  
    88  	// If all hooks are successful, check the annotation of each hook to determine whether the hook should be deleted
    89  	// under succeeded condition. If so, then clear the corresponding resource object in each hook
    90  	for _, h := range executingHooks {
    91  		if err := cfg.deleteHookByPolicy(h, release.HookSucceeded); err != nil {
    92  			return err
    93  		}
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  // hookByWeight is a sorter for hooks
   100  type hookByWeight []*release.Hook
   101  
   102  func (x hookByWeight) Len() int      { return len(x) }
   103  func (x hookByWeight) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
   104  func (x hookByWeight) Less(i, j int) bool {
   105  	if x[i].Weight == x[j].Weight {
   106  		return x[i].Name < x[j].Name
   107  	}
   108  	return x[i].Weight < x[j].Weight
   109  }
   110  
   111  // deleteHookByPolicy deletes a hook if the hook policy instructs it to
   112  func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy) error {
   113  	if hookHasDeletePolicy(h, policy) {
   114  		resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest))
   115  		if err != nil {
   116  			return errors.Wrapf(err, "unable to build kubernetes object for deleting hook %s", h.Path)
   117  		}
   118  		_, errs := cfg.KubeClient.Delete(resources)
   119  		if len(errs) > 0 {
   120  			return errors.New(joinErrors(errs))
   121  		}
   122  	}
   123  	return nil
   124  }
   125  
   126  // hookHasDeletePolicy determines whether the defined hook deletion policy matches the hook deletion polices
   127  // supported by helm. If so, mark the hook as one should be deleted.
   128  func hookHasDeletePolicy(h *release.Hook, policy release.HookDeletePolicy) bool {
   129  	for _, v := range h.DeletePolicies {
   130  		if policy == v {
   131  			return true
   132  		}
   133  	}
   134  	return false
   135  }