github.com/christarazi/controller-tools@v0.3.1-0.20210907042920-aa94049173f8/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  
    22  	apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    23  
    24  	"sigs.k8s.io/controller-tools/pkg/markers"
    25  )
    26  
    27  // CRDMarkers lists all markers that directly modify the CRD (not validation
    28  // schemas).
    29  var CRDMarkers = []*definitionWithHelp{
    30  	// TODO(directxman12): more detailed help
    31  	must(markers.MakeDefinition("kubebuilder:subresource:status", markers.DescribesType, SubresourceStatus{})).
    32  		WithHelp(SubresourceStatus{}.Help()),
    33  
    34  	must(markers.MakeDefinition("kubebuilder:subresource:scale", markers.DescribesType, SubresourceScale{})).
    35  		WithHelp(SubresourceScale{}.Help()),
    36  
    37  	must(markers.MakeDefinition("kubebuilder:printcolumn", markers.DescribesType, PrintColumn{})).
    38  		WithHelp(PrintColumn{}.Help()),
    39  
    40  	must(markers.MakeDefinition("kubebuilder:resource", markers.DescribesType, Resource{})).
    41  		WithHelp(Resource{}.Help()),
    42  
    43  	must(markers.MakeDefinition("kubebuilder:storageversion", markers.DescribesType, StorageVersion{})).
    44  		WithHelp(StorageVersion{}.Help()),
    45  
    46  	must(markers.MakeDefinition("kubebuilder:skipversion", markers.DescribesType, SkipVersion{})).
    47  		WithHelp(SkipVersion{}.Help()),
    48  
    49  	must(markers.MakeDefinition("kubebuilder:unservedversion", markers.DescribesType, UnservedVersion{})).
    50  		WithHelp(UnservedVersion{}.Help()),
    51  
    52  	must(markers.MakeDefinition("kubebuilder:xpreserveunknownfields", markers.DescribesType, XPreserveUnknownFields{})).
    53  		WithHelp(XPreserveUnknownFields{}.Help()),
    54  
    55  	must(markers.MakeDefinition("kubebuilder:topleveldesc", markers.DescribesType, TopLevelDesc{})).
    56  		WithHelp(TopLevelDesc{}.Help()),
    57  }
    58  
    59  // TODO: categories and singular used to be annotations types
    60  // TODO: doc
    61  
    62  func init() {
    63  	AllDefinitions = append(AllDefinitions, CRDMarkers...)
    64  }
    65  
    66  // +controllertools:marker:generateHelp:category=CRD
    67  
    68  // SubresourceStatus enables the "/status" subresource on a CRD.
    69  type SubresourceStatus struct{}
    70  
    71  func (s SubresourceStatus) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
    72  	var subresources *apiext.CustomResourceSubresources
    73  	for i := range crd.Versions {
    74  		ver := &crd.Versions[i]
    75  		if ver.Name != version {
    76  			continue
    77  		}
    78  		if ver.Subresources == nil {
    79  			ver.Subresources = &apiext.CustomResourceSubresources{}
    80  		}
    81  		subresources = ver.Subresources
    82  		break
    83  	}
    84  	if subresources == nil {
    85  		return fmt.Errorf("status subresource applied to version %q not in CRD", version)
    86  	}
    87  	subresources.Status = &apiext.CustomResourceSubresourceStatus{}
    88  	return nil
    89  }
    90  
    91  // +controllertools:marker:generateHelp:category=CRD
    92  
    93  // SubresourceScale enables the "/scale" subresource on a CRD.
    94  type SubresourceScale struct {
    95  	// marker names are leftover legacy cruft
    96  
    97  	// SpecPath specifies the jsonpath to the replicas field for the scale's spec.
    98  	SpecPath string `marker:"specpath"`
    99  
   100  	// StatusPath specifies the jsonpath to the replicas field for the scale's status.
   101  	StatusPath string `marker:"statuspath"`
   102  
   103  	// SelectorPath specifies the jsonpath to the pod label selector field for the scale's status.
   104  	//
   105  	// The selector field must be the *string* form (serialized form) of a selector.
   106  	// Setting a pod label selector is necessary for your type to work with the HorizontalPodAutoscaler.
   107  	SelectorPath *string `marker:"selectorpath"`
   108  }
   109  
   110  func (s SubresourceScale) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   111  	var subresources *apiext.CustomResourceSubresources
   112  	for i := range crd.Versions {
   113  		ver := &crd.Versions[i]
   114  		if ver.Name != version {
   115  			continue
   116  		}
   117  		if ver.Subresources == nil {
   118  			ver.Subresources = &apiext.CustomResourceSubresources{}
   119  		}
   120  		subresources = ver.Subresources
   121  		break
   122  	}
   123  	if subresources == nil {
   124  		return fmt.Errorf("scale subresource applied to version %q not in CRD", version)
   125  	}
   126  	subresources.Scale = &apiext.CustomResourceSubresourceScale{
   127  		SpecReplicasPath:   s.SpecPath,
   128  		StatusReplicasPath: s.StatusPath,
   129  		LabelSelectorPath:  s.SelectorPath,
   130  	}
   131  	return nil
   132  }
   133  
   134  // +controllertools:marker:generateHelp:category=CRD
   135  
   136  // StorageVersion marks this version as the "storage version" for the CRD for conversion.
   137  //
   138  // When conversion is enabled for a CRD (i.e. it's not a trivial-versions/single-version CRD),
   139  // one version is set as the "storage version" to be stored in etcd.  Attempting to store any
   140  // other version will result in conversion to the storage version via a conversion webhook.
   141  type StorageVersion struct{}
   142  
   143  func (s StorageVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   144  	if version == "" {
   145  		// single-version, do nothing
   146  		return nil
   147  	}
   148  	// multi-version
   149  	for i := range crd.Versions {
   150  		ver := &crd.Versions[i]
   151  		if ver.Name != version {
   152  			continue
   153  		}
   154  		ver.Storage = true
   155  		break
   156  	}
   157  	return nil
   158  }
   159  
   160  // +controllertools:marker:generateHelp:category=CRD
   161  
   162  // SkipVersion removes the particular version of the CRD from the CRDs spec.
   163  //
   164  // This is useful if you need to skip generating and listing version entries
   165  // for 'internal' resource versions, which typically exist if using the
   166  // Kubernetes upstream conversion-gen tool.
   167  type SkipVersion struct{}
   168  
   169  func (s SkipVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   170  	if version == "" {
   171  		// single-version, this is an invalid state
   172  		return fmt.Errorf("cannot skip a version if there is only a single version")
   173  	}
   174  	var versions []apiext.CustomResourceDefinitionVersion
   175  	// multi-version
   176  	for i := range crd.Versions {
   177  		ver := crd.Versions[i]
   178  		if ver.Name == version {
   179  			// skip the skipped version
   180  			continue
   181  		}
   182  		versions = append(versions, ver)
   183  	}
   184  	crd.Versions = versions
   185  	return nil
   186  }
   187  
   188  // +controllertools:marker:generateHelp:category=CRD
   189  
   190  // PrintColumn adds a column to "kubectl get" output for this CRD.
   191  type PrintColumn struct {
   192  	// Name specifies the name of the column.
   193  	Name string
   194  
   195  	// Type indicates the type of the column.
   196  	//
   197  	// It may be any OpenAPI data type listed at
   198  	// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types.
   199  	Type string
   200  
   201  	// JSONPath specifies the jsonpath expression used to extract the value of the column.
   202  	JSONPath string `marker:"JSONPath"` // legacy cruft
   203  
   204  	// Description specifies the help/description for this column.
   205  	Description string `marker:",optional"`
   206  
   207  	// Format specifies the format of the column.
   208  	//
   209  	// It may be any OpenAPI data format corresponding to the type, listed at
   210  	// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types.
   211  	Format string `marker:",optional"`
   212  
   213  	// Priority indicates how important it is that this column be displayed.
   214  	//
   215  	// Lower priority (*higher* numbered) columns will be hidden if the terminal
   216  	// width is too small.
   217  	Priority int32 `marker:",optional"`
   218  }
   219  
   220  func (s PrintColumn) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   221  	var columns *[]apiext.CustomResourceColumnDefinition
   222  	for i := range crd.Versions {
   223  		ver := &crd.Versions[i]
   224  		if ver.Name != version {
   225  			continue
   226  		}
   227  		if ver.Subresources == nil {
   228  			ver.Subresources = &apiext.CustomResourceSubresources{}
   229  		}
   230  		columns = &ver.AdditionalPrinterColumns
   231  		break
   232  	}
   233  	if columns == nil {
   234  		return fmt.Errorf("printer columns applied to version %q not in CRD", version)
   235  	}
   236  
   237  	*columns = append(*columns, apiext.CustomResourceColumnDefinition{
   238  		Name:        s.Name,
   239  		Type:        s.Type,
   240  		JSONPath:    s.JSONPath,
   241  		Description: s.Description,
   242  		Format:      s.Format,
   243  		Priority:    s.Priority,
   244  	})
   245  
   246  	return nil
   247  }
   248  
   249  // +controllertools:marker:generateHelp:category=CRD
   250  
   251  // Resource configures naming and scope for a CRD.
   252  type Resource struct {
   253  	// Path specifies the plural "resource" for this CRD.
   254  	//
   255  	// It generally corresponds to a plural, lower-cased version of the Kind.
   256  	// See https://book.kubebuilder.io/cronjob-tutorial/gvks.html.
   257  	Path string `marker:",optional"`
   258  
   259  	// ShortName specifies aliases for this CRD.
   260  	//
   261  	// Short names are often used when people have work with your resource
   262  	// over and over again.  For instance, "rs" for "replicaset" or
   263  	// "crd" for customresourcedefinition.
   264  	ShortName []string `marker:",optional"`
   265  
   266  	// Categories specifies which group aliases this resource is part of.
   267  	//
   268  	// Group aliases are used to work with groups of resources at once.
   269  	// The most common one is "all" which covers about a third of the base
   270  	// resources in Kubernetes, and is generally used for "user-facing" resources.
   271  	Categories []string `marker:",optional"`
   272  
   273  	// Singular overrides the singular form of your resource.
   274  	//
   275  	// The singular form is otherwise defaulted off the plural (path).
   276  	Singular string `marker:",optional"`
   277  
   278  	// Scope overrides the scope of the CRD (Cluster vs Namespaced).
   279  	//
   280  	// Scope defaults to "Namespaced".  Cluster-scoped ("Cluster") resources
   281  	// don't exist in namespaces.
   282  	Scope string `marker:",optional"`
   283  }
   284  
   285  func (s Resource) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   286  	if s.Path != "" {
   287  		crd.Names.Plural = s.Path
   288  	}
   289  	if s.Singular != "" {
   290  		crd.Names.Singular = s.Singular
   291  	}
   292  	crd.Names.ShortNames = s.ShortName
   293  	crd.Names.Categories = s.Categories
   294  
   295  	switch s.Scope {
   296  	case "":
   297  		crd.Scope = apiext.NamespaceScoped
   298  	default:
   299  		crd.Scope = apiext.ResourceScope(s.Scope)
   300  	}
   301  
   302  	return nil
   303  }
   304  
   305  // +controllertools:marker:generateHelp:category=CRD
   306  
   307  // UnservedVersion does not serve this version.
   308  //
   309  // This is useful if you need to drop support for a version in favor of a newer version.
   310  type UnservedVersion struct{}
   311  
   312  func (s UnservedVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   313  	for i := range crd.Versions {
   314  		ver := &crd.Versions[i]
   315  		if ver.Name != version {
   316  			continue
   317  		}
   318  		ver.Served = false
   319  		break
   320  	}
   321  	return nil
   322  }
   323  
   324  // +controllertools:marker:generateHelp:category=CRD
   325  
   326  // TopLevelDesc adds "description" to the top-level validation schema.
   327  //
   328  // This is useful for CRDs that want a top-level description field to describe
   329  // the resource. Specifying this marker will add a description field at the
   330  // top-level to the validation schema.
   331  type TopLevelDesc struct{}
   332  
   333  func (s TopLevelDesc) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error {
   334  	for i := range crd.Versions {
   335  		ver := &crd.Versions[i]
   336  		if ver.Name != version {
   337  			continue
   338  		}
   339  		ver.Schema.OpenAPIV3Schema.Properties["description"] = apiext.JSONSchemaProps{
   340  			Description: "Description is a human-readable description of the resource.",
   341  			Type:        "string",
   342  		}
   343  		break
   344  	}
   345  	return nil
   346  }
   347  
   348  // NB(directxman12): singular was historically distinct, so we keep it here for backwards compat