2019-01-12 04:58:27 +00:00
/ *
Copyright 2017 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package apiextensions
import (
"fmt"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
2019-08-30 18:33:25 +00:00
var swaggerMetadataDescriptions = metav1 . ObjectMeta { } . SwaggerDoc ( )
2019-04-07 17:07:55 +00:00
// SetCRDCondition sets the status condition. It either overwrites the existing one or creates a new one.
2019-01-12 04:58:27 +00:00
func SetCRDCondition ( crd * CustomResourceDefinition , newCondition CustomResourceDefinitionCondition ) {
2019-09-27 21:51:53 +00:00
newCondition . LastTransitionTime = metav1 . NewTime ( time . Now ( ) )
2019-01-12 04:58:27 +00:00
existingCondition := FindCRDCondition ( crd , newCondition . Type )
if existingCondition == nil {
crd . Status . Conditions = append ( crd . Status . Conditions , newCondition )
return
}
2019-09-27 21:51:53 +00:00
if existingCondition . Status != newCondition . Status || existingCondition . LastTransitionTime . IsZero ( ) {
2019-01-12 04:58:27 +00:00
existingCondition . LastTransitionTime = newCondition . LastTransitionTime
}
2019-09-27 21:51:53 +00:00
existingCondition . Status = newCondition . Status
2019-01-12 04:58:27 +00:00
existingCondition . Reason = newCondition . Reason
existingCondition . Message = newCondition . Message
}
// RemoveCRDCondition removes the status condition.
func RemoveCRDCondition ( crd * CustomResourceDefinition , conditionType CustomResourceDefinitionConditionType ) {
newConditions := [ ] CustomResourceDefinitionCondition { }
for _ , condition := range crd . Status . Conditions {
if condition . Type != conditionType {
newConditions = append ( newConditions , condition )
}
}
crd . Status . Conditions = newConditions
}
2019-04-07 17:07:55 +00:00
// FindCRDCondition returns the condition you're looking for or nil.
2019-01-12 04:58:27 +00:00
func FindCRDCondition ( crd * CustomResourceDefinition , conditionType CustomResourceDefinitionConditionType ) * CustomResourceDefinitionCondition {
for i := range crd . Status . Conditions {
if crd . Status . Conditions [ i ] . Type == conditionType {
return & crd . Status . Conditions [ i ]
}
}
return nil
}
2019-04-07 17:07:55 +00:00
// IsCRDConditionTrue indicates if the condition is present and strictly true.
2019-01-12 04:58:27 +00:00
func IsCRDConditionTrue ( crd * CustomResourceDefinition , conditionType CustomResourceDefinitionConditionType ) bool {
return IsCRDConditionPresentAndEqual ( crd , conditionType , ConditionTrue )
}
2019-04-07 17:07:55 +00:00
// IsCRDConditionFalse indicates if the condition is present and false.
2019-01-12 04:58:27 +00:00
func IsCRDConditionFalse ( crd * CustomResourceDefinition , conditionType CustomResourceDefinitionConditionType ) bool {
return IsCRDConditionPresentAndEqual ( crd , conditionType , ConditionFalse )
}
2019-04-07 17:07:55 +00:00
// IsCRDConditionPresentAndEqual indicates if the condition is present and equal to the given status.
2019-01-12 04:58:27 +00:00
func IsCRDConditionPresentAndEqual ( crd * CustomResourceDefinition , conditionType CustomResourceDefinitionConditionType , status ConditionStatus ) bool {
for _ , condition := range crd . Status . Conditions {
if condition . Type == conditionType {
return condition . Status == status
}
}
return false
}
2019-04-07 17:07:55 +00:00
// IsCRDConditionEquivalent returns true if the lhs and rhs are equivalent except for times.
2019-01-12 04:58:27 +00:00
func IsCRDConditionEquivalent ( lhs , rhs * CustomResourceDefinitionCondition ) bool {
if lhs == nil && rhs == nil {
return true
}
if lhs == nil || rhs == nil {
return false
}
return lhs . Message == rhs . Message && lhs . Reason == rhs . Reason && lhs . Status == rhs . Status && lhs . Type == rhs . Type
}
2019-04-07 17:07:55 +00:00
// CRDHasFinalizer returns true if the finalizer is in the list.
2019-01-12 04:58:27 +00:00
func CRDHasFinalizer ( crd * CustomResourceDefinition , needle string ) bool {
for _ , finalizer := range crd . Finalizers {
if finalizer == needle {
return true
}
}
return false
}
2019-04-07 17:07:55 +00:00
// CRDRemoveFinalizer removes the finalizer if present.
2019-01-12 04:58:27 +00:00
func CRDRemoveFinalizer ( crd * CustomResourceDefinition , needle string ) {
newFinalizers := [ ] string { }
for _ , finalizer := range crd . Finalizers {
if finalizer != needle {
newFinalizers = append ( newFinalizers , finalizer )
}
}
crd . Finalizers = newFinalizers
}
2019-04-07 17:07:55 +00:00
// HasServedCRDVersion returns true if the given version is in the list of CRD's versions and the Served flag is set.
2019-01-12 04:58:27 +00:00
func HasServedCRDVersion ( crd * CustomResourceDefinition , version string ) bool {
for _ , v := range crd . Spec . Versions {
if v . Name == version {
return v . Served
}
}
return false
}
// GetCRDStorageVersion returns the storage version for given CRD.
func GetCRDStorageVersion ( crd * CustomResourceDefinition ) ( string , error ) {
for _ , v := range crd . Spec . Versions {
if v . Storage {
return v . Name , nil
}
}
// This should not happened if crd is valid
return "" , fmt . Errorf ( "invalid CustomResourceDefinition, no storage version" )
}
2019-04-07 17:07:55 +00:00
// IsStoredVersion returns whether the given version is the storage version of the CRD.
2019-01-12 04:58:27 +00:00
func IsStoredVersion ( crd * CustomResourceDefinition , version string ) bool {
for _ , v := range crd . Status . StoredVersions {
if version == v {
return true
}
}
return false
}
2019-04-07 17:07:55 +00:00
// GetSchemaForVersion returns the validation schema for the given version or nil.
func GetSchemaForVersion ( crd * CustomResourceDefinition , version string ) ( * CustomResourceValidation , error ) {
if ! HasPerVersionSchema ( crd . Spec . Versions ) {
return crd . Spec . Validation , nil
}
if crd . Spec . Validation != nil {
return nil , fmt . Errorf ( "malformed CustomResourceDefinition %s version %s: top-level and per-version schemas must be mutual exclusive" , crd . Name , version )
}
for _ , v := range crd . Spec . Versions {
if version == v . Name {
return v . Schema , nil
}
}
return nil , fmt . Errorf ( "version %s not found in CustomResourceDefinition: %v" , version , crd . Name )
}
// GetSubresourcesForVersion returns the subresources for given version or nil.
func GetSubresourcesForVersion ( crd * CustomResourceDefinition , version string ) ( * CustomResourceSubresources , error ) {
if ! HasPerVersionSubresources ( crd . Spec . Versions ) {
return crd . Spec . Subresources , nil
}
if crd . Spec . Subresources != nil {
return nil , fmt . Errorf ( "malformed CustomResourceDefinition %s version %s: top-level and per-version subresources must be mutual exclusive" , crd . Name , version )
}
for _ , v := range crd . Spec . Versions {
if version == v . Name {
return v . Subresources , nil
}
}
return nil , fmt . Errorf ( "version %s not found in CustomResourceDefinition: %v" , version , crd . Name )
}
// GetColumnsForVersion returns the columns for given version or nil.
// NOTE: the newly logically-defaulted columns is not pointing to the original CRD object.
// One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
// the original CRD object instead.
func GetColumnsForVersion ( crd * CustomResourceDefinition , version string ) ( [ ] CustomResourceColumnDefinition , error ) {
if ! HasPerVersionColumns ( crd . Spec . Versions ) {
return serveDefaultColumnsIfEmpty ( crd . Spec . AdditionalPrinterColumns ) , nil
}
if len ( crd . Spec . AdditionalPrinterColumns ) > 0 {
return nil , fmt . Errorf ( "malformed CustomResourceDefinition %s version %s: top-level and per-version additionalPrinterColumns must be mutual exclusive" , crd . Name , version )
}
for _ , v := range crd . Spec . Versions {
if version == v . Name {
return serveDefaultColumnsIfEmpty ( v . AdditionalPrinterColumns ) , nil
}
}
return nil , fmt . Errorf ( "version %s not found in CustomResourceDefinition: %v" , version , crd . Name )
}
// HasPerVersionSchema returns true if a CRD uses per-version schema.
func HasPerVersionSchema ( versions [ ] CustomResourceDefinitionVersion ) bool {
for _ , v := range versions {
if v . Schema != nil {
return true
}
}
return false
}
// HasPerVersionSubresources returns true if a CRD uses per-version subresources.
func HasPerVersionSubresources ( versions [ ] CustomResourceDefinitionVersion ) bool {
for _ , v := range versions {
if v . Subresources != nil {
return true
}
}
return false
}
// HasPerVersionColumns returns true if a CRD uses per-version columns.
func HasPerVersionColumns ( versions [ ] CustomResourceDefinitionVersion ) bool {
for _ , v := range versions {
if len ( v . AdditionalPrinterColumns ) > 0 {
return true
}
}
return false
}
// serveDefaultColumnsIfEmpty applies logically defaulting to columns, if the input columns is empty.
// NOTE: in this way, the newly logically-defaulted columns is not pointing to the original CRD object.
// One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
// the original CRD object instead.
func serveDefaultColumnsIfEmpty ( columns [ ] CustomResourceColumnDefinition ) [ ] CustomResourceColumnDefinition {
if len ( columns ) > 0 {
return columns
}
return [ ] CustomResourceColumnDefinition {
2019-08-30 18:33:25 +00:00
{ Name : "Age" , Type : "date" , Description : swaggerMetadataDescriptions [ "creationTimestamp" ] , JSONPath : ".metadata.creationTimestamp" } ,
2019-04-07 17:07:55 +00:00
}
}
// HasVersionServed returns true if given CRD has given version served.
func HasVersionServed ( crd * CustomResourceDefinition , version string ) bool {
for _ , v := range crd . Spec . Versions {
if ! v . Served || v . Name != version {
continue
}
return true
}
return false
}