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