github.com/zoumo/helm@v2.5.0+incompatible/pkg/tiller/hooks.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors All rights reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tiller
    18  
    19  import (
    20  	"fmt"
    21  	"log"
    22  	"path"
    23  	"strconv"
    24  	"strings"
    25  
    26  	"github.com/ghodss/yaml"
    27  
    28  	"k8s.io/helm/pkg/chartutil"
    29  	"k8s.io/helm/pkg/hooks"
    30  	"k8s.io/helm/pkg/proto/hapi/release"
    31  	util "k8s.io/helm/pkg/releaseutil"
    32  )
    33  
    34  var events = map[string]release.Hook_Event{
    35  	hooks.PreInstall:         release.Hook_PRE_INSTALL,
    36  	hooks.PostInstall:        release.Hook_POST_INSTALL,
    37  	hooks.PreDelete:          release.Hook_PRE_DELETE,
    38  	hooks.PostDelete:         release.Hook_POST_DELETE,
    39  	hooks.PreUpgrade:         release.Hook_PRE_UPGRADE,
    40  	hooks.PostUpgrade:        release.Hook_POST_UPGRADE,
    41  	hooks.PreRollback:        release.Hook_PRE_ROLLBACK,
    42  	hooks.PostRollback:       release.Hook_POST_ROLLBACK,
    43  	hooks.ReleaseTestSuccess: release.Hook_RELEASE_TEST_SUCCESS,
    44  	hooks.ReleaseTestFailure: release.Hook_RELEASE_TEST_FAILURE,
    45  }
    46  
    47  // manifest represents a manifest file, which has a name and some content.
    48  type manifest struct {
    49  	name    string
    50  	content string
    51  	head    *util.SimpleHead
    52  }
    53  
    54  type result struct {
    55  	hooks   []*release.Hook
    56  	generic []manifest
    57  }
    58  
    59  type manifestFile struct {
    60  	entries map[string]string
    61  	path    string
    62  	apis    chartutil.VersionSet
    63  }
    64  
    65  // sortManifests takes a map of filename/YAML contents, splits the file
    66  // by manifest entries, and sorts the entries into hook types.
    67  //
    68  // The resulting hooks struct will be populated with all of the generated hooks.
    69  // Any file that does not declare one of the hook types will be placed in the
    70  // 'generic' bucket.
    71  //
    72  // Files that do not parse into the expected format are simply placed into a map and
    73  // returned.
    74  func sortManifests(files map[string]string, apis chartutil.VersionSet, sort SortOrder) ([]*release.Hook, []manifest, error) {
    75  	result := &result{}
    76  
    77  	for filePath, c := range files {
    78  
    79  		// Skip partials. We could return these as a separate map, but there doesn't
    80  		// seem to be any need for that at this time.
    81  		if strings.HasPrefix(path.Base(filePath), "_") {
    82  			continue
    83  		}
    84  		// Skip empty files and log this.
    85  		if len(strings.TrimSpace(c)) == 0 {
    86  			log.Printf("info: manifest %q is empty. Skipping.", filePath)
    87  			continue
    88  		}
    89  
    90  		manifestFile := &manifestFile{
    91  			entries: util.SplitManifests(c),
    92  			path:    filePath,
    93  			apis:    apis,
    94  		}
    95  
    96  		if err := manifestFile.sort(result); err != nil {
    97  			return result.hooks, result.generic, err
    98  		}
    99  	}
   100  
   101  	return result.hooks, sortByKind(result.generic, sort), nil
   102  }
   103  
   104  // sort takes a manifestFile object which may contain multiple resource definition
   105  // entries and sorts each entry by hook types, and saves the resulting hooks and
   106  // generic manifests (or non-hooks) to the result struct.
   107  //
   108  // To determine hook type, it looks for a YAML structure like this:
   109  //
   110  //  kind: SomeKind
   111  //  apiVersion: v1
   112  // 	metadata:
   113  //		annotations:
   114  //			helm.sh/hook: pre-install
   115  //
   116  func (file *manifestFile) sort(result *result) error {
   117  	for _, m := range file.entries {
   118  		var entry util.SimpleHead
   119  		err := yaml.Unmarshal([]byte(m), &entry)
   120  
   121  		if err != nil {
   122  			e := fmt.Errorf("YAML parse error on %s: %s", file.path, err)
   123  			return e
   124  		}
   125  
   126  		if entry.Version != "" && !file.apis.Has(entry.Version) {
   127  			return fmt.Errorf("apiVersion %q in %s is not available", entry.Version, file.path)
   128  		}
   129  
   130  		if !hasAnyAnnotation(entry) {
   131  			result.generic = append(result.generic, manifest{
   132  				name:    file.path,
   133  				content: m,
   134  				head:    &entry,
   135  			})
   136  			continue
   137  		}
   138  
   139  		hookTypes, ok := entry.Metadata.Annotations[hooks.HookAnno]
   140  		if !ok {
   141  			result.generic = append(result.generic, manifest{
   142  				name:    file.path,
   143  				content: m,
   144  				head:    &entry,
   145  			})
   146  			continue
   147  		}
   148  
   149  		hw := calculateHookWeight(entry)
   150  
   151  		h := &release.Hook{
   152  			Name:     entry.Metadata.Name,
   153  			Kind:     entry.Kind,
   154  			Path:     file.path,
   155  			Manifest: m,
   156  			Events:   []release.Hook_Event{},
   157  			Weight:   hw,
   158  		}
   159  
   160  		isKnownHook := false
   161  		for _, hookType := range strings.Split(hookTypes, ",") {
   162  			hookType = strings.ToLower(strings.TrimSpace(hookType))
   163  			e, ok := events[hookType]
   164  			if ok {
   165  				isKnownHook = true
   166  				h.Events = append(h.Events, e)
   167  			}
   168  		}
   169  
   170  		if !isKnownHook {
   171  			log.Printf("info: skipping unknown hook: %q", hookTypes)
   172  			continue
   173  		}
   174  
   175  		result.hooks = append(result.hooks, h)
   176  	}
   177  
   178  	return nil
   179  }
   180  
   181  func hasAnyAnnotation(entry util.SimpleHead) bool {
   182  	if entry.Metadata == nil ||
   183  		entry.Metadata.Annotations == nil ||
   184  		len(entry.Metadata.Annotations) == 0 {
   185  		return false
   186  	}
   187  
   188  	return true
   189  }
   190  
   191  func calculateHookWeight(entry util.SimpleHead) int32 {
   192  	hws, _ := entry.Metadata.Annotations[hooks.HookWeightAnno]
   193  	hw, err := strconv.Atoi(hws)
   194  	if err != nil {
   195  		hw = 0
   196  	}
   197  
   198  	return int32(hw)
   199  }