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 }