...

Source file src/sigs.k8s.io/kustomize/api/internal/accumulator/loadconfigfromcrds.go

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

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package accumulator
     5  
     6  import (
     7  	"encoding/json"
     8  	"strings"
     9  
    10  	"k8s.io/kube-openapi/pkg/validation/spec"
    11  	"sigs.k8s.io/kustomize/api/ifc"
    12  	"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
    13  	"sigs.k8s.io/kustomize/api/types"
    14  	"sigs.k8s.io/kustomize/kyaml/errors"
    15  	"sigs.k8s.io/kustomize/kyaml/filesys"
    16  	"sigs.k8s.io/kustomize/kyaml/resid"
    17  	"sigs.k8s.io/yaml"
    18  )
    19  
    20  // OpenAPIDefinition describes single type.
    21  // Normally these definitions are auto-generated using gen-openapi.
    22  // Same as in k8s.io / kube-openapi / pkg / common.
    23  type OpenAPIDefinition struct {
    24  	Schema       spec.Schema
    25  	Dependencies []string
    26  }
    27  
    28  type myProperties = map[string]spec.Schema
    29  type nameToApiMap map[string]OpenAPIDefinition
    30  
    31  // LoadConfigFromCRDs parse CRD schemas from paths into a TransformerConfig
    32  func LoadConfigFromCRDs(
    33  	ldr ifc.Loader, paths []string) (*builtinconfig.TransformerConfig, error) {
    34  	tc := builtinconfig.MakeEmptyConfig()
    35  	for _, path := range paths {
    36  		content, err := ldr.Load(path)
    37  		if err != nil {
    38  			return nil, err
    39  		}
    40  		m, err := makeNameToApiMap(content)
    41  		if err != nil {
    42  			return nil, errors.WrapPrefixf(err, "unable to parse open API definition from '%s'", path)
    43  		}
    44  		otherTc, err := makeConfigFromApiMap(m)
    45  		if err != nil {
    46  			return nil, err
    47  		}
    48  		tc, err = tc.Merge(otherTc)
    49  		if err != nil {
    50  			return nil, err
    51  		}
    52  	}
    53  	return tc, nil
    54  }
    55  
    56  func makeNameToApiMap(content []byte) (result nameToApiMap, err error) {
    57  	if content[0] == '{' {
    58  		err = json.Unmarshal(content, &result)
    59  	} else {
    60  		err = yaml.Unmarshal(content, &result)
    61  	}
    62  	return
    63  }
    64  
    65  func makeConfigFromApiMap(m nameToApiMap) (*builtinconfig.TransformerConfig, error) {
    66  	result := builtinconfig.MakeEmptyConfig()
    67  	for name, api := range m {
    68  		if !looksLikeAk8sType(api.Schema.SchemaProps.Properties) {
    69  			continue
    70  		}
    71  		tc := builtinconfig.MakeEmptyConfig()
    72  		err := loadCrdIntoConfig(
    73  			tc, makeGvkFromTypeName(name), m, name, []string{})
    74  		if err != nil {
    75  			return result, err
    76  		}
    77  		result, err = result.Merge(tc)
    78  		if err != nil {
    79  			return result, err
    80  		}
    81  	}
    82  	return result, nil
    83  }
    84  
    85  // TODO: Get Group and Version for CRD from the
    86  // openAPI definition once
    87  // "x-kubernetes-group-version-kind" is available in CRD
    88  func makeGvkFromTypeName(n string) resid.Gvk {
    89  	names := strings.Split(n, filesys.SelfDir)
    90  	kind := names[len(names)-1]
    91  	return resid.Gvk{Kind: kind}
    92  }
    93  
    94  func looksLikeAk8sType(properties myProperties) bool {
    95  	_, ok := properties["kind"]
    96  	if !ok {
    97  		return false
    98  	}
    99  	_, ok = properties["apiVersion"]
   100  	if !ok {
   101  		return false
   102  	}
   103  	_, ok = properties["metadata"]
   104  	return ok
   105  }
   106  
   107  const (
   108  	// "x-kubernetes-annotation": ""
   109  	xAnnotation = "x-kubernetes-annotation"
   110  
   111  	// "x-kubernetes-label-selector": ""
   112  	xLabelSelector = "x-kubernetes-label-selector"
   113  
   114  	// "x-kubernetes-identity": ""
   115  	xIdentity = "x-kubernetes-identity"
   116  
   117  	// "x-kubernetes-object-ref-api-version": <apiVersion name>
   118  	xVersion = "x-kubernetes-object-ref-api-version"
   119  
   120  	// "x-kubernetes-object-ref-kind": <kind name>
   121  	xKind = "x-kubernetes-object-ref-kind"
   122  
   123  	// "x-kubernetes-object-ref-name-key": "name"
   124  	// default is "name"
   125  	xNameKey = "x-kubernetes-object-ref-name-key"
   126  )
   127  
   128  // loadCrdIntoConfig loads a CRD spec into a TransformerConfig
   129  func loadCrdIntoConfig(
   130  	theConfig *builtinconfig.TransformerConfig, theGvk resid.Gvk, theMap nameToApiMap,
   131  	typeName string, path []string) (err error) {
   132  	api, ok := theMap[typeName]
   133  	if !ok {
   134  		return nil
   135  	}
   136  	for propName, property := range api.Schema.SchemaProps.Properties {
   137  		_, annotate := property.Extensions.GetString(xAnnotation)
   138  		if annotate {
   139  			err = theConfig.AddAnnotationFieldSpec(
   140  				makeFs(theGvk, append(path, propName)))
   141  			if err != nil {
   142  				return
   143  			}
   144  		}
   145  		_, label := property.Extensions.GetString(xLabelSelector)
   146  		if label {
   147  			err = theConfig.AddLabelFieldSpec(
   148  				makeFs(theGvk, append(path, propName)))
   149  			if err != nil {
   150  				return
   151  			}
   152  		}
   153  		_, identity := property.Extensions.GetString(xIdentity)
   154  		if identity {
   155  			err = theConfig.AddPrefixFieldSpec(
   156  				makeFs(theGvk, append(path, propName)))
   157  			if err != nil {
   158  				return
   159  			}
   160  		}
   161  		version, ok := property.Extensions.GetString(xVersion)
   162  		if ok {
   163  			kind, ok := property.Extensions.GetString(xKind)
   164  			if ok {
   165  				nameKey, ok := property.Extensions.GetString(xNameKey)
   166  				if !ok {
   167  					nameKey = "name"
   168  				}
   169  				err = theConfig.AddNamereferenceFieldSpec(
   170  					builtinconfig.NameBackReferences{
   171  						Gvk: resid.Gvk{Kind: kind, Version: version},
   172  						Referrers: []types.FieldSpec{
   173  							makeFs(theGvk, append(path, propName, nameKey))},
   174  					})
   175  				if err != nil {
   176  					return
   177  				}
   178  			}
   179  		}
   180  		if property.Ref.GetURL() != nil {
   181  			err = loadCrdIntoConfig(
   182  				theConfig, theGvk, theMap,
   183  				property.Ref.String(), append(path, propName))
   184  			if err != nil {
   185  				return
   186  			}
   187  		}
   188  	}
   189  	return nil
   190  }
   191  
   192  func makeFs(in resid.Gvk, path []string) types.FieldSpec {
   193  	return types.FieldSpec{
   194  		CreateIfNotPresent: false,
   195  		Gvk:                in,
   196  		Path:               strings.Join(path, "/"),
   197  	}
   198  }
   199  

View as plain text