github.com/Beeketing/helm@v2.12.1+incompatible/pkg/tiller/hooks.go (about) 1 /* 2 Copyright The Helm Authors. 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/manifest" 31 "k8s.io/helm/pkg/proto/hapi/release" 32 util "k8s.io/helm/pkg/releaseutil" 33 ) 34 35 var events = map[string]release.Hook_Event{ 36 hooks.PreInstall: release.Hook_PRE_INSTALL, 37 hooks.PostInstall: release.Hook_POST_INSTALL, 38 hooks.PreDelete: release.Hook_PRE_DELETE, 39 hooks.PostDelete: release.Hook_POST_DELETE, 40 hooks.PreUpgrade: release.Hook_PRE_UPGRADE, 41 hooks.PostUpgrade: release.Hook_POST_UPGRADE, 42 hooks.PreRollback: release.Hook_PRE_ROLLBACK, 43 hooks.PostRollback: release.Hook_POST_ROLLBACK, 44 hooks.ReleaseTestSuccess: release.Hook_RELEASE_TEST_SUCCESS, 45 hooks.ReleaseTestFailure: release.Hook_RELEASE_TEST_FAILURE, 46 hooks.CRDInstall: release.Hook_CRD_INSTALL, 47 } 48 49 // deletePolices represents a mapping between the key in the annotation for label deleting policy and its real meaning 50 var deletePolices = map[string]release.Hook_DeletePolicy{ 51 hooks.HookSucceeded: release.Hook_SUCCEEDED, 52 hooks.HookFailed: release.Hook_FAILED, 53 hooks.BeforeHookCreation: release.Hook_BEFORE_HOOK_CREATION, 54 } 55 56 // Manifest represents a manifest file, which has a name and some content. 57 type Manifest = manifest.Manifest 58 59 type result struct { 60 hooks []*release.Hook 61 generic []Manifest 62 } 63 64 type manifestFile struct { 65 entries map[string]string 66 path string 67 apis chartutil.VersionSet 68 } 69 70 // sortManifests takes a map of filename/YAML contents, splits the file 71 // by manifest entries, and sorts the entries into hook types. 72 // 73 // The resulting hooks struct will be populated with all of the generated hooks. 74 // Any file that does not declare one of the hook types will be placed in the 75 // 'generic' bucket. 76 // 77 // Files that do not parse into the expected format are simply placed into a map and 78 // returned. 79 func sortManifests(files map[string]string, apis chartutil.VersionSet, sort SortOrder) ([]*release.Hook, []Manifest, error) { 80 result := &result{} 81 82 for filePath, c := range files { 83 84 // Skip partials. We could return these as a separate map, but there doesn't 85 // seem to be any need for that at this time. 86 if strings.HasPrefix(path.Base(filePath), "_") { 87 continue 88 } 89 // Skip empty files and log this. 90 if len(strings.TrimSpace(c)) == 0 { 91 log.Printf("info: manifest %q is empty. Skipping.", filePath) 92 continue 93 } 94 95 manifestFile := &manifestFile{ 96 entries: util.SplitManifests(c), 97 path: filePath, 98 apis: apis, 99 } 100 101 if err := manifestFile.sort(result); err != nil { 102 return result.hooks, result.generic, err 103 } 104 } 105 106 return result.hooks, sortByKind(result.generic, sort), nil 107 } 108 109 // sort takes a manifestFile object which may contain multiple resource definition 110 // entries and sorts each entry by hook types, and saves the resulting hooks and 111 // generic manifests (or non-hooks) to the result struct. 112 // 113 // To determine hook type, it looks for a YAML structure like this: 114 // 115 // kind: SomeKind 116 // apiVersion: v1 117 // metadata: 118 // annotations: 119 // helm.sh/hook: pre-install 120 // 121 // To determine the policy to delete the hook, it looks for a YAML structure like this: 122 // 123 // kind: SomeKind 124 // apiVersion: v1 125 // metadata: 126 // annotations: 127 // helm.sh/hook-delete-policy: hook-succeeded 128 func (file *manifestFile) sort(result *result) error { 129 for _, m := range file.entries { 130 var entry util.SimpleHead 131 err := yaml.Unmarshal([]byte(m), &entry) 132 133 if err != nil { 134 e := fmt.Errorf("YAML parse error on %s: %s", file.path, err) 135 return e 136 } 137 138 if !hasAnyAnnotation(entry) { 139 result.generic = append(result.generic, Manifest{ 140 Name: file.path, 141 Content: m, 142 Head: &entry, 143 }) 144 continue 145 } 146 147 hookTypes, ok := entry.Metadata.Annotations[hooks.HookAnno] 148 if !ok { 149 result.generic = append(result.generic, Manifest{ 150 Name: file.path, 151 Content: m, 152 Head: &entry, 153 }) 154 continue 155 } 156 157 hw := calculateHookWeight(entry) 158 159 h := &release.Hook{ 160 Name: entry.Metadata.Name, 161 Kind: entry.Kind, 162 Path: file.path, 163 Manifest: m, 164 Events: []release.Hook_Event{}, 165 Weight: hw, 166 DeletePolicies: []release.Hook_DeletePolicy{}, 167 } 168 169 isUnknownHook := false 170 for _, hookType := range strings.Split(hookTypes, ",") { 171 hookType = strings.ToLower(strings.TrimSpace(hookType)) 172 e, ok := events[hookType] 173 if !ok { 174 isUnknownHook = true 175 break 176 } 177 h.Events = append(h.Events, e) 178 } 179 180 if isUnknownHook { 181 log.Printf("info: skipping unknown hook: %q", hookTypes) 182 continue 183 } 184 185 result.hooks = append(result.hooks, h) 186 187 operateAnnotationValues(entry, hooks.HookDeleteAnno, func(value string) { 188 policy, exist := deletePolices[value] 189 if exist { 190 h.DeletePolicies = append(h.DeletePolicies, policy) 191 } else { 192 log.Printf("info: skipping unknown hook delete policy: %q", value) 193 } 194 }) 195 } 196 return nil 197 } 198 199 func hasAnyAnnotation(entry util.SimpleHead) bool { 200 if entry.Metadata == nil || 201 entry.Metadata.Annotations == nil || 202 len(entry.Metadata.Annotations) == 0 { 203 return false 204 } 205 206 return true 207 } 208 209 func calculateHookWeight(entry util.SimpleHead) int32 { 210 hws := entry.Metadata.Annotations[hooks.HookWeightAnno] 211 hw, err := strconv.Atoi(hws) 212 if err != nil { 213 hw = 0 214 } 215 216 return int32(hw) 217 } 218 219 func operateAnnotationValues(entry util.SimpleHead, annotation string, operate func(p string)) { 220 if dps, ok := entry.Metadata.Annotations[annotation]; ok { 221 for _, dp := range strings.Split(dps, ",") { 222 dp = strings.ToLower(strings.TrimSpace(dp)) 223 operate(dp) 224 } 225 } 226 }