// Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 //go:generate pluginator package main import ( "fmt" "github.com/evanphx/json-patch" "github.com/pkg/errors" "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/yaml" ) type plugin struct { loadedPatch *resource.Resource decodedPatch jsonpatch.Patch Path string `json:"path,omitempty" yaml:"path,omitempty"` Patch string `json:"patch,omitempty" yaml:"patch,omitempty"` Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"` } //noinspection GoUnusedGlobalVariable var KustomizePlugin plugin func (p *plugin) Config( h *resmap.PluginHelpers, c []byte) (err error) { err = yaml.Unmarshal(c, p) if err != nil { return err } if p.Patch == "" && p.Path == "" { err = fmt.Errorf( "must specify one of patch and path in\n%s", string(c)) return } if p.Patch != "" && p.Path != "" { err = fmt.Errorf( "patch and path can't be set at the same time\n%s", string(c)) return } var in []byte if p.Path != "" { in, err = h.Loader().Load(p.Path) if err != nil { return } } if p.Patch != "" { in = []byte(p.Patch) } patchSM, errSM := h.ResmapFactory().RF().FromBytes(in) patchJson, errJson := jsonPatchFromBytes(in) if errSM != nil && errJson != nil { err = fmt.Errorf( "unable to get either a Strategic Merge Patch or JSON patch 6902 from %s", p.Patch) return } if errSM == nil && errJson != nil { p.loadedPatch = patchSM } if errJson == nil && errSM != nil { p.decodedPatch = patchJson } if patchSM != nil && patchJson != nil { err = fmt.Errorf( "a patch can't be both a Strategic Merge Patch and JSON patch 6902 %s", p.Patch) } return nil } func (p *plugin) Transform(m resmap.ResMap) error { if p.loadedPatch != nil && p.Target == nil { target, err := m.GetById(p.loadedPatch.OrgId()) if err != nil { return err } err = target.Patch(p.loadedPatch.Kunstructured) if err != nil { return err } return nil } if p.Target == nil { return fmt.Errorf("must specify a target for patch %s", p.Patch) } resources, err := m.Select(*p.Target) if err != nil { return err } for _, res := range resources { if p.decodedPatch != nil { rawObj, err := res.MarshalJSON() if err != nil { return err } modifiedObj, err := p.decodedPatch.Apply(rawObj) if err != nil { return errors.Wrapf( err, "failed to apply json patch '%s'", p.Patch) } err = res.UnmarshalJSON(modifiedObj) if err != nil { return err } } if p.loadedPatch != nil { patchCopy := p.loadedPatch.DeepCopy() patchCopy.SetName(res.GetName()) patchCopy.SetNamespace(res.GetNamespace()) patchCopy.SetGvk(res.GetGvk()) err = res.Patch(patchCopy.Kunstructured) if err != nil { return err } } } return nil } // jsonPatchFromBytes loads a Json 6902 patch from // a bytes input func jsonPatchFromBytes( in []byte) (jsonpatch.Patch, error) { ops := string(in) if ops == "" { return nil, fmt.Errorf("empty json patch operations") } if ops[0] != '[' { jsonOps, err := yaml.YAMLToJSON(in) if err != nil { return nil, err } ops = string(jsonOps) } return jsonpatch.DecodePatch([]byte(ops)) }