...

Source file src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/helpers.go

Documentation: k8s.io/apiextensions-apiserver/pkg/apis/apiextensions

     1  /*
     2  Copyright 2017 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 apiextensions
    18  
    19  import (
    20  	"fmt"
    21  	"time"
    22  
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  )
    25  
    26  var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
    27  
    28  // SetCRDCondition sets the status condition. It either overwrites the existing one or creates a new one.
    29  func SetCRDCondition(crd *CustomResourceDefinition, newCondition CustomResourceDefinitionCondition) {
    30  	newCondition.LastTransitionTime = metav1.NewTime(time.Now())
    31  
    32  	existingCondition := FindCRDCondition(crd, newCondition.Type)
    33  	if existingCondition == nil {
    34  		crd.Status.Conditions = append(crd.Status.Conditions, newCondition)
    35  		return
    36  	}
    37  
    38  	if existingCondition.Status != newCondition.Status || existingCondition.LastTransitionTime.IsZero() {
    39  		existingCondition.LastTransitionTime = newCondition.LastTransitionTime
    40  	}
    41  
    42  	existingCondition.Status = newCondition.Status
    43  	existingCondition.Reason = newCondition.Reason
    44  	existingCondition.Message = newCondition.Message
    45  }
    46  
    47  // RemoveCRDCondition removes the status condition.
    48  func RemoveCRDCondition(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) {
    49  	newConditions := []CustomResourceDefinitionCondition{}
    50  	for _, condition := range crd.Status.Conditions {
    51  		if condition.Type != conditionType {
    52  			newConditions = append(newConditions, condition)
    53  		}
    54  	}
    55  	crd.Status.Conditions = newConditions
    56  }
    57  
    58  // FindCRDCondition returns the condition you're looking for or nil.
    59  func FindCRDCondition(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) *CustomResourceDefinitionCondition {
    60  	for i := range crd.Status.Conditions {
    61  		if crd.Status.Conditions[i].Type == conditionType {
    62  			return &crd.Status.Conditions[i]
    63  		}
    64  	}
    65  
    66  	return nil
    67  }
    68  
    69  // IsCRDConditionTrue indicates if the condition is present and strictly true.
    70  func IsCRDConditionTrue(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool {
    71  	return IsCRDConditionPresentAndEqual(crd, conditionType, ConditionTrue)
    72  }
    73  
    74  // IsCRDConditionFalse indicates if the condition is present and false.
    75  func IsCRDConditionFalse(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool {
    76  	return IsCRDConditionPresentAndEqual(crd, conditionType, ConditionFalse)
    77  }
    78  
    79  // IsCRDConditionPresentAndEqual indicates if the condition is present and equal to the given status.
    80  func IsCRDConditionPresentAndEqual(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType, status ConditionStatus) bool {
    81  	for _, condition := range crd.Status.Conditions {
    82  		if condition.Type == conditionType {
    83  			return condition.Status == status
    84  		}
    85  	}
    86  	return false
    87  }
    88  
    89  // IsCRDConditionEquivalent returns true if the lhs and rhs are equivalent except for times.
    90  func IsCRDConditionEquivalent(lhs, rhs *CustomResourceDefinitionCondition) bool {
    91  	if lhs == nil && rhs == nil {
    92  		return true
    93  	}
    94  	if lhs == nil || rhs == nil {
    95  		return false
    96  	}
    97  
    98  	return lhs.Message == rhs.Message && lhs.Reason == rhs.Reason && lhs.Status == rhs.Status && lhs.Type == rhs.Type
    99  }
   100  
   101  // CRDHasFinalizer returns true if the finalizer is in the list.
   102  func CRDHasFinalizer(crd *CustomResourceDefinition, needle string) bool {
   103  	for _, finalizer := range crd.Finalizers {
   104  		if finalizer == needle {
   105  			return true
   106  		}
   107  	}
   108  
   109  	return false
   110  }
   111  
   112  // CRDRemoveFinalizer removes the finalizer if present.
   113  func CRDRemoveFinalizer(crd *CustomResourceDefinition, needle string) {
   114  	newFinalizers := []string{}
   115  	for _, finalizer := range crd.Finalizers {
   116  		if finalizer != needle {
   117  			newFinalizers = append(newFinalizers, finalizer)
   118  		}
   119  	}
   120  	crd.Finalizers = newFinalizers
   121  }
   122  
   123  // HasServedCRDVersion returns true if the given version is in the list of CRD's versions and the Served flag is set.
   124  func HasServedCRDVersion(crd *CustomResourceDefinition, version string) bool {
   125  	for _, v := range crd.Spec.Versions {
   126  		if v.Name == version {
   127  			return v.Served
   128  		}
   129  	}
   130  	return false
   131  }
   132  
   133  // GetCRDStorageVersion returns the storage version for given CRD.
   134  func GetCRDStorageVersion(crd *CustomResourceDefinition) (string, error) {
   135  	for _, v := range crd.Spec.Versions {
   136  		if v.Storage {
   137  			return v.Name, nil
   138  		}
   139  	}
   140  	// This should not happened if crd is valid
   141  	return "", fmt.Errorf("invalid CustomResourceDefinition, no storage version")
   142  }
   143  
   144  // IsStoredVersion returns whether the given version is the storage version of the CRD.
   145  func IsStoredVersion(crd *CustomResourceDefinition, version string) bool {
   146  	for _, v := range crd.Status.StoredVersions {
   147  		if version == v {
   148  			return true
   149  		}
   150  	}
   151  	return false
   152  }
   153  
   154  // GetSchemaForVersion returns the validation schema for the given version or nil.
   155  func GetSchemaForVersion(crd *CustomResourceDefinition, version string) (*CustomResourceValidation, error) {
   156  	if !HasPerVersionSchema(crd.Spec.Versions) {
   157  		return crd.Spec.Validation, nil
   158  	}
   159  	if crd.Spec.Validation != nil {
   160  		return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version schemas must be mutual exclusive", crd.Name, version)
   161  	}
   162  	for _, v := range crd.Spec.Versions {
   163  		if version == v.Name {
   164  			return v.Schema, nil
   165  		}
   166  	}
   167  	return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
   168  }
   169  
   170  // GetSubresourcesForVersion returns the subresources for given version or nil.
   171  func GetSubresourcesForVersion(crd *CustomResourceDefinition, version string) (*CustomResourceSubresources, error) {
   172  	if !HasPerVersionSubresources(crd.Spec.Versions) {
   173  		return crd.Spec.Subresources, nil
   174  	}
   175  	if crd.Spec.Subresources != nil {
   176  		return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version subresources must be mutual exclusive", crd.Name, version)
   177  	}
   178  	for _, v := range crd.Spec.Versions {
   179  		if version == v.Name {
   180  			return v.Subresources, nil
   181  		}
   182  	}
   183  	return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
   184  }
   185  
   186  // GetColumnsForVersion returns the columns for given version or nil.
   187  // NOTE: the newly logically-defaulted columns is not pointing to the original CRD object.
   188  // One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
   189  // the original CRD object instead.
   190  func GetColumnsForVersion(crd *CustomResourceDefinition, version string) ([]CustomResourceColumnDefinition, error) {
   191  	if !HasPerVersionColumns(crd.Spec.Versions) {
   192  		return serveDefaultColumnsIfEmpty(crd.Spec.AdditionalPrinterColumns), nil
   193  	}
   194  	if len(crd.Spec.AdditionalPrinterColumns) > 0 {
   195  		return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version additionalPrinterColumns must be mutual exclusive", crd.Name, version)
   196  	}
   197  	for _, v := range crd.Spec.Versions {
   198  		if version == v.Name {
   199  			return serveDefaultColumnsIfEmpty(v.AdditionalPrinterColumns), nil
   200  		}
   201  	}
   202  	return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
   203  }
   204  
   205  // HasPerVersionSchema returns true if a CRD uses per-version schema.
   206  func HasPerVersionSchema(versions []CustomResourceDefinitionVersion) bool {
   207  	for _, v := range versions {
   208  		if v.Schema != nil {
   209  			return true
   210  		}
   211  	}
   212  	return false
   213  }
   214  
   215  // HasPerVersionSubresources returns true if a CRD uses per-version subresources.
   216  func HasPerVersionSubresources(versions []CustomResourceDefinitionVersion) bool {
   217  	for _, v := range versions {
   218  		if v.Subresources != nil {
   219  			return true
   220  		}
   221  	}
   222  	return false
   223  }
   224  
   225  // HasPerVersionColumns returns true if a CRD uses per-version columns.
   226  func HasPerVersionColumns(versions []CustomResourceDefinitionVersion) bool {
   227  	for _, v := range versions {
   228  		if len(v.AdditionalPrinterColumns) > 0 {
   229  			return true
   230  		}
   231  	}
   232  	return false
   233  }
   234  
   235  // serveDefaultColumnsIfEmpty applies logically defaulting to columns, if the input columns is empty.
   236  // NOTE: in this way, the newly logically-defaulted columns is not pointing to the original CRD object.
   237  // One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
   238  // the original CRD object instead.
   239  func serveDefaultColumnsIfEmpty(columns []CustomResourceColumnDefinition) []CustomResourceColumnDefinition {
   240  	if len(columns) > 0 {
   241  		return columns
   242  	}
   243  	return []CustomResourceColumnDefinition{
   244  		{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
   245  	}
   246  }
   247  
   248  // HasVersionServed returns true if given CRD has given version served.
   249  func HasVersionServed(crd *CustomResourceDefinition, version string) bool {
   250  	for _, v := range crd.Spec.Versions {
   251  		if !v.Served || v.Name != version {
   252  			continue
   253  		}
   254  		return true
   255  	}
   256  	return false
   257  }
   258  

View as plain text