github.com/TheSpiritXIII/controller-tools@v0.14.1/pkg/crd/markers/crd.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package markers
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    24  
    25  	"github.com/TheSpiritXIII/controller-tools/pkg/markers"
    26  )
    27  
    28  // CRDMarkers lists all markers that directly modify the CRD (not validation
    29  // schemas).
    30  var CRDMarkers = []*definitionWithHelp{
    31  	// TODO(directxman12): more detailed help
    32  	must(markers.MakeDefinition("kubebuilder:subresource:status", markers.DescribesType, SubresourceStatus{})).
    33  		WithHelp(SubresourceStatus{}.Help()),
    34  
    35  	must(markers.MakeDefinition("kubebuilder:subresource:scale", markers.DescribesType, SubresourceScale{})).
    36  		WithHelp(SubresourceScale{}.Help()),
    37  
    38  	must(markers.MakeDefinition("kubebuilder:printcolumn", markers.DescribesType, PrintColumn{})).
    39  		WithHelp(PrintColumn{}.Help()),
    40  
    41  	must(markers.MakeDefinition("kubebuilder:resource", markers.DescribesType, Resource{})).
    42  		WithHelp(Resource{}.Help()),
    43  
    44  	must(markers.MakeDefinition("kubebuilder:storageversion", markers.DescribesType, StorageVersion{})).
    45  		WithHelp(StorageVersion{}.Help()),
    46  
    47  	must(markers.MakeDefinition("kubebuilder:skipversion", markers.DescribesType, SkipVersion{})).
    48  		WithHelp(SkipVersion{}.Help()),
    49  
    50  	must(markers.MakeDefinition("kubebuilder:unservedversion", markers.DescribesType, UnservedVersion{})).
    51  		WithHelp(UnservedVersion{}.Help()),
    52  
    53  	must(markers.MakeDefinition("kubebuilder:deprecatedversion", markers.DescribesType, DeprecatedVersion{})).
    54  		WithHelp(DeprecatedVersion{}.Help()),
    55  
    56  	must(markers.MakeDefinition("kubebuilder:metadata", markers.DescribesType, Metadata{})).
    57  		WithHelp(Metadata{}.Help()),
    58  
    59  	must(markers.MakeDefinition("kubebuilder:field:scope", markers.DescribesField, FieldScope(""))).
    60  		WithHelp(FieldScope("").Help()),
    61  }
    62  
    63  // TODO: categories and singular used to be annotations types
    64  // TODO: doc
    65  
    66  func init() {
    67  	AllDefinitions = append(AllDefinitions, CRDMarkers...)
    68  }
    69  
    70  // +controllertools:marker:generateHelp:category=CRD
    71  
    72  // SubresourceStatus enables the "/status" subresource on a CRD.
    73  type SubresourceStatus struct{}
    74  
    75  func (s SubresourceStatus) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
    76  	var subresources *apiext.CustomResourceSubresources
    77  	for i := range crd.Versions {
    78  		ver := &crd.Versions[i]
    79  		if ver.Name != version {
    80  			continue
    81  		}
    82  		if ver.Subresources == nil {
    83  			ver.Subresources = &apiext.CustomResourceSubresources{}
    84  		}
    85  		subresources = ver.Subresources
    86  		break
    87  	}
    88  	if subresources == nil {
    89  		return fmt.Errorf("status subresource applied to version %q not in CRD", version)
    90  	}
    91  	subresources.Status = &apiext.CustomResourceSubresourceStatus{}
    92  	return nil
    93  }
    94  
    95  // +controllertools:marker:generateHelp:category=CRD
    96  
    97  // SubresourceScale enables the "/scale" subresource on a CRD.
    98  type SubresourceScale struct {
    99  	// marker names are leftover legacy cruft
   100  
   101  	// SpecPath specifies the jsonpath to the replicas field for the scale's spec.
   102  	SpecPath string `marker:"specpath"`
   103  
   104  	// StatusPath specifies the jsonpath to the replicas field for the scale's status.
   105  	StatusPath string `marker:"statuspath"`
   106  
   107  	// SelectorPath specifies the jsonpath to the pod label selector field for the scale's status.
   108  	//
   109  	// The selector field must be the *string* form (serialized form) of a selector.
   110  	// Setting a pod label selector is necessary for your type to work with the HorizontalPodAutoscaler.
   111  	SelectorPath *string `marker:"selectorpath"`
   112  }
   113  
   114  func (s SubresourceScale) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   115  	var subresources *apiext.CustomResourceSubresources
   116  	for i := range crd.Versions {
   117  		ver := &crd.Versions[i]
   118  		if ver.Name != version {
   119  			continue
   120  		}
   121  		if ver.Subresources == nil {
   122  			ver.Subresources = &apiext.CustomResourceSubresources{}
   123  		}
   124  		subresources = ver.Subresources
   125  		break
   126  	}
   127  	if subresources == nil {
   128  		return fmt.Errorf("scale subresource applied to version %q not in CRD", version)
   129  	}
   130  	subresources.Scale = &apiext.CustomResourceSubresourceScale{
   131  		SpecReplicasPath:   s.SpecPath,
   132  		StatusReplicasPath: s.StatusPath,
   133  		LabelSelectorPath:  s.SelectorPath,
   134  	}
   135  	return nil
   136  }
   137  
   138  // +controllertools:marker:generateHelp:category=CRD
   139  
   140  // StorageVersion marks this version as the "storage version" for the CRD for conversion.
   141  //
   142  // When conversion is enabled for a CRD (i.e. it's not a trivial-versions/single-version CRD),
   143  // one version is set as the "storage version" to be stored in etcd.  Attempting to store any
   144  // other version will result in conversion to the storage version via a conversion webhook.
   145  type StorageVersion struct{}
   146  
   147  func (s StorageVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   148  	if version == "" {
   149  		// single-version, do nothing
   150  		return nil
   151  	}
   152  	// multi-version
   153  	for i := range crd.Versions {
   154  		ver := &crd.Versions[i]
   155  		if ver.Name != version {
   156  			continue
   157  		}
   158  		ver.Storage = true
   159  		break
   160  	}
   161  	return nil
   162  }
   163  
   164  // +controllertools:marker:generateHelp:category=CRD
   165  
   166  // SkipVersion removes the particular version of the CRD from the CRDs spec.
   167  //
   168  // This is useful if you need to skip generating and listing version entries
   169  // for 'internal' resource versions, which typically exist if using the
   170  // Kubernetes upstream conversion-gen tool.
   171  type SkipVersion struct{}
   172  
   173  func (s SkipVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   174  	if version == "" {
   175  		// single-version, this is an invalid state
   176  		return fmt.Errorf("cannot skip a version if there is only a single version")
   177  	}
   178  	var versions []apiext.CustomResourceDefinitionVersion
   179  	// multi-version
   180  	for i := range crd.Versions {
   181  		ver := crd.Versions[i]
   182  		if ver.Name == version {
   183  			// skip the skipped version
   184  			continue
   185  		}
   186  		versions = append(versions, ver)
   187  	}
   188  	crd.Versions = versions
   189  	return nil
   190  }
   191  
   192  // +controllertools:marker:generateHelp:category=CRD
   193  
   194  // PrintColumn adds a column to "kubectl get" output for this CRD.
   195  type PrintColumn struct {
   196  	// Name specifies the name of the column.
   197  	Name string
   198  
   199  	// Type indicates the type of the column.
   200  	//
   201  	// It may be any OpenAPI data type listed at
   202  	// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types.
   203  	Type string
   204  
   205  	// JSONPath specifies the jsonpath expression used to extract the value of the column.
   206  	JSONPath string `marker:"JSONPath"` // legacy cruft
   207  
   208  	// Description specifies the help/description for this column.
   209  	Description string `marker:",optional"`
   210  
   211  	// Format specifies the format of the column.
   212  	//
   213  	// It may be any OpenAPI data format corresponding to the type, listed at
   214  	// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types.
   215  	Format string `marker:",optional"`
   216  
   217  	// Priority indicates how important it is that this column be displayed.
   218  	//
   219  	// Lower priority (*higher* numbered) columns will be hidden if the terminal
   220  	// width is too small.
   221  	Priority int32 `marker:",optional"`
   222  }
   223  
   224  func (s PrintColumn) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   225  	var columns *[]apiext.CustomResourceColumnDefinition
   226  	for i := range crd.Versions {
   227  		ver := &crd.Versions[i]
   228  		if ver.Name != version {
   229  			continue
   230  		}
   231  		if ver.Subresources == nil {
   232  			ver.Subresources = &apiext.CustomResourceSubresources{}
   233  		}
   234  		columns = &ver.AdditionalPrinterColumns
   235  		break
   236  	}
   237  	if columns == nil {
   238  		return fmt.Errorf("printer columns applied to version %q not in CRD", version)
   239  	}
   240  
   241  	*columns = append(*columns, apiext.CustomResourceColumnDefinition{
   242  		Name:        s.Name,
   243  		Type:        s.Type,
   244  		JSONPath:    s.JSONPath,
   245  		Description: s.Description,
   246  		Format:      s.Format,
   247  		Priority:    s.Priority,
   248  	})
   249  
   250  	return nil
   251  }
   252  
   253  // +controllertools:marker:generateHelp:category=CRD
   254  
   255  // Resource configures naming and scope for a CRD.
   256  type Resource struct {
   257  	// Path specifies the plural "resource" for this CRD.
   258  	//
   259  	// It generally corresponds to a plural, lower-cased version of the Kind.
   260  	// See https://book.kubebuilder.io/cronjob-tutorial/gvks.html.
   261  	Path string `marker:",optional"`
   262  
   263  	// ShortName specifies aliases for this CRD.
   264  	//
   265  	// Short names are often used when people have work with your resource
   266  	// over and over again.  For instance, "rs" for "replicaset" or
   267  	// "crd" for customresourcedefinition.
   268  	ShortName []string `marker:",optional"`
   269  
   270  	// Categories specifies which group aliases this resource is part of.
   271  	//
   272  	// Group aliases are used to work with groups of resources at once.
   273  	// The most common one is "all" which covers about a third of the base
   274  	// resources in Kubernetes, and is generally used for "user-facing" resources.
   275  	Categories []string `marker:",optional"`
   276  
   277  	// Singular overrides the singular form of your resource.
   278  	//
   279  	// The singular form is otherwise defaulted off the plural (path).
   280  	Singular string `marker:",optional"`
   281  
   282  	// Scope overrides the scope of the CRD (Cluster vs Namespaced).
   283  	//
   284  	// Scope defaults to "Namespaced".  Cluster-scoped ("Cluster") resources
   285  	// don't exist in namespaces.
   286  	Scope string `marker:",optional"`
   287  }
   288  
   289  func (s Resource) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, _ string) error {
   290  	if s.Path != "" {
   291  		crd.Names.Plural = s.Path
   292  	}
   293  	if s.Singular != "" {
   294  		crd.Names.Singular = s.Singular
   295  	}
   296  	crd.Names.ShortNames = s.ShortName
   297  	crd.Names.Categories = s.Categories
   298  
   299  	switch s.Scope {
   300  	case "":
   301  		crd.Scope = apiext.NamespaceScoped
   302  	default:
   303  		crd.Scope = apiext.ResourceScope(s.Scope)
   304  	}
   305  
   306  	return nil
   307  }
   308  
   309  // +controllertools:marker:generateHelp:category=CRD
   310  
   311  // UnservedVersion does not serve this version.
   312  //
   313  // This is useful if you need to drop support for a version in favor of a newer version.
   314  type UnservedVersion struct{}
   315  
   316  func (s UnservedVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   317  	for i := range crd.Versions {
   318  		ver := &crd.Versions[i]
   319  		if ver.Name != version {
   320  			continue
   321  		}
   322  		ver.Served = false
   323  		break
   324  	}
   325  	return nil
   326  }
   327  
   328  // NB(directxman12): singular was historically distinct, so we keep it here for backwards compat
   329  
   330  // +controllertools:marker:generateHelp:category=CRD
   331  
   332  // DeprecatedVersion marks this version as deprecated.
   333  type DeprecatedVersion struct {
   334  	// Warning message to be shown on the deprecated version
   335  	Warning *string `marker:",optional"`
   336  }
   337  
   338  func (s DeprecatedVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   339  	if version == "" {
   340  		// single-version, do nothing
   341  		return nil
   342  	}
   343  	// multi-version
   344  	for i := range crd.Versions {
   345  		ver := &crd.Versions[i]
   346  		if ver.Name != version {
   347  			continue
   348  		}
   349  		ver.Deprecated = true
   350  		ver.DeprecationWarning = s.Warning
   351  		break
   352  	}
   353  	return nil
   354  }
   355  
   356  // +controllertools:marker:generateHelp:category=CRD
   357  
   358  // Metadata configures the additional annotations or labels for this CRD.
   359  // For example adding annotation "api-approved.kubernetes.io" for a CRD with Kubernetes groups,
   360  // or annotation "cert-manager.io/inject-ca-from-secret" for a CRD that needs CA injection.
   361  type Metadata struct {
   362  	// Annotations will be added into the annotations of this CRD.
   363  	Annotations []string `marker:",optional"`
   364  	// Labels will be added into the labels of this CRD.
   365  	Labels []string `marker:",optional"`
   366  }
   367  
   368  func (s Metadata) ApplyToCRD(crd *apiext.CustomResourceDefinition, _ string) error {
   369  	if len(s.Annotations) > 0 {
   370  		if crd.Annotations == nil {
   371  			crd.Annotations = map[string]string{}
   372  		}
   373  		for _, str := range s.Annotations {
   374  			kv := strings.SplitN(str, "=", 2)
   375  			if len(kv) < 2 {
   376  				return fmt.Errorf("annotation %s is not in 'xxx=xxx' format", str)
   377  			}
   378  			crd.Annotations[kv[0]] = kv[1]
   379  		}
   380  	}
   381  
   382  	if len(s.Labels) > 0 {
   383  		if crd.Labels == nil {
   384  			crd.Labels = map[string]string{}
   385  		}
   386  		for _, str := range s.Labels {
   387  			kv := strings.SplitN(str, "=", 2)
   388  			crd.Labels[kv[0]] = kv[1]
   389  		}
   390  	}
   391  
   392  	return nil
   393  }
   394  
   395  // +controllertools:marker:generateHelp:category=CRD
   396  // FieldScope specifies the scope of the field. If the field scope does not match the outer-most
   397  // resource scope, then this field is ignored and not included in the final CRD.
   398  type FieldScope string
   399  
   400  func (m FieldScope) Value() string {
   401  	return string(m)
   402  }