github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/kubernetes/manifest/labels.go (about) 1 /* 2 Copyright 2019 The Skaffold 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 manifest 18 19 import ( 20 "context" 21 "strings" 22 23 apimachinery "k8s.io/apimachinery/pkg/runtime/schema" 24 25 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log" 26 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" 27 ) 28 29 type ResourceSelectorLabels struct { 30 allowlist map[apimachinery.GroupKind]latest.ResourceFilter 31 denylist map[apimachinery.GroupKind]latest.ResourceFilter 32 } 33 34 func NewResourceSelectorLabels(allowlist map[apimachinery.GroupKind]latest.ResourceFilter, denylist map[apimachinery.GroupKind]latest.ResourceFilter) *ResourceSelectorLabels { 35 return &ResourceSelectorLabels{ 36 allowlist: allowlist, 37 denylist: denylist, 38 } 39 } 40 41 func (rsi *ResourceSelectorLabels) allowByGroupKind(gk apimachinery.GroupKind) bool { 42 if _, allowed := rsi.allowlist[gk]; allowed { 43 // TODO(aaron-prindle) see if it makes sense to make this only use the allowlist... 44 if rf, disallowed := rsi.denylist[gk]; disallowed { 45 for _, s := range rf.Labels { 46 if s == ".*" { 47 return false 48 } 49 } 50 for _, s := range rf.Image { 51 if s == ".*" { 52 return false 53 } 54 } 55 } 56 return true 57 } 58 return false 59 } 60 61 func (rsi *ResourceSelectorLabels) allowByNavpath(gk apimachinery.GroupKind, navpath string, k string) (string, bool) { 62 for _, w := range ConfigConnectorResourceSelector { 63 if w.Matches(gk.Group, gk.Kind) { 64 if k != metadataField { 65 return "", false 66 } 67 return "labels", true 68 } 69 } 70 71 if rf, ok := rsi.denylist[gk]; ok { 72 for _, denypath := range rf.Labels { 73 if denypath == ".*" { 74 return "", false 75 } 76 // truncate the last part of the labels path and see if this matches 77 lastDot := strings.LastIndex(denypath, ".") 78 if lastDot == -1 { 79 // no dot exists in denypath, denypath is invalid 80 break 81 } 82 if navpath == denypath[:lastDot] { 83 return "", false 84 } 85 } 86 } 87 88 if rf, ok := rsi.allowlist[gk]; ok { 89 for _, allowpath := range rf.Labels { 90 if allowpath == ".*" { 91 if k != metadataField { 92 return "", false 93 } 94 return "labels", true 95 } 96 // truncate the last part of the labels path and see if this matches 97 lastDot := strings.LastIndex(allowpath, ".") 98 if lastDot == -1 { 99 // no dot exists in allowpath, allowpath is invalid 100 continue 101 } 102 if navpath == allowpath[:lastDot] { 103 return allowpath[lastDot+1:], true 104 } 105 } 106 } 107 return "", false 108 } 109 110 // SetLabels add labels to a list of Kubernetes manifests. 111 func (l *ManifestList) SetLabels(labels map[string]string, rs ResourceSelector) (ManifestList, error) { 112 if len(labels) == 0 { 113 return *l, nil 114 } 115 116 replacer := newLabelsSetter(labels) 117 updated, err := l.Visit(replacer, rs) 118 if err != nil { 119 return nil, labelSettingErr(err) 120 } 121 122 log.Entry(context.TODO()).Debugln("manifests with labels", updated.String()) 123 124 return updated, nil 125 } 126 127 type labelsSetter struct { 128 labels map[string]string 129 } 130 131 func newLabelsSetter(labels map[string]string) *labelsSetter { 132 return &labelsSetter{ 133 labels: labels, 134 } 135 } 136 137 func (r *labelsSetter) Visit(gk apimachinery.GroupKind, navpath string, o map[string]interface{}, k string, v interface{}, rs ResourceSelector) bool { 138 labelsField, ok := rs.allowByNavpath(gk, navpath, k) 139 if !ok { 140 return true 141 } 142 143 if len(r.labels) == 0 { 144 return false 145 } 146 metadata, ok := v.(map[string]interface{}) 147 if !ok { 148 return true 149 } 150 151 l, present := metadata[labelsField] 152 if !present { 153 metadata[labelsField] = r.labels 154 return false 155 } 156 labels, ok := l.(map[string]interface{}) 157 if !ok { 158 return true 159 } 160 for k, v := range r.labels { 161 // Don't overwrite existing labels 162 if _, present := labels[k]; !present { 163 labels[k] = v 164 } 165 } 166 return false 167 }