func IsStoredVersion(crd *apiextensionsv1.CustomResourceDefinition, version string) bool {
	for _, v := range crd.Status.StoredVersions {
		if version == v {
			return true
		}
	}
	return false
}
IsStoredVersion returns whether the given version is the storage version of the CRD.
package apihelpers
import (
	"fmt"
	"net/url"
	"strings"
	"time"
	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func IsProtectedCommunityGroup(group string) bool {
	switch {
	case group == "k8s.io" || strings.HasSuffix(group, ".k8s.io"):
		return true
	case group == "kubernetes.io" || strings.HasSuffix(group, ".kubernetes.io"):
		return true
	default:
		return false
	}
}
type APIApprovalState int
const (
	APIApprovalInvalid APIApprovalState = iota
	APIApproved
	APIApprovalBypassed
	APIApprovalMissing
)
func GetAPIApprovalState(annotations map[string]string) (state APIApprovalState, reason string) {
	annotation := annotations[apiextensionsv1.KubeAPIApprovedAnnotation]
	url, annotationURLParseErr := url.ParseRequestURI(annotation)
	switch {
	case len(annotation) == 0:
		return APIApprovalMissing, fmt.Sprintf("protected groups must have approval annotation %q, see https://github.com/kubernetes/enhancements/pull/1111", apiextensionsv1.KubeAPIApprovedAnnotation)
	case strings.HasPrefix(annotation, "unapproved"):
		return APIApprovalBypassed, fmt.Sprintf("not approved: %q", annotation)
	case annotationURLParseErr == nil && url != nil && len(url.Host) > 0 && len(url.Scheme) > 0:
		return APIApproved, fmt.Sprintf("approved in %v", annotation)
	default:
		return APIApprovalInvalid, fmt.Sprintf("protected groups must have approval annotation %q with either a URL or a reason starting with \"unapproved\", see https://github.com/kubernetes/enhancements/pull/1111", apiextensionsv1.KubeAPIApprovedAnnotation)
	}
}
func SetCRDCondition(crd *apiextensionsv1.CustomResourceDefinition, newCondition apiextensionsv1.CustomResourceDefinitionCondition) {
	newCondition.LastTransitionTime = metav1.NewTime(time.Now())
	existingCondition := FindCRDCondition(crd, newCondition.Type)
	if existingCondition == nil {
		crd.Status.Conditions = append(crd.Status.Conditions, newCondition)
		return
	}
	if existingCondition.Status != newCondition.Status || existingCondition.LastTransitionTime.IsZero() {
		existingCondition.LastTransitionTime = newCondition.LastTransitionTime
	}
	existingCondition.Status = newCondition.Status
	existingCondition.Reason = newCondition.Reason
	existingCondition.Message = newCondition.Message
}
func RemoveCRDCondition(crd *apiextensionsv1.CustomResourceDefinition, conditionType apiextensionsv1.CustomResourceDefinitionConditionType) {
	newConditions := []apiextensionsv1.CustomResourceDefinitionCondition{}
	for _, condition := range crd.Status.Conditions {
		if condition.Type != conditionType {
			newConditions = append(newConditions, condition)
		}
	}
	crd.Status.Conditions = newConditions
}
func FindCRDCondition(crd *apiextensionsv1.CustomResourceDefinition, conditionType apiextensionsv1.CustomResourceDefinitionConditionType) *apiextensionsv1.CustomResourceDefinitionCondition {
	for i := range crd.Status.Conditions {
		if crd.Status.Conditions[i].Type == conditionType {
			return &crd.Status.Conditions[i]
		}
	}
	return nil
}
func IsCRDConditionTrue(crd *apiextensionsv1.CustomResourceDefinition, conditionType apiextensionsv1.CustomResourceDefinitionConditionType) bool {
	return IsCRDConditionPresentAndEqual(crd, conditionType, apiextensionsv1.ConditionTrue)
}
func IsCRDConditionFalse(crd *apiextensionsv1.CustomResourceDefinition, conditionType apiextensionsv1.CustomResourceDefinitionConditionType) bool {
	return IsCRDConditionPresentAndEqual(crd, conditionType, apiextensionsv1.ConditionFalse)
}
func IsCRDConditionPresentAndEqual(crd *apiextensionsv1.CustomResourceDefinition, conditionType apiextensionsv1.CustomResourceDefinitionConditionType, status apiextensionsv1.ConditionStatus) bool {
	for _, condition := range crd.Status.Conditions {
		if condition.Type == conditionType {
			return condition.Status == status
		}
	}
	return false
}
func IsCRDConditionEquivalent(lhs, rhs *apiextensionsv1.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
}
func CRDHasFinalizer(crd *apiextensionsv1.CustomResourceDefinition, needle string) bool {
	for _, finalizer := range crd.Finalizers {
		if finalizer == needle {
			return true
		}
	}
	return false
}
func CRDRemoveFinalizer(crd *apiextensionsv1.CustomResourceDefinition, needle string) {
	newFinalizers := []string{}
	for _, finalizer := range crd.Finalizers {
		if finalizer != needle {
			newFinalizers = append(newFinalizers, finalizer)
		}
	}
	crd.Finalizers = newFinalizers
}
func HasServedCRDVersion(crd *apiextensionsv1.CustomResourceDefinition, version string) bool {
	for _, v := range crd.Spec.Versions {
		if v.Name == version {
			return v.Served
		}
	}
	return false
}
func GetCRDStorageVersion(crd *apiextensionsv1.CustomResourceDefinition) (string, error) {
	for _, v := range crd.Spec.Versions {
		if v.Storage {
			return v.Name, nil
		}
	}
	return "", fmt.Errorf("invalid apiextensionsv1.CustomResourceDefinition, no storage version")
}