github.com/replicatedhq/ship@v0.55.0/pkg/lifecycle/kustomize/patch.go (about) 1 package kustomize 2 3 import ( 4 "encoding/json" 5 "os" 6 "path" 7 "path/filepath" 8 "strings" 9 10 "github.com/go-kit/kit/log" 11 "github.com/go-kit/kit/log/level" 12 "github.com/pkg/errors" 13 yaml "gopkg.in/yaml.v3" 14 "k8s.io/client-go/kubernetes/scheme" 15 kustomizepatch "sigs.k8s.io/kustomize/pkg/patch" 16 k8stypes "sigs.k8s.io/kustomize/pkg/types" 17 18 "github.com/replicatedhq/ship/pkg/api" 19 "github.com/replicatedhq/ship/pkg/constants" 20 "github.com/replicatedhq/ship/pkg/util" 21 ) 22 23 type k8sMetadataLabelsOnly struct { 24 Metadata struct { 25 Labels map[string]interface{} `yaml:"labels"` 26 } `yaml:"metadata"` 27 } 28 29 type patchOperation struct { 30 Op string `json:"op"` 31 Path string `json:"path"` 32 Value string `json:"value,omitempty"` 33 writePath string 34 } 35 36 var ( 37 removeHeritagePatch = patchOperation{ 38 Op: "remove", 39 Path: "/metadata/labels/heritage", 40 writePath: "heritage-patch.json", 41 } 42 removeChartPatch = patchOperation{ 43 Op: "remove", 44 Path: "/metadata/labels/chart", 45 writePath: "chart-patch.json", 46 } 47 ) 48 49 // generateTillerPatches writes a kustomization.yaml including JSON6902 patches to remove 50 // the chart and heritage metadata labels. 51 func (l *Kustomizer) generateTillerPatches(step api.Kustomize) error { 52 debug := level.Debug(log.With(l.Logger, "struct", "kustomizer", "handler", "generateTillerPatches")) 53 54 debug.Log("event", "mkdir.DefaultOverlaysPath") 55 if err := l.FS.MkdirAll(constants.DefaultOverlaysPath, 0755); err != nil { 56 return errors.Wrapf(err, "create default overlays path at %s", constants.DefaultOverlaysPath) 57 } 58 59 defaultPatches := []patchOperation{removeChartPatch, removeHeritagePatch} 60 for idx, defaultPatch := range defaultPatches { 61 defaultPatchAsSlice := []patchOperation{defaultPatch} 62 63 patchesB, err := json.Marshal(defaultPatchAsSlice) 64 if err != nil { 65 return errors.Wrapf(err, "marshal default patch idx %d", idx) 66 } 67 68 if err := l.FS.WriteFile(path.Join(constants.DefaultOverlaysPath, defaultPatch.writePath), patchesB, 0755); err != nil { 69 return errors.Wrapf(err, "write default patch idx %d", idx) 70 } 71 } 72 73 relativePathToBases, err := filepath.Rel(constants.DefaultOverlaysPath, step.Base) 74 if err != nil { 75 return errors.Wrap(err, "relative path to bases") 76 } 77 78 var excludedBases []string 79 state, err := l.State.CachedState() 80 if err != nil { 81 return errors.Wrap(err, "load state") 82 } 83 if state.V1 != nil && state.CurrentKustomize() != nil { 84 excludedBases = state.CurrentKustomize().Ship().ExcludedBases 85 } 86 87 json6902Patches := []kustomizepatch.Json6902{} 88 if err := l.FS.Walk( 89 step.Base, 90 func(targetPath string, info os.FileInfo, err error) error { 91 if err != nil { 92 debug.Log("event", "walk.fail", "path", targetPath) 93 return errors.Wrap(err, "walk path") 94 } 95 96 // this ignores non-k8s resources and things included in the list of excluded bases 97 if !l.shouldAddFileToBase(step.Base, excludedBases, targetPath) { 98 return nil 99 } 100 101 fileB, err := l.FS.ReadFile(targetPath) 102 if err != nil { 103 return errors.Wrapf(err, "read file %s", targetPath) 104 } 105 106 resource, err := util.NewKubernetesResource(fileB) 107 if err != nil { 108 // Ignore all non-k8s resources 109 return nil 110 } 111 112 if _, err := scheme.Scheme.New(util.ToGroupVersionKind(resource.Id().Gvk())); err != nil { 113 // Ignore all non-k8s resources 114 return nil 115 } 116 117 fileMetadataOnly := k8sMetadataLabelsOnly{} 118 if err := yaml.Unmarshal(fileB, &fileMetadataOnly); err != nil { 119 return errors.Wrap(err, "unmarshal k8s metadata only") 120 } 121 122 for _, excluded := range excludedBases { 123 if info.Name() == excluded { 124 // don't add this to defaultPatches 125 debug.Log("skipping default patches", info.Name()) 126 return nil 127 } 128 } 129 130 for _, defaultPatch := range defaultPatches { 131 splitDefaultPath := strings.Split(defaultPatch.Path, "/") 132 patchLabel := splitDefaultPath[len(splitDefaultPath)-1] 133 134 if l.hasMetadataLabel(patchLabel, fileMetadataOnly) { 135 json6902Patches = append(json6902Patches, kustomizepatch.Json6902{ 136 Target: &kustomizepatch.Target{ 137 Gvk: resource.GetGvk(), 138 Namespace: resource.Id().Namespace(), 139 Name: resource.GetName(), 140 }, 141 Path: defaultPatch.writePath, 142 }) 143 } 144 } 145 146 return nil 147 }, 148 ); err != nil { 149 return err 150 } 151 152 kustomizationYaml := k8stypes.Kustomization{ 153 Bases: []string{relativePathToBases}, 154 PatchesJson6902: json6902Patches, 155 } 156 157 kustomizationYamlB, err := util.MarshalIndent(2, kustomizationYaml) 158 if err != nil { 159 return errors.Wrap(err, "marshal kustomization yaml") 160 } 161 162 debug.Log("event", "writeFile.kustomization") 163 if err := l.FS.WriteFile(path.Join(constants.DefaultOverlaysPath, "kustomization.yaml"), kustomizationYamlB, 0755); err != nil { 164 return errors.Wrap(err, "write temp kustomization") 165 } 166 167 return nil 168 } 169 170 func (l *Kustomizer) hasMetadataLabel(label string, k8sFile k8sMetadataLabelsOnly) bool { 171 if k8sFile.Metadata.Labels == nil { 172 return false 173 } 174 175 return k8sFile.Metadata.Labels[label] != nil 176 }