...

Source file src/sigs.k8s.io/kustomize/api/internal/builtins/PatchTransformer.go

Documentation: sigs.k8s.io/kustomize/api/internal/builtins

     1  // Code generated by pluginator on PatchTransformer; DO NOT EDIT.
     2  // pluginator {(devel)  unknown   }
     3  
     4  package builtins
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	jsonpatch "gopkg.in/evanphx/json-patch.v4"
    11  	"sigs.k8s.io/kustomize/api/filters/patchjson6902"
    12  	"sigs.k8s.io/kustomize/api/resmap"
    13  	"sigs.k8s.io/kustomize/api/resource"
    14  	"sigs.k8s.io/kustomize/api/types"
    15  	"sigs.k8s.io/kustomize/kyaml/errors"
    16  	"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
    17  	"sigs.k8s.io/yaml"
    18  )
    19  
    20  type PatchTransformerPlugin struct {
    21  	smPatches   []*resource.Resource // strategic-merge patches
    22  	jsonPatches jsonpatch.Patch      // json6902 patch
    23  	// patchText is pure patch text created by Path or Patch
    24  	patchText string
    25  	// patchSource is patch source message
    26  	patchSource string
    27  	Path        string          `json:"path,omitempty"    yaml:"path,omitempty"`
    28  	Patch       string          `json:"patch,omitempty"   yaml:"patch,omitempty"`
    29  	Target      *types.Selector `json:"target,omitempty"  yaml:"target,omitempty"`
    30  	Options     map[string]bool `json:"options,omitempty" yaml:"options,omitempty"`
    31  }
    32  
    33  func (p *PatchTransformerPlugin) Config(h *resmap.PluginHelpers, c []byte) error {
    34  	if err := yaml.Unmarshal(c, p); err != nil {
    35  		return err
    36  	}
    37  
    38  	p.Patch = strings.TrimSpace(p.Patch)
    39  	switch {
    40  	case p.Patch == "" && p.Path == "":
    41  		return fmt.Errorf("must specify one of patch and path in\n%s", string(c))
    42  	case p.Patch != "" && p.Path != "":
    43  		return fmt.Errorf("patch and path can't be set at the same time\n%s", string(c))
    44  	case p.Patch != "":
    45  		p.patchText = p.Patch
    46  		p.patchSource = fmt.Sprintf("[patch: %q]", p.patchText)
    47  	case p.Path != "":
    48  		loaded, err := h.Loader().Load(p.Path)
    49  		if err != nil {
    50  			return fmt.Errorf("failed to get the patch file from path(%s): %w", p.Path, err)
    51  		}
    52  		p.patchText = string(loaded)
    53  		p.patchSource = fmt.Sprintf("[path: %q]", p.Path)
    54  	}
    55  
    56  	patchesSM, errSM := h.ResmapFactory().RF().SliceFromBytes([]byte(p.patchText))
    57  	patchesJson, errJson := jsonPatchFromBytes([]byte(p.patchText))
    58  
    59  	if (errSM == nil && errJson == nil) ||
    60  		(patchesSM != nil && patchesJson != nil) {
    61  		return fmt.Errorf(
    62  			"illegally qualifies as both an SM and JSON patch: %s",
    63  			p.patchSource)
    64  	}
    65  	if errSM != nil && errJson != nil {
    66  		return fmt.Errorf(
    67  			"unable to parse SM or JSON patch from %s", p.patchSource)
    68  	}
    69  	if errSM == nil {
    70  		p.smPatches = patchesSM
    71  		for _, loadedPatch := range p.smPatches {
    72  			if p.Options["allowNameChange"] {
    73  				loadedPatch.AllowNameChange()
    74  			}
    75  			if p.Options["allowKindChange"] {
    76  				loadedPatch.AllowKindChange()
    77  			}
    78  		}
    79  	} else {
    80  		p.jsonPatches = patchesJson
    81  	}
    82  	return nil
    83  }
    84  
    85  func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
    86  	if p.smPatches != nil {
    87  		return p.transformStrategicMerge(m)
    88  	}
    89  	return p.transformJson6902(m)
    90  }
    91  
    92  // transformStrategicMerge applies each loaded strategic merge patch
    93  // to the resource in the ResMap that matches the identifier of the patch.
    94  // If only one patch is specified, the Target can be used instead.
    95  func (p *PatchTransformerPlugin) transformStrategicMerge(m resmap.ResMap) error {
    96  	if p.Target != nil {
    97  		if len(p.smPatches) > 1 {
    98  			// detail: https://github.com/kubernetes-sigs/kustomize/issues/5049#issuecomment-1440604403
    99  			return fmt.Errorf("Multiple Strategic-Merge Patches in one `patches` entry is not allowed to set `patches.target` field: %s", p.patchSource)
   100  		}
   101  
   102  		// single patch
   103  		patch := p.smPatches[0]
   104  		selected, err := m.Select(*p.Target)
   105  		if err != nil {
   106  			return fmt.Errorf("unable to find patch target %q in `resources`: %w", p.Target, err)
   107  		}
   108  		return errors.Wrap(m.ApplySmPatch(resource.MakeIdSet(selected), patch))
   109  	}
   110  
   111  	for _, patch := range p.smPatches {
   112  		target, err := m.GetById(patch.OrgId())
   113  		if err != nil {
   114  			return fmt.Errorf("no resource matches strategic merge patch %q: %w", patch.OrgId(), err)
   115  		}
   116  		if err := target.ApplySmPatch(patch); err != nil {
   117  			return errors.Wrap(err)
   118  		}
   119  	}
   120  	return nil
   121  }
   122  
   123  // transformJson6902 applies json6902 Patch to all the resources in the ResMap that match Target.
   124  func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap) error {
   125  	if p.Target == nil {
   126  		return fmt.Errorf("must specify a target for JSON patch %s", p.patchSource)
   127  	}
   128  	resources, err := m.Select(*p.Target)
   129  	if err != nil {
   130  		return err
   131  	}
   132  	for _, res := range resources {
   133  		res.StorePreviousId()
   134  		internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode)
   135  		err = res.ApplyFilter(patchjson6902.Filter{
   136  			Patch: p.patchText,
   137  		})
   138  		if err != nil {
   139  			return err
   140  		}
   141  
   142  		annotations := res.GetAnnotations()
   143  		for key, value := range internalAnnotations {
   144  			annotations[key] = value
   145  		}
   146  		err = res.SetAnnotations(annotations)
   147  	}
   148  	return nil
   149  }
   150  
   151  // jsonPatchFromBytes loads a Json 6902 patch from a bytes input
   152  func jsonPatchFromBytes(in []byte) (jsonpatch.Patch, error) {
   153  	ops := string(in)
   154  	if ops == "" {
   155  		return nil, fmt.Errorf("empty json patch operations")
   156  	}
   157  
   158  	if ops[0] != '[' {
   159  		// TODO(5049):
   160  		//   In the case of multiple yaml documents, return error instead of ignoring all but first.
   161  		//   Details: https://github.com/kubernetes-sigs/kustomize/pull/5194#discussion_r1256686728
   162  		jsonOps, err := yaml.YAMLToJSON(in)
   163  		if err != nil {
   164  			return nil, err
   165  		}
   166  		ops = string(jsonOps)
   167  	}
   168  	return jsonpatch.DecodePatch([]byte(ops))
   169  }
   170  
   171  func NewPatchTransformerPlugin() resmap.TransformerPlugin {
   172  	return &PatchTransformerPlugin{}
   173  }
   174  

View as plain text