...

Source file src/sigs.k8s.io/kustomize/api/resource/factory.go

Documentation: sigs.k8s.io/kustomize/api/resource

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package resource
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"log"
    10  	"strings"
    11  
    12  	"sigs.k8s.io/kustomize/api/ifc"
    13  	"sigs.k8s.io/kustomize/api/internal/generators"
    14  	"sigs.k8s.io/kustomize/api/internal/kusterr"
    15  	"sigs.k8s.io/kustomize/api/konfig"
    16  	"sigs.k8s.io/kustomize/api/types"
    17  	"sigs.k8s.io/kustomize/kyaml/kio"
    18  	"sigs.k8s.io/kustomize/kyaml/resid"
    19  	"sigs.k8s.io/kustomize/kyaml/yaml"
    20  )
    21  
    22  // Factory makes instances of Resource.
    23  type Factory struct {
    24  	hasher ifc.KustHasher
    25  
    26  	// When set to true, IncludeLocalConfigs indicates
    27  	// that Factory should include resources with the
    28  	// annotation 'config.kubernetes.io/local-config'.
    29  	// By default these resources are ignored.
    30  	IncludeLocalConfigs bool
    31  }
    32  
    33  // NewFactory makes an instance of Factory.
    34  func NewFactory(h ifc.KustHasher) *Factory {
    35  	return &Factory{hasher: h}
    36  }
    37  
    38  // Hasher returns an ifc.KustHasher
    39  func (rf *Factory) Hasher() ifc.KustHasher {
    40  	return rf.hasher
    41  }
    42  
    43  // FromMap returns a new instance of Resource.
    44  func (rf *Factory) FromMap(m map[string]interface{}) *Resource {
    45  	res, err := rf.FromMapAndOption(m, nil)
    46  	if err != nil {
    47  		// TODO: return err instead of log.
    48  		log.Fatalf("failed to create resource from map: %v", err)
    49  	}
    50  	return res
    51  }
    52  
    53  // FromMapWithName returns a new instance with the given "original" name.
    54  func (rf *Factory) FromMapWithName(n string, m map[string]interface{}) *Resource {
    55  	return rf.FromMapWithNamespaceAndName(resid.DefaultNamespace, n, m)
    56  }
    57  
    58  // FromMapWithNamespaceAndName returns a new instance with the given "original" namespace.
    59  func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string]interface{}) *Resource {
    60  	r, err := rf.FromMapAndOption(m, nil)
    61  	if err != nil {
    62  		// TODO: return err instead of log.
    63  		log.Fatalf("failed to create resource from map: %v", err)
    64  	}
    65  	return r.setPreviousId(ns, n, r.GetKind())
    66  }
    67  
    68  // FromMapAndOption returns a new instance of Resource with given options.
    69  func (rf *Factory) FromMapAndOption(
    70  	m map[string]interface{}, args *types.GeneratorArgs) (*Resource, error) {
    71  	n, err := yaml.FromMap(m)
    72  	if err != nil {
    73  		return nil, fmt.Errorf("failed to convert map to YAML node: %w", err)
    74  	}
    75  	return rf.makeOne(n, args), nil
    76  }
    77  
    78  // makeOne returns a new instance of Resource.
    79  func (rf *Factory) makeOne(rn *yaml.RNode, o *types.GeneratorArgs) *Resource {
    80  	if rn == nil {
    81  		log.Fatal("RNode must not be null")
    82  	}
    83  	resource := &Resource{RNode: *rn}
    84  	if o != nil {
    85  		if o.Options == nil || !o.Options.DisableNameSuffixHash {
    86  			resource.EnableHashSuffix()
    87  		}
    88  		resource.SetBehavior(types.NewGenerationBehavior(o.Behavior))
    89  	}
    90  
    91  	return resource
    92  }
    93  
    94  // SliceFromPatches returns a slice of resources given a patch path
    95  // slice from a kustomization file.
    96  func (rf *Factory) SliceFromPatches(
    97  	ldr ifc.Loader, paths []types.PatchStrategicMerge) ([]*Resource, error) {
    98  	var result []*Resource
    99  	for _, path := range paths {
   100  		content, err := ldr.Load(string(path))
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  		res, err := rf.SliceFromBytes(content)
   105  		if err != nil {
   106  			return nil, kusterr.Handler(err, string(path))
   107  		}
   108  		result = append(result, res...)
   109  	}
   110  	return result, nil
   111  }
   112  
   113  // FromBytes unmarshalls bytes into one Resource.
   114  func (rf *Factory) FromBytes(in []byte) (*Resource, error) {
   115  	result, err := rf.SliceFromBytes(in)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	if len(result) != 1 {
   120  		return nil, fmt.Errorf(
   121  			"expected 1 resource, found %d in %v", len(result), in)
   122  	}
   123  	return result[0], nil
   124  }
   125  
   126  // SliceFromBytes unmarshals bytes into a Resource slice.
   127  func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
   128  	nodes, err := rf.RNodesFromBytes(in)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	return rf.resourcesFromRNodes(nodes), nil
   133  }
   134  
   135  // DropLocalNodes removes the local nodes by default. Local nodes are detected via the annotation `config.kubernetes.io/local-config: "true"`
   136  func (rf *Factory) DropLocalNodes(nodes []*yaml.RNode) ([]*Resource, error) {
   137  	var result []*yaml.RNode
   138  	for _, node := range nodes {
   139  		if node.IsNilOrEmpty() {
   140  			continue
   141  		}
   142  		md, err := node.GetValidatedMetadata()
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  
   147  		if rf.IncludeLocalConfigs {
   148  			result = append(result, node)
   149  			continue
   150  		}
   151  		localConfig, exist := md.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
   152  		if !exist || localConfig == "false" {
   153  			result = append(result, node)
   154  		}
   155  	}
   156  	return rf.resourcesFromRNodes(result), nil
   157  }
   158  
   159  // ResourcesFromRNodes converts RNodes to Resources.
   160  func (rf *Factory) ResourcesFromRNodes(
   161  	nodes []*yaml.RNode) (result []*Resource, err error) {
   162  	return rf.DropLocalNodes(nodes)
   163  }
   164  
   165  // resourcesFromRNode assumes all nodes are good.
   166  func (rf *Factory) resourcesFromRNodes(
   167  	nodes []*yaml.RNode) (result []*Resource) {
   168  	for _, n := range nodes {
   169  		result = append(result, rf.makeOne(n, nil))
   170  	}
   171  	return
   172  }
   173  
   174  func (rf *Factory) RNodesFromBytes(b []byte) ([]*yaml.RNode, error) {
   175  	nodes, err := kio.FromBytes(b)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  	nodes, err = rf.dropBadNodes(nodes)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  	return rf.inlineAnyEmbeddedLists(nodes)
   184  }
   185  
   186  // inlineAnyEmbeddedLists scans the RNode slice for nodes named FooList.
   187  // Such nodes are expected to be lists of resources, each of type Foo.
   188  // These lists are replaced in the result by their inlined resources.
   189  func (rf *Factory) inlineAnyEmbeddedLists(
   190  	nodes []*yaml.RNode) (result []*yaml.RNode, err error) {
   191  	var n0 *yaml.RNode
   192  	for len(nodes) > 0 {
   193  		n0, nodes = nodes[0], nodes[1:]
   194  		kind := n0.GetKind()
   195  		if !strings.HasSuffix(kind, "List") {
   196  			result = append(result, n0)
   197  			continue
   198  		}
   199  		// Convert a FooList into a slice of Foo.
   200  		var m map[string]interface{}
   201  		m, err = n0.Map()
   202  		if err != nil {
   203  			return nil, fmt.Errorf("trouble expanding list of %s; %w", kind, err)
   204  		}
   205  		items, ok := m["items"]
   206  		if !ok {
   207  			// treat as an empty list
   208  			continue
   209  		}
   210  		slice, ok := items.([]interface{})
   211  		if !ok {
   212  			if items == nil {
   213  				// an empty list
   214  				continue
   215  			}
   216  			return nil, fmt.Errorf(
   217  				"expected array in %s/items, but found %T", kind, items)
   218  		}
   219  		innerNodes, err := rf.convertObjectSliceToNodeSlice(slice)
   220  		if err != nil {
   221  			return nil, err
   222  		}
   223  		nodes = append(nodes, innerNodes...)
   224  	}
   225  	return result, nil
   226  }
   227  
   228  // convertObjectSlice converts a list of objects to a list of RNode.
   229  func (rf *Factory) convertObjectSliceToNodeSlice(
   230  	objects []interface{}) (result []*yaml.RNode, err error) {
   231  	var bytes []byte
   232  	var nodes []*yaml.RNode
   233  	for _, obj := range objects {
   234  		bytes, err = json.Marshal(obj)
   235  		if err != nil {
   236  			return
   237  		}
   238  		nodes, err = kio.FromBytes(bytes)
   239  		if err != nil {
   240  			return
   241  		}
   242  		nodes, err = rf.dropBadNodes(nodes)
   243  		if err != nil {
   244  			return
   245  		}
   246  		result = append(result, nodes...)
   247  	}
   248  	return
   249  }
   250  
   251  // dropBadNodes may drop some nodes from its input argument.
   252  func (rf *Factory) dropBadNodes(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
   253  	var result []*yaml.RNode
   254  	for _, n := range nodes {
   255  		if n.IsNilOrEmpty() {
   256  			continue
   257  		}
   258  		if _, err := n.GetValidatedMetadata(); err != nil {
   259  			return nil, err
   260  		}
   261  		if foundNil, path := n.HasNilEntryInList(); foundNil {
   262  			return nil, fmt.Errorf("empty item at %v in object %v", path, n)
   263  		}
   264  		result = append(result, n)
   265  	}
   266  	return result, nil
   267  }
   268  
   269  // SliceFromBytesWithNames unmarshals bytes into a Resource slice with specified original
   270  // name.
   271  func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resource, error) {
   272  	result, err := rf.SliceFromBytes(in)
   273  	if err != nil {
   274  		return nil, err
   275  	}
   276  	if len(names) != len(result) {
   277  		return nil, fmt.Errorf("number of names doesn't match number of resources")
   278  	}
   279  	for i, res := range result {
   280  		res.setPreviousId(resid.DefaultNamespace, names[i], res.GetKind())
   281  	}
   282  	return result, nil
   283  }
   284  
   285  // MakeConfigMap makes an instance of Resource for ConfigMap
   286  func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (*Resource, error) {
   287  	rn, err := generators.MakeConfigMap(kvLdr, args)
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  	return rf.makeOne(rn, &args.GeneratorArgs), nil
   292  }
   293  
   294  // MakeSecret makes an instance of Resource for Secret
   295  func (rf *Factory) MakeSecret(kvLdr ifc.KvLoader, args *types.SecretArgs) (*Resource, error) {
   296  	rn, err := generators.MakeSecret(kvLdr, args)
   297  	if err != nil {
   298  		return nil, err
   299  	}
   300  	return rf.makeOne(rn, &args.GeneratorArgs), nil
   301  }
   302  

View as plain text