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