github.com/zoumo/helm@v2.5.0+incompatible/pkg/releaseutil/annotation.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 releaseutil 18 19 import ( 20 "bytes" 21 "log" 22 23 "k8s.io/apimachinery/pkg/api/meta" 24 "k8s.io/apimachinery/pkg/runtime" 25 "k8s.io/apimachinery/pkg/runtime/serializer/json" 26 "k8s.io/client-go/kubernetes/scheme" 27 app "k8s.io/client-go/pkg/api/v1" 28 apps "k8s.io/client-go/pkg/apis/apps/v1beta1" 29 batch "k8s.io/client-go/pkg/apis/batch/v1" 30 batchv2 "k8s.io/client-go/pkg/apis/batch/v2alpha1" 31 extensions "k8s.io/client-go/pkg/apis/extensions/v1beta1" 32 ) 33 34 // AnnotationKey is annotation key of kubernetes object 35 type AnnotationKey string 36 37 const ( 38 // DefaultPathKey is the key of chart logic path. Logic path splits components by 39 // slash. For example: 40 // A chart like: 41 // rootchart --> subchart1 42 // |-> subchart2 43 // The logic path of every charts: 44 // rootchart: rootchart 45 // subchart1: rootchart/subchart1 46 // subchart2: rootchart/subchart2 47 // If a kubernetes resource object have an annotation key named "helm.sh/path", Then 48 // its value should meet the requirements. 49 DefaultPathKey AnnotationKey = "helm.sh/path" 50 // defaultNamespaceKey is the key of release namespace 51 DefaultNamespaceKey AnnotationKey = "helm.sh/namespace" 52 // defaultReleaseKey is the key of release name 53 DefaultReleaseKey AnnotationKey = "helm.sh/release" 54 ) 55 56 var ( 57 // defaultSerializer is a codec and used for encoding and decoding kubernetes resources 58 defaultSerializer *json.Serializer = nil 59 ) 60 61 func init() { 62 // create default serializer 63 defaultSerializer = json.NewYAMLSerializer(json.DefaultMetaFactory, scheme.Scheme, scheme.Scheme) 64 } 65 66 // InjectAnnotations adds key-value pairs to resource annotations. If resource is not a valid kubernetes 67 // resource, it does nothing and returns original resource. 68 func InjectAnnotations(resource string, annos map[AnnotationKey]string) string { 69 if len(annos) <= 0 { 70 return resource 71 } 72 73 // decode object 74 obj, _, err := defaultSerializer.Decode([]byte(resource), nil, nil) 75 if err != nil { 76 return resource 77 } 78 accessor := meta.NewAccessor() 79 annotations, err := accessor.Annotations(obj) 80 if err != nil { 81 return resource 82 } 83 err = accessor.SetAnnotations(obj, merge(annotations, annos)) 84 if err != nil { 85 return resource 86 } 87 88 // check and add annotations to the template of specific types 89 switch ins := obj.(type) { 90 case *extensions.Deployment: 91 { 92 ins.Spec.Template.Annotations = merge(ins.Spec.Template.Annotations, annos) 93 } 94 case *apps.Deployment: 95 { 96 ins.Spec.Template.Annotations = merge(ins.Spec.Template.Annotations, annos) 97 } 98 case *extensions.DaemonSet: 99 { 100 ins.Spec.Template.Annotations = merge(ins.Spec.Template.Annotations, annos) 101 } 102 case *extensions.ReplicaSet: 103 { 104 ins.Spec.Template.Annotations = merge(ins.Spec.Template.Annotations, annos) 105 } 106 case *apps.StatefulSet: 107 { 108 ins.Spec.Template.Annotations = merge(ins.Spec.Template.Annotations, annos) 109 } 110 case *batch.Job: 111 { 112 ins.Spec.Template.Annotations = merge(ins.Spec.Template.Annotations, annos) 113 } 114 case *batchv2.CronJob: 115 { 116 ins.Spec.JobTemplate.Annotations = merge(ins.Spec.JobTemplate.Annotations, annos) 117 ins.Spec.JobTemplate.Spec.Template.Annotations = merge(ins.Spec.JobTemplate.Spec.Template.Annotations, annos) 118 } 119 case *app.ReplicationController: 120 { 121 ins.Spec.Template.Annotations = merge(ins.Spec.Template.Annotations, annos) 122 } 123 } 124 125 // encode object 126 buf := bytes.NewBuffer(nil) 127 err = defaultSerializer.Encode(obj, buf) 128 if err != nil { 129 return resource 130 } 131 return buf.String() 132 } 133 134 // merge merges annotations into origin 135 func merge(origin map[string]string, annos map[AnnotationKey]string) map[string]string { 136 if origin == nil { 137 origin = make(map[string]string) 138 } 139 for k, v := range annos { 140 origin[string(k)] = v 141 } 142 return origin 143 } 144 145 // MatchReleaseByString matches two object string and check if they are from same release 146 func MatchReleaseByString(a, b string) bool { 147 // decode object 148 objA, _, err := defaultSerializer.Decode([]byte(a), nil, nil) 149 if err != nil { 150 log.Printf("match: Failed to decode resource: %s", err) 151 return false 152 } 153 objB, _, err := defaultSerializer.Decode([]byte(b), nil, nil) 154 if err != nil { 155 log.Printf("match: Failed to decode resource: %s", err) 156 return false 157 } 158 return MatchRelease(objA, objB) 159 } 160 161 // MatchRelease matches two object and check if they are from same release 162 func MatchRelease(a, b runtime.Object) bool { 163 accessor := meta.NewAccessor() 164 annoA, err := accessor.Annotations(a) 165 if err != nil { 166 annoA = map[string]string{} 167 } 168 annoB, err := accessor.Annotations(b) 169 if err != nil { 170 annoB = map[string]string{} 171 } 172 return matchReleaseAnnotations(annoA, annoB) 173 } 174 175 // matchReleaseAnnotations checks path, namespace and release 176 func matchReleaseAnnotations(a, b map[string]string) bool { 177 keys := []string{string(DefaultPathKey), string(DefaultNamespaceKey), string(DefaultReleaseKey)} 178 for _, key := range keys { 179 v1, ok1 := a[key] 180 v2, ok2 := b[key] 181 if !(ok1 || ok2) { 182 continue 183 } 184 if v1 != v2 { 185 return false 186 } 187 } 188 return true 189 }