github.com/koderover/helm@v2.17.0+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 // Timeout used when deleting resources with a hook-delete-policy. 57 const defaultHookDeleteTimeoutInSeconds = int64(60) 58 59 // Manifest represents a manifest file, which has a name and some content. 60 type Manifest = manifest.Manifest 61 62 type result struct { 63 hooks []*release.Hook 64 generic []Manifest 65 } 66 67 type manifestFile struct { 68 entries map[string]string 69 path string 70 apis chartutil.VersionSet 71 } 72 73 // sortManifests takes a map of filename/YAML contents, splits the file 74 // by manifest entries, and sorts the entries into hook types. 75 // 76 // The resulting hooks struct will be populated with all of the generated hooks. 77 // Any file that does not declare one of the hook types will be placed in the 78 // 'generic' bucket. 79 // 80 // Files that do not parse into the expected format are simply placed into a map and 81 // returned. 82 func sortManifests(files map[string]string, apis chartutil.VersionSet, sort SortOrder) ([]*release.Hook, []Manifest, error) { 83 result := &result{} 84 85 for filePath, c := range files { 86 87 // Skip partials. We could return these as a separate map, but there doesn't 88 // seem to be any need for that at this time. 89 if strings.HasPrefix(path.Base(filePath), "_") { 90 continue 91 } 92 // Skip empty files and log this. 93 if len(strings.TrimSpace(c)) == 0 { 94 log.Printf("info: manifest %q is empty. Skipping.", filePath) 95 continue 96 } 97 98 manifestFile := &manifestFile{ 99 entries: util.SplitManifests(c), 100 path: filePath, 101 apis: apis, 102 } 103 104 if err := manifestFile.sort(result); err != nil { 105 return result.hooks, result.generic, err 106 } 107 } 108 109 return result.hooks, sortByKind(result.generic, sort), nil 110 } 111 112 // sort takes a manifestFile object which may contain multiple resource definition 113 // entries and sorts each entry by hook types, and saves the resulting hooks and 114 // generic manifests (or non-hooks) to the result struct. 115 // 116 // To determine hook type, it looks for a YAML structure like this: 117 // 118 // kind: SomeKind 119 // apiVersion: v1 120 // metadata: 121 // annotations: 122 // helm.sh/hook: pre-install 123 // 124 // To determine the policy to delete the hook, it looks for a YAML structure like this: 125 // 126 // kind: SomeKind 127 // apiVersion: v1 128 // metadata: 129 // annotations: 130 // helm.sh/hook-delete-policy: hook-succeeded 131 func (file *manifestFile) sort(result *result) error { 132 for _, m := range file.entries { 133 var entry util.SimpleHead 134 err := yaml.Unmarshal([]byte(m), &entry) 135 136 if err != nil { 137 e := fmt.Errorf("YAML parse error on %s: %s", file.path, err) 138 return e 139 } 140 141 if !hasAnyAnnotation(entry) { 142 result.generic = append(result.generic, Manifest{ 143 Name: file.path, 144 Content: m, 145 Head: &entry, 146 }) 147 continue 148 } 149 150 hookTypes, ok := entry.Metadata.Annotations[hooks.HookAnno] 151 if !ok { 152 result.generic = append(result.generic, Manifest{ 153 Name: file.path, 154 Content: m, 155 Head: &entry, 156 }) 157 continue 158 } 159 160 hw := calculateHookWeight(entry) 161 162 h := &release.Hook{ 163 Name: entry.Metadata.Name, 164 Kind: entry.Kind, 165 Path: file.path, 166 Manifest: m, 167 Events: []release.Hook_Event{}, 168 Weight: hw, 169 DeletePolicies: []release.Hook_DeletePolicy{}, 170 } 171 172 isUnknownHook := false 173 for _, hookType := range strings.Split(hookTypes, ",") { 174 hookType = strings.ToLower(strings.TrimSpace(hookType)) 175 e, ok := events[hookType] 176 if !ok { 177 isUnknownHook = true 178 break 179 } 180 h.Events = append(h.Events, e) 181 } 182 183 if isUnknownHook { 184 log.Printf("info: skipping unknown hook: %q", hookTypes) 185 continue 186 } 187 188 result.hooks = append(result.hooks, h) 189 190 operateAnnotationValues(entry, hooks.HookDeleteAnno, func(value string) { 191 policy, exist := deletePolices[value] 192 if exist { 193 h.DeletePolicies = append(h.DeletePolicies, policy) 194 } else { 195 log.Printf("info: skipping unknown hook delete policy: %q", value) 196 } 197 }) 198 199 // Only check for delete timeout annotation if there is a deletion policy. 200 if len(h.DeletePolicies) > 0 { 201 h.DeleteTimeout = defaultHookDeleteTimeoutInSeconds 202 operateAnnotationValues(entry, hooks.HookDeleteTimeoutAnno, func(value string) { 203 timeout, err := strconv.ParseInt(value, 10, 64) 204 if err != nil || timeout < 0 { 205 log.Printf("info: ignoring invalid hook delete timeout value: %q", value) 206 } 207 h.DeleteTimeout = timeout 208 }) 209 } 210 } 211 return nil 212 } 213 214 func hasAnyAnnotation(entry util.SimpleHead) bool { 215 if entry.Metadata == nil || 216 entry.Metadata.Annotations == nil || 217 len(entry.Metadata.Annotations) == 0 { 218 return false 219 } 220 221 return true 222 } 223 224 func calculateHookWeight(entry util.SimpleHead) int32 { 225 hws := entry.Metadata.Annotations[hooks.HookWeightAnno] 226 hw, err := strconv.Atoi(hws) 227 if err != nil { 228 hw = 0 229 } 230 231 return int32(hw) 232 } 233 234 func operateAnnotationValues(entry util.SimpleHead, annotation string, operate func(p string)) { 235 if dps, ok := entry.Metadata.Annotations[annotation]; ok { 236 for _, dp := range strings.Split(dps, ",") { 237 dp = strings.ToLower(strings.TrimSpace(dp)) 238 operate(dp) 239 } 240 } 241 }