diff --git a/go.mod b/go.mod index 7908a1d4f..7f40e258d 100644 --- a/go.mod +++ b/go.mod @@ -118,3 +118,5 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) + +replace github.com/openshift/library-go => github.com/deads2k/library-go v0.0.0-20241022211154-c774bfa7f641 diff --git a/go.sum b/go.sum index 8a1292e8f..97002d5c4 100644 --- a/go.sum +++ b/go.sum @@ -33,6 +33,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deads2k/library-go v0.0.0-20241022211154-c774bfa7f641 h1:iuX6hQtQHkF9i40HPwdqXqaXPlVGcBOWcYUjdCrvSfo= +github.com/deads2k/library-go v0.0.0-20241022211154-c774bfa7f641/go.mod h1:9B1MYPoLtP9tqjWxcbUNVpwxy68zOH/3EIP6c31dAM0= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= @@ -148,8 +150,6 @@ github.com/openshift/build-machinery-go v0.0.0-20240613134303-8359781da660 h1:F0 github.com/openshift/build-machinery-go v0.0.0-20240613134303-8359781da660/go.mod h1:8jcm8UPtg2mCAsxfqKil1xrmRMI3a+XU2TZ9fF8A7TE= github.com/openshift/client-go v0.0.0-20241001162912-da6d55e4611f h1:FRc0bVNWprihWS0GqQWzb3dY4dkCwpOP3mDw5NwSoR4= github.com/openshift/client-go v0.0.0-20241001162912-da6d55e4611f/go.mod h1:KiZi2mJRH1TOJ3FtBDYS6YvUL30s/iIXaGSUrSa36mo= -github.com/openshift/library-go v0.0.0-20241016205830-b3a7a46e7136 h1:DzmXoRRel2FiuAHFlA2TG/52OIMQ8oid6GwFhy9pcAo= -github.com/openshift/library-go v0.0.0-20241016205830-b3a7a46e7136/go.mod h1:9B1MYPoLtP9tqjWxcbUNVpwxy68zOH/3EIP6c31dAM0= github.com/openshift/multi-operator-manager v0.0.0-20241017140751-8b22f6c45da3 h1:/rOm5CjL8W8sSeSzf/bg8wySP98+juT2SbVhBhmROLo= github.com/openshift/multi-operator-manager v0.0.0-20241017140751-8b22f6c45da3/go.mod h1:7u7Wj5yctFzwixEJdrnpZCCNuJwdwls3SwqHoxRlN7E= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= diff --git a/vendor/github.com/openshift/library-go/pkg/apiserver/jsonpatch/jsonpatch.go b/vendor/github.com/openshift/library-go/pkg/apiserver/jsonpatch/jsonpatch.go index 282b17c60..a718832b1 100644 --- a/vendor/github.com/openshift/library-go/pkg/apiserver/jsonpatch/jsonpatch.go +++ b/vendor/github.com/openshift/library-go/pkg/apiserver/jsonpatch/jsonpatch.go @@ -2,6 +2,9 @@ package jsonpatch import ( "encoding/json" + "fmt" + + utilerrors "k8s.io/apimachinery/pkg/util/errors" ) type PatchOperation struct { @@ -39,6 +42,9 @@ func (p *PatchSet) IsEmpty() bool { } func (p *PatchSet) Marshal() ([]byte, error) { + if err := p.validate(); err != nil { + return nil, err + } jsonBytes, err := json.Marshal(p.patches) if err != nil { return nil, err @@ -55,6 +61,22 @@ func (p *PatchSet) addOperation(op, path string, value interface{}) { p.patches = append(p.patches, patch) } +func (p *PatchSet) validate() error { + var errs []error + for i, patch := range p.patches { + if patch.Op == patchTestOperation { + // testing resourceVersion is fragile + // because it is likely to change frequently + // instead, test against a different field + // should be written. + if patch.Path == "/metadata/resourceVersion" { + errs = append(errs, fmt.Errorf("test operation at index: %d contains forbidden path: %q", i, patch.Path)) + } + } + } + return utilerrors.NewAggregate(errs) +} + type TestCondition struct { path string value interface{} diff --git a/vendor/github.com/openshift/library-go/pkg/config/clusteroperator/v1helpers/status.go b/vendor/github.com/openshift/library-go/pkg/config/clusteroperator/v1helpers/status.go index 8491bc9e9..923651620 100644 --- a/vendor/github.com/openshift/library-go/pkg/config/clusteroperator/v1helpers/status.go +++ b/vendor/github.com/openshift/library-go/pkg/config/clusteroperator/v1helpers/status.go @@ -3,57 +3,20 @@ package v1helpers import ( "bytes" "fmt" - "strings" - "time" - + applyconfigv1 "github.com/openshift/client-go/config/applyconfigurations/config/v1" "k8s.io/apimachinery/pkg/api/equality" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/json" + "k8s.io/utils/ptr" + "strings" configv1 "github.com/openshift/api/config/v1" ) -// SetStatusCondition sets the corresponding condition in conditions to newCondition. -func SetStatusCondition(conditions *[]configv1.ClusterOperatorStatusCondition, newCondition configv1.ClusterOperatorStatusCondition) { - if conditions == nil { - conditions = &[]configv1.ClusterOperatorStatusCondition{} - } - existingCondition := FindStatusCondition(*conditions, newCondition.Type) - if existingCondition == nil { - newCondition.LastTransitionTime = metav1.NewTime(time.Now()) - *conditions = append(*conditions, newCondition) - return - } - - if existingCondition.Status != newCondition.Status { - existingCondition.Status = newCondition.Status - existingCondition.LastTransitionTime = metav1.NewTime(time.Now()) - } - - existingCondition.Reason = newCondition.Reason - existingCondition.Message = newCondition.Message -} - -// RemoveStatusCondition removes the corresponding conditionType from conditions. -func RemoveStatusCondition(conditions *[]configv1.ClusterOperatorStatusCondition, conditionType configv1.ClusterStatusConditionType) { - if conditions == nil { - conditions = &[]configv1.ClusterOperatorStatusCondition{} - } - newConditions := []configv1.ClusterOperatorStatusCondition{} - for _, condition := range *conditions { - if condition.Type != conditionType { - newConditions = append(newConditions, condition) - } - } - - *conditions = newConditions -} - // FindStatusCondition finds the conditionType in conditions. -func FindStatusCondition(conditions []configv1.ClusterOperatorStatusCondition, conditionType configv1.ClusterStatusConditionType) *configv1.ClusterOperatorStatusCondition { +func FindStatusCondition(conditions []applyconfigv1.ClusterOperatorStatusConditionApplyConfiguration, conditionType *configv1.ClusterStatusConditionType) *applyconfigv1.ClusterOperatorStatusConditionApplyConfiguration { for i := range conditions { - if conditions[i].Type == conditionType { + if ptr.Deref(conditions[i].Type, "") == ptr.Deref(conditionType, "") { return &conditions[i] } } @@ -62,38 +25,52 @@ func FindStatusCondition(conditions []configv1.ClusterOperatorStatusCondition, c } // GetStatusDiff returns a string representing change in condition status in human readable form. -func GetStatusDiff(oldStatus configv1.ClusterOperatorStatus, newStatus configv1.ClusterOperatorStatus) string { +func GetStatusDiff(oldStatus, newStatus *applyconfigv1.ClusterOperatorStatusApplyConfiguration) string { + switch { + case oldStatus == nil && newStatus == nil: + case oldStatus != nil && newStatus == nil: + return "status was removed" + } messages := []string{} for _, newCondition := range newStatus.Conditions { + if oldStatus == nil { + messages = append(messages, fmt.Sprintf("%s set to %s (%q)", ptr.Deref(newCondition.Type, ""), ptr.Deref(newCondition.Status, ""), ptr.Deref(newCondition.Message, ""))) + continue + } existingStatusCondition := FindStatusCondition(oldStatus.Conditions, newCondition.Type) if existingStatusCondition == nil { - messages = append(messages, fmt.Sprintf("%s set to %s (%q)", newCondition.Type, newCondition.Status, newCondition.Message)) + messages = append(messages, fmt.Sprintf("%s set to %s (%q)", ptr.Deref(newCondition.Type, ""), ptr.Deref(newCondition.Status, ""), ptr.Deref(newCondition.Message, ""))) continue } - if existingStatusCondition.Status != newCondition.Status { - messages = append(messages, fmt.Sprintf("%s changed from %s to %s (%q)", existingStatusCondition.Type, existingStatusCondition.Status, newCondition.Status, newCondition.Message)) + if ptr.Deref(existingStatusCondition.Status, "") != ptr.Deref(newCondition.Status, "") { + messages = append(messages, fmt.Sprintf("%s changed from %s to %s (%q)", ptr.Deref(existingStatusCondition.Type, ""), ptr.Deref(existingStatusCondition.Status, ""), ptr.Deref(newCondition.Status, ""), ptr.Deref(newCondition.Message, ""))) continue } - if existingStatusCondition.Message != newCondition.Message { - messages = append(messages, fmt.Sprintf("%s message changed from %q to %q", existingStatusCondition.Type, existingStatusCondition.Message, newCondition.Message)) + existingMessage := strings.TrimPrefix(ptr.Deref(existingStatusCondition.Message, ""), "\ufeff") + newMessage := strings.TrimPrefix(ptr.Deref(newCondition.Message, ""), "\ufeff") + if existingMessage != newMessage { + messages = append(messages, fmt.Sprintf("%s message changed from %q to %q", ptr.Deref(existingStatusCondition.Type, ""), existingMessage, newMessage)) } } - for _, oldCondition := range oldStatus.Conditions { - // This should not happen. It means we removed old condition entirely instead of just changing its status - if c := FindStatusCondition(newStatus.Conditions, oldCondition.Type); c == nil { - messages = append(messages, fmt.Sprintf("%s was removed", oldCondition.Type)) + if oldStatus != nil { + for _, oldCondition := range oldStatus.Conditions { + // This should not happen. It means we removed old condition entirely instead of just changing its status + if c := FindStatusCondition(newStatus.Conditions, oldCondition.Type); c == nil { + messages = append(messages, fmt.Sprintf("%s was removed", ptr.Deref(oldCondition.Type, ""))) + } } } - if !equality.Semantic.DeepEqual(oldStatus.RelatedObjects, newStatus.RelatedObjects) { - messages = append(messages, fmt.Sprintf("status.relatedObjects changed from %q to %q", oldStatus.RelatedObjects, newStatus.RelatedObjects)) - } - if !equality.Semantic.DeepEqual(oldStatus.Extension, newStatus.Extension) { - messages = append(messages, fmt.Sprintf("status.extension changed from %q to %q", oldStatus.Extension, newStatus.Extension)) - } - - if !equality.Semantic.DeepEqual(oldStatus.Versions, newStatus.Versions) { - messages = append(messages, fmt.Sprintf("status.versions changed from %q to %q", oldStatus.Versions, newStatus.Versions)) + if oldStatus != nil { + if !equality.Semantic.DeepEqual(oldStatus.RelatedObjects, newStatus.RelatedObjects) { + messages = append(messages, fmt.Sprintf("status.relatedObjects changed from %#v to %#v", oldStatus.RelatedObjects, newStatus.RelatedObjects)) + } + if !equality.Semantic.DeepEqual(oldStatus.Extension, newStatus.Extension) { + messages = append(messages, fmt.Sprintf("status.extension changed from %#v to %#v", oldStatus.Extension, newStatus.Extension)) + } + if !equality.Semantic.DeepEqual(oldStatus.Versions, newStatus.Versions) { + messages = append(messages, fmt.Sprintf("status.versions changed from %#v to %#v", oldStatus.Versions, newStatus.Versions)) + } } if len(messages) == 0 { diff --git a/vendor/github.com/openshift/library-go/pkg/manifestclient/default-discovery/README.md b/vendor/github.com/openshift/library-go/pkg/manifestclient/default-discovery/README.md new file mode 100644 index 000000000..3d8440e89 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/manifestclient/default-discovery/README.md @@ -0,0 +1,2 @@ +Just extracted from a random oc adm inspect. +It beats nothing for now. \ No newline at end of file diff --git a/vendor/github.com/openshift/library-go/pkg/manifestclient/default-discovery/aggregated-discovery-api.yaml b/vendor/github.com/openshift/library-go/pkg/manifestclient/default-discovery/aggregated-discovery-api.yaml new file mode 100644 index 000000000..dfe7a756d --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/manifestclient/default-discovery/aggregated-discovery-api.yaml @@ -0,0 +1,493 @@ +apiVersion: apidiscovery.k8s.io/v2 +items: +- metadata: + creationTimestamp: null + versions: + - freshness: Current + resources: + - resource: bindings + responseKind: + group: "" + kind: Binding + version: "" + scope: Namespaced + singularResource: binding + verbs: + - create + - resource: componentstatuses + responseKind: + group: "" + kind: ComponentStatus + version: "" + scope: Cluster + shortNames: + - cs + singularResource: componentstatus + verbs: + - get + - list + - resource: configmaps + responseKind: + group: "" + kind: ConfigMap + version: "" + scope: Namespaced + shortNames: + - cm + singularResource: configmap + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: endpoints + responseKind: + group: "" + kind: Endpoints + version: "" + scope: Namespaced + shortNames: + - ep + singularResource: endpoints + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: events + responseKind: + group: "" + kind: Event + version: "" + scope: Namespaced + shortNames: + - ev + singularResource: event + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: limitranges + responseKind: + group: "" + kind: LimitRange + version: "" + scope: Namespaced + shortNames: + - limits + singularResource: limitrange + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: namespaces + responseKind: + group: "" + kind: Namespace + version: "" + scope: Cluster + shortNames: + - ns + singularResource: namespace + subresources: + - responseKind: + group: "" + kind: Namespace + version: "" + subresource: finalize + verbs: + - update + - responseKind: + group: "" + kind: Namespace + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - resource: nodes + responseKind: + group: "" + kind: Node + version: "" + scope: Cluster + shortNames: + - "no" + singularResource: node + subresources: + - responseKind: + group: "" + kind: NodeProxyOptions + version: "" + subresource: proxy + verbs: + - create + - delete + - get + - patch + - update + - responseKind: + group: "" + kind: Node + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: persistentvolumeclaims + responseKind: + group: "" + kind: PersistentVolumeClaim + version: "" + scope: Namespaced + shortNames: + - pvc + singularResource: persistentvolumeclaim + subresources: + - responseKind: + group: "" + kind: PersistentVolumeClaim + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: persistentvolumes + responseKind: + group: "" + kind: PersistentVolume + version: "" + scope: Cluster + shortNames: + - pv + singularResource: persistentvolume + subresources: + - responseKind: + group: "" + kind: PersistentVolume + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - all + resource: pods + responseKind: + group: "" + kind: Pod + version: "" + scope: Namespaced + shortNames: + - po + singularResource: pod + subresources: + - responseKind: + group: "" + kind: PodAttachOptions + version: "" + subresource: attach + verbs: + - create + - get + - responseKind: + group: "" + kind: Binding + version: "" + subresource: binding + verbs: + - create + - responseKind: + group: "" + kind: Pod + version: "" + subresource: ephemeralcontainers + verbs: + - get + - patch + - update + - responseKind: + group: policy + kind: Eviction + version: v1 + subresource: eviction + verbs: + - create + - responseKind: + group: "" + kind: PodExecOptions + version: "" + subresource: exec + verbs: + - create + - get + - responseKind: + group: "" + kind: Pod + version: "" + subresource: log + verbs: + - get + - responseKind: + group: "" + kind: PodPortForwardOptions + version: "" + subresource: portforward + verbs: + - create + - get + - responseKind: + group: "" + kind: PodProxyOptions + version: "" + subresource: proxy + verbs: + - create + - delete + - get + - patch + - update + - responseKind: + group: "" + kind: Pod + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: podtemplates + responseKind: + group: "" + kind: PodTemplate + version: "" + scope: Namespaced + singularResource: podtemplate + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - all + resource: replicationcontrollers + responseKind: + group: "" + kind: ReplicationController + version: "" + scope: Namespaced + shortNames: + - rc + singularResource: replicationcontroller + subresources: + - responseKind: + group: autoscaling + kind: Scale + version: v1 + subresource: scale + verbs: + - get + - patch + - update + - responseKind: + group: "" + kind: ReplicationController + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: resourcequotas + responseKind: + group: "" + kind: ResourceQuota + version: "" + scope: Namespaced + shortNames: + - quota + singularResource: resourcequota + subresources: + - responseKind: + group: "" + kind: ResourceQuota + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: secrets + responseKind: + group: "" + kind: Secret + version: "" + scope: Namespaced + singularResource: secret + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: serviceaccounts + responseKind: + group: "" + kind: ServiceAccount + version: "" + scope: Namespaced + shortNames: + - sa + singularResource: serviceaccount + subresources: + - responseKind: + group: authentication.k8s.io + kind: TokenRequest + version: v1 + subresource: token + verbs: + - create + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - all + resource: services + responseKind: + group: "" + kind: Service + version: "" + scope: Namespaced + shortNames: + - svc + singularResource: service + subresources: + - responseKind: + group: "" + kind: ServiceProxyOptions + version: "" + subresource: proxy + verbs: + - create + - delete + - get + - patch + - update + - responseKind: + group: "" + kind: Service + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +kind: APIGroupDiscoveryList +metadata: {} diff --git a/vendor/github.com/openshift/library-go/pkg/manifestclient/default-discovery/aggregated-discovery-apis.yaml b/vendor/github.com/openshift/library-go/pkg/manifestclient/default-discovery/aggregated-discovery-apis.yaml new file mode 100644 index 000000000..50bb6dfb1 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/manifestclient/default-discovery/aggregated-discovery-apis.yaml @@ -0,0 +1,5720 @@ +apiVersion: apidiscovery.k8s.io/v2 +items: +- metadata: + creationTimestamp: null + name: apiregistration.k8s.io + versions: + - freshness: Current + resources: + - categories: + - api-extensions + resource: apiservices + responseKind: + group: "" + kind: APIService + version: "" + scope: Cluster + singularResource: apiservice + subresources: + - responseKind: + group: "" + kind: APIService + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: apps + versions: + - freshness: Current + resources: + - resource: controllerrevisions + responseKind: + group: "" + kind: ControllerRevision + version: "" + scope: Namespaced + singularResource: controllerrevision + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - all + resource: daemonsets + responseKind: + group: "" + kind: DaemonSet + version: "" + scope: Namespaced + shortNames: + - ds + singularResource: daemonset + subresources: + - responseKind: + group: "" + kind: DaemonSet + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - all + resource: deployments + responseKind: + group: "" + kind: Deployment + version: "" + scope: Namespaced + shortNames: + - deploy + singularResource: deployment + subresources: + - responseKind: + group: autoscaling + kind: Scale + version: v1 + subresource: scale + verbs: + - get + - patch + - update + - responseKind: + group: "" + kind: Deployment + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - all + resource: replicasets + responseKind: + group: "" + kind: ReplicaSet + version: "" + scope: Namespaced + shortNames: + - rs + singularResource: replicaset + subresources: + - responseKind: + group: autoscaling + kind: Scale + version: v1 + subresource: scale + verbs: + - get + - patch + - update + - responseKind: + group: "" + kind: ReplicaSet + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - all + resource: statefulsets + responseKind: + group: "" + kind: StatefulSet + version: "" + scope: Namespaced + shortNames: + - sts + singularResource: statefulset + subresources: + - responseKind: + group: autoscaling + kind: Scale + version: v1 + subresource: scale + verbs: + - get + - patch + - update + - responseKind: + group: "" + kind: StatefulSet + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: events.k8s.io + versions: + - freshness: Current + resources: + - resource: events + responseKind: + group: "" + kind: Event + version: "" + scope: Namespaced + shortNames: + - ev + singularResource: event + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: authentication.k8s.io + versions: + - freshness: Current + resources: + - resource: selfsubjectreviews + responseKind: + group: "" + kind: SelfSubjectReview + version: "" + scope: Cluster + singularResource: selfsubjectreview + verbs: + - create + - resource: tokenreviews + responseKind: + group: "" + kind: TokenReview + version: "" + scope: Cluster + singularResource: tokenreview + verbs: + - create + version: v1 +- metadata: + creationTimestamp: null + name: authorization.k8s.io + versions: + - freshness: Current + resources: + - resource: localsubjectaccessreviews + responseKind: + group: "" + kind: LocalSubjectAccessReview + version: "" + scope: Namespaced + singularResource: localsubjectaccessreview + verbs: + - create + - resource: selfsubjectaccessreviews + responseKind: + group: "" + kind: SelfSubjectAccessReview + version: "" + scope: Cluster + singularResource: selfsubjectaccessreview + verbs: + - create + - resource: selfsubjectrulesreviews + responseKind: + group: "" + kind: SelfSubjectRulesReview + version: "" + scope: Cluster + singularResource: selfsubjectrulesreview + verbs: + - create + - resource: subjectaccessreviews + responseKind: + group: "" + kind: SubjectAccessReview + version: "" + scope: Cluster + singularResource: subjectaccessreview + verbs: + - create + version: v1 +- metadata: + creationTimestamp: null + name: autoscaling + versions: + - freshness: Current + resources: + - categories: + - all + resource: horizontalpodautoscalers + responseKind: + group: "" + kind: HorizontalPodAutoscaler + version: "" + scope: Namespaced + shortNames: + - hpa + singularResource: horizontalpodautoscaler + subresources: + - responseKind: + group: "" + kind: HorizontalPodAutoscaler + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v2 + - freshness: Current + resources: + - categories: + - all + resource: horizontalpodautoscalers + responseKind: + group: "" + kind: HorizontalPodAutoscaler + version: "" + scope: Namespaced + shortNames: + - hpa + singularResource: horizontalpodautoscaler + subresources: + - responseKind: + group: "" + kind: HorizontalPodAutoscaler + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: batch + versions: + - freshness: Current + resources: + - categories: + - all + resource: cronjobs + responseKind: + group: "" + kind: CronJob + version: "" + scope: Namespaced + shortNames: + - cj + singularResource: cronjob + subresources: + - responseKind: + group: "" + kind: CronJob + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - all + resource: jobs + responseKind: + group: "" + kind: Job + version: "" + scope: Namespaced + singularResource: job + subresources: + - responseKind: + group: "" + kind: Job + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: certificates.k8s.io + versions: + - freshness: Current + resources: + - resource: certificatesigningrequests + responseKind: + group: "" + kind: CertificateSigningRequest + version: "" + scope: Cluster + shortNames: + - csr + singularResource: certificatesigningrequest + subresources: + - responseKind: + group: "" + kind: CertificateSigningRequest + version: "" + subresource: approval + verbs: + - get + - patch + - update + - responseKind: + group: "" + kind: CertificateSigningRequest + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: networking.k8s.io + versions: + - freshness: Current + resources: + - resource: ingressclasses + responseKind: + group: "" + kind: IngressClass + version: "" + scope: Cluster + singularResource: ingressclass + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: ingresses + responseKind: + group: "" + kind: Ingress + version: "" + scope: Namespaced + shortNames: + - ing + singularResource: ingress + subresources: + - responseKind: + group: "" + kind: Ingress + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: networkpolicies + responseKind: + group: "" + kind: NetworkPolicy + version: "" + scope: Namespaced + shortNames: + - netpol + singularResource: networkpolicy + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: policy + versions: + - freshness: Current + resources: + - resource: poddisruptionbudgets + responseKind: + group: "" + kind: PodDisruptionBudget + version: "" + scope: Namespaced + shortNames: + - pdb + singularResource: poddisruptionbudget + subresources: + - responseKind: + group: "" + kind: PodDisruptionBudget + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: rbac.authorization.k8s.io + versions: + - freshness: Current + resources: + - resource: clusterrolebindings + responseKind: + group: "" + kind: ClusterRoleBinding + version: "" + scope: Cluster + singularResource: clusterrolebinding + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: clusterroles + responseKind: + group: "" + kind: ClusterRole + version: "" + scope: Cluster + singularResource: clusterrole + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: rolebindings + responseKind: + group: "" + kind: RoleBinding + version: "" + scope: Namespaced + singularResource: rolebinding + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: roles + responseKind: + group: "" + kind: Role + version: "" + scope: Namespaced + singularResource: role + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: storage.k8s.io + versions: + - freshness: Current + resources: + - resource: csidrivers + responseKind: + group: "" + kind: CSIDriver + version: "" + scope: Cluster + singularResource: csidriver + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: csinodes + responseKind: + group: "" + kind: CSINode + version: "" + scope: Cluster + singularResource: csinode + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: csistoragecapacities + responseKind: + group: "" + kind: CSIStorageCapacity + version: "" + scope: Namespaced + singularResource: csistoragecapacity + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: storageclasses + responseKind: + group: "" + kind: StorageClass + version: "" + scope: Cluster + shortNames: + - sc + singularResource: storageclass + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: volumeattachments + responseKind: + group: "" + kind: VolumeAttachment + version: "" + scope: Cluster + singularResource: volumeattachment + subresources: + - responseKind: + group: "" + kind: VolumeAttachment + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: admissionregistration.k8s.io + versions: + - freshness: Current + resources: + - categories: + - api-extensions + resource: mutatingwebhookconfigurations + responseKind: + group: "" + kind: MutatingWebhookConfiguration + version: "" + scope: Cluster + singularResource: mutatingwebhookconfiguration + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - api-extensions + resource: validatingadmissionpolicies + responseKind: + group: "" + kind: ValidatingAdmissionPolicy + version: "" + scope: Cluster + singularResource: validatingadmissionpolicy + subresources: + - responseKind: + group: "" + kind: ValidatingAdmissionPolicy + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - api-extensions + resource: validatingadmissionpolicybindings + responseKind: + group: "" + kind: ValidatingAdmissionPolicyBinding + version: "" + scope: Cluster + singularResource: validatingadmissionpolicybinding + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - api-extensions + resource: validatingwebhookconfigurations + responseKind: + group: "" + kind: ValidatingWebhookConfiguration + version: "" + scope: Cluster + singularResource: validatingwebhookconfiguration + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 + - freshness: Current + resources: + - categories: + - api-extensions + resource: validatingadmissionpolicies + responseKind: + group: "" + kind: ValidatingAdmissionPolicy + version: "" + scope: Cluster + singularResource: validatingadmissionpolicy + subresources: + - responseKind: + group: "" + kind: ValidatingAdmissionPolicy + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - api-extensions + resource: validatingadmissionpolicybindings + responseKind: + group: "" + kind: ValidatingAdmissionPolicyBinding + version: "" + scope: Cluster + singularResource: validatingadmissionpolicybinding + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1beta1 +- metadata: + creationTimestamp: null + name: apiextensions.k8s.io + versions: + - freshness: Current + resources: + - categories: + - api-extensions + resource: customresourcedefinitions + responseKind: + group: "" + kind: CustomResourceDefinition + version: "" + scope: Cluster + shortNames: + - crd + - crds + singularResource: customresourcedefinition + subresources: + - responseKind: + group: "" + kind: CustomResourceDefinition + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: scheduling.k8s.io + versions: + - freshness: Current + resources: + - resource: priorityclasses + responseKind: + group: "" + kind: PriorityClass + version: "" + scope: Cluster + shortNames: + - pc + singularResource: priorityclass + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: coordination.k8s.io + versions: + - freshness: Current + resources: + - resource: leases + responseKind: + group: "" + kind: Lease + version: "" + scope: Namespaced + singularResource: lease + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: node.k8s.io + versions: + - freshness: Current + resources: + - resource: runtimeclasses + responseKind: + group: "" + kind: RuntimeClass + version: "" + scope: Cluster + singularResource: runtimeclass + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: discovery.k8s.io + versions: + - freshness: Current + resources: + - resource: endpointslices + responseKind: + group: "" + kind: EndpointSlice + version: "" + scope: Namespaced + singularResource: endpointslice + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: flowcontrol.apiserver.k8s.io + versions: + - freshness: Current + resources: + - resource: flowschemas + responseKind: + group: "" + kind: FlowSchema + version: "" + scope: Cluster + singularResource: flowschema + subresources: + - responseKind: + group: "" + kind: FlowSchema + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: prioritylevelconfigurations + responseKind: + group: "" + kind: PriorityLevelConfiguration + version: "" + scope: Cluster + singularResource: prioritylevelconfiguration + subresources: + - responseKind: + group: "" + kind: PriorityLevelConfiguration + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 + - freshness: Current + resources: + - resource: flowschemas + responseKind: + group: "" + kind: FlowSchema + version: "" + scope: Cluster + singularResource: flowschema + subresources: + - responseKind: + group: "" + kind: FlowSchema + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: prioritylevelconfigurations + responseKind: + group: "" + kind: PriorityLevelConfiguration + version: "" + scope: Cluster + singularResource: prioritylevelconfiguration + subresources: + - responseKind: + group: "" + kind: PriorityLevelConfiguration + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1beta3 +- metadata: + creationTimestamp: null + name: apps.openshift.io + versions: + - freshness: Current + resources: + - categories: + - all + resource: deploymentconfigs + responseKind: + group: "" + kind: DeploymentConfig + version: "" + scope: Namespaced + shortNames: + - dc + singularResource: deploymentconfig + subresources: + - responseKind: + group: "" + kind: DeploymentRequest + version: "" + subresource: instantiate + verbs: + - create + - responseKind: + group: "" + kind: DeploymentLog + version: "" + subresource: log + verbs: + - get + - responseKind: + group: "" + kind: DeploymentConfigRollback + version: "" + subresource: rollback + verbs: + - create + - responseKind: + group: extensions + kind: Scale + version: v1beta1 + subresource: scale + verbs: + - get + - patch + - update + - responseKind: + group: "" + kind: DeploymentConfig + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: authorization.openshift.io + versions: + - freshness: Current + resources: + - resource: clusterrolebindings + responseKind: + group: "" + kind: ClusterRoleBinding + version: "" + scope: Cluster + singularResource: clusterrolebinding + verbs: + - create + - delete + - get + - list + - patch + - update + - resource: clusterroles + responseKind: + group: "" + kind: ClusterRole + version: "" + scope: Cluster + singularResource: clusterrole + verbs: + - create + - delete + - get + - list + - patch + - update + - resource: localresourceaccessreviews + responseKind: + group: "" + kind: LocalResourceAccessReview + version: "" + scope: Namespaced + singularResource: localresourceaccessreview + verbs: + - create + - resource: localsubjectaccessreviews + responseKind: + group: "" + kind: LocalSubjectAccessReview + version: "" + scope: Namespaced + singularResource: localsubjectaccessreview + verbs: + - create + - resource: resourceaccessreviews + responseKind: + group: "" + kind: ResourceAccessReview + version: "" + scope: Cluster + singularResource: localresourceaccessreview + verbs: + - create + - resource: rolebindingrestrictions + responseKind: + group: "" + kind: RoleBindingRestriction + version: "" + scope: Namespaced + singularResource: rolebindingrestriction + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: rolebindings + responseKind: + group: "" + kind: RoleBinding + version: "" + scope: Namespaced + singularResource: rolebinding + verbs: + - create + - delete + - get + - list + - patch + - update + - resource: roles + responseKind: + group: "" + kind: Role + version: "" + scope: Namespaced + singularResource: role + verbs: + - create + - delete + - get + - list + - patch + - update + - resource: selfsubjectrulesreviews + responseKind: + group: "" + kind: SelfSubjectRulesReview + version: "" + scope: Namespaced + singularResource: selfsubjectrulesreview + verbs: + - create + - resource: subjectaccessreviews + responseKind: + group: "" + kind: SubjectAccessReview + version: "" + scope: Cluster + singularResource: subjectaccessreview + verbs: + - create + - resource: subjectrulesreviews + responseKind: + group: "" + kind: SubjectRulesReview + version: "" + scope: Namespaced + singularResource: subjectrulesreview + verbs: + - create + version: v1 +- metadata: + creationTimestamp: null + name: build.openshift.io + versions: + - freshness: Current + resources: + - categories: + - all + resource: buildconfigs + responseKind: + group: "" + kind: BuildConfig + version: "" + scope: Namespaced + shortNames: + - bc + singularResource: buildconfig + subresources: + - responseKind: + group: "" + kind: BuildRequest + version: "" + subresource: instantiate + verbs: + - create + - responseKind: + group: "" + kind: BinaryBuildRequestOptions + version: "" + subresource: instantiatebinary + verbs: + - create + - responseKind: + group: "" + kind: Build + version: "" + subresource: webhooks + verbs: + - create + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - categories: + - all + resource: builds + responseKind: + group: "" + kind: Build + version: "" + scope: Namespaced + singularResource: build + subresources: + - responseKind: + group: "" + kind: BuildRequest + version: "" + subresource: clone + verbs: + - create + - responseKind: + group: "" + kind: Build + version: "" + subresource: details + verbs: + - update + - responseKind: + group: "" + kind: BuildLog + version: "" + subresource: log + verbs: + - get + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: image.openshift.io + versions: + - freshness: Current + resources: + - resource: images + responseKind: + group: "" + kind: Image + version: "" + scope: Cluster + singularResource: image + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: imagesignatures + responseKind: + group: "" + kind: ImageSignature + version: "" + scope: Cluster + singularResource: imagesignature + verbs: + - create + - delete + - resource: imagestreamimages + responseKind: + group: "" + kind: ImageStreamImage + version: "" + scope: Namespaced + shortNames: + - isimage + singularResource: imagestreamimage + verbs: + - get + - resource: imagestreamimports + responseKind: + group: "" + kind: ImageStreamImport + version: "" + scope: Namespaced + singularResource: imagestreamimport + verbs: + - create + - resource: imagestreammappings + responseKind: + group: "" + kind: ImageStreamMapping + version: "" + scope: Namespaced + singularResource: imagestreammapping + verbs: + - create + - categories: + - all + resource: imagestreams + responseKind: + group: "" + kind: ImageStream + version: "" + scope: Namespaced + shortNames: + - is + singularResource: imagestream + subresources: + - responseKind: + group: "" + kind: ImageStreamLayers + version: "" + subresource: layers + verbs: + - get + - responseKind: + group: "" + kind: SecretList + version: "" + subresource: secrets + verbs: + - get + - responseKind: + group: "" + kind: ImageStream + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: imagestreamtags + responseKind: + group: "" + kind: ImageStreamTag + version: "" + scope: Namespaced + shortNames: + - istag + singularResource: imagestreamtag + verbs: + - create + - delete + - get + - list + - patch + - update + - resource: imagetags + responseKind: + group: "" + kind: ImageTag + version: "" + scope: Namespaced + shortNames: + - itag + singularResource: imagetag + verbs: + - create + - delete + - get + - list + - patch + - update + version: v1 +- metadata: + creationTimestamp: null + name: oauth.openshift.io + versions: + - freshness: Current + resources: + - resource: oauthaccesstokens + responseKind: + group: "" + kind: OAuthAccessToken + version: "" + scope: Cluster + singularResource: oauthaccesstoken + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: oauthauthorizetokens + responseKind: + group: "" + kind: OAuthAuthorizeToken + version: "" + scope: Cluster + singularResource: oauthauthorizetoken + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: oauthclientauthorizations + responseKind: + group: "" + kind: OAuthClientAuthorization + version: "" + scope: Cluster + singularResource: oauthclientauthorization + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: oauthclients + responseKind: + group: "" + kind: OAuthClient + version: "" + scope: Cluster + singularResource: oauthclient + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: tokenreviews + responseKind: + group: authentication.k8s.io + kind: TokenReview + version: v1 + scope: Cluster + singularResource: tokenreview + verbs: + - create + - resource: useroauthaccesstokens + responseKind: + group: "" + kind: UserOAuthAccessToken + version: "" + scope: Cluster + singularResource: useroauthaccesstoken + verbs: + - delete + - get + - list + - watch + version: v1 +- metadata: + creationTimestamp: null + name: project.openshift.io + versions: + - freshness: Current + resources: + - resource: projectrequests + responseKind: + group: "" + kind: ProjectRequest + version: "" + scope: Cluster + singularResource: projectrequest + verbs: + - create + - list + - resource: projects + responseKind: + group: "" + kind: Project + version: "" + scope: Cluster + singularResource: project + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: quota.openshift.io + versions: + - freshness: Current + resources: + - resource: appliedclusterresourcequotas + responseKind: + group: "" + kind: AppliedClusterResourceQuota + version: "" + scope: Namespaced + singularResource: appliedclusterresourcequota + verbs: + - get + - list + - resource: clusterresourcequotas + responseKind: + group: "" + kind: ClusterResourceQuota + version: "" + scope: Cluster + shortNames: + - clusterquota + singularResource: clusterresourcequota + subresources: + - responseKind: + group: "" + kind: ClusterResourceQuota + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: route.openshift.io + versions: + - freshness: Current + resources: + - categories: + - all + resource: routes + responseKind: + group: "" + kind: Route + version: "" + scope: Namespaced + singularResource: route + subresources: + - responseKind: + group: "" + kind: Route + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: security.openshift.io + versions: + - freshness: Current + resources: + - resource: podsecuritypolicyreviews + responseKind: + group: "" + kind: PodSecurityPolicyReview + version: "" + scope: Namespaced + singularResource: podsecuritypolicyreview + verbs: + - create + - resource: podsecuritypolicyselfsubjectreviews + responseKind: + group: "" + kind: PodSecurityPolicySelfSubjectReview + version: "" + scope: Namespaced + singularResource: podsecuritypolicyselfsubjectreview + verbs: + - create + - resource: podsecuritypolicysubjectreviews + responseKind: + group: "" + kind: PodSecurityPolicySubjectReview + version: "" + scope: Namespaced + singularResource: podsecuritypolicysubjectreview + verbs: + - create + - resource: rangeallocations + responseKind: + group: "" + kind: RangeAllocation + version: "" + scope: Cluster + singularResource: rangeallocation + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: securitycontextconstraints + responseKind: + group: "" + kind: SecurityContextConstraints + version: "" + scope: Cluster + shortNames: + - scc + singularResource: securitycontextconstraint + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: template.openshift.io + versions: + - freshness: Current + resources: + - resource: brokertemplateinstances + responseKind: + group: "" + kind: BrokerTemplateInstance + version: "" + scope: Cluster + singularResource: brokertemplateinstance + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: processedtemplates + responseKind: + group: "" + kind: Template + version: "" + scope: Namespaced + singularResource: processedtemplate + verbs: + - create + - resource: templateinstances + responseKind: + group: "" + kind: TemplateInstance + version: "" + scope: Namespaced + singularResource: templateinstance + subresources: + - responseKind: + group: "" + kind: TemplateInstance + version: "" + subresource: status + verbs: + - get + - patch + - update + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: templates + responseKind: + group: "" + kind: Template + version: "" + scope: Namespaced + singularResource: template + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: user.openshift.io + versions: + - freshness: Current + resources: + - resource: groups + responseKind: + group: "" + kind: Group + version: "" + scope: Cluster + singularResource: group + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: identities + responseKind: + group: "" + kind: Identity + version: "" + scope: Cluster + singularResource: identity + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - resource: useridentitymappings + responseKind: + group: "" + kind: UserIdentityMapping + version: "" + scope: Cluster + singularResource: useridentitymapping + verbs: + - create + - delete + - get + - patch + - update + - resource: users + responseKind: + group: "" + kind: User + version: "" + scope: Cluster + singularResource: user + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: packages.operators.coreos.com + versions: + - freshness: Current + resources: + - resource: packagemanifests + responseKind: + group: "" + kind: PackageManifest + version: "" + scope: Namespaced + singularResource: packagemanifest + subresources: + - responseKind: + group: "" + kind: PackageManifest + version: "" + subresource: icon + verbs: + - get + verbs: + - get + - list + version: v1 +- metadata: + creationTimestamp: null + name: apiserver.openshift.io + versions: + - freshness: Current + resources: + - resource: apirequestcounts + responseKind: + group: apiserver.openshift.io + kind: APIRequestCount + version: v1 + scope: Cluster + singularResource: apirequestcount + subresources: + - responseKind: + group: apiserver.openshift.io + kind: APIRequestCount + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: autoscaling.openshift.io + versions: + - freshness: Current + resources: + - resource: clusterautoscalers + responseKind: + group: autoscaling.openshift.io + kind: ClusterAutoscaler + version: v1 + scope: Cluster + shortNames: + - ca + singularResource: clusterautoscaler + subresources: + - responseKind: + group: autoscaling.openshift.io + kind: ClusterAutoscaler + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 + - freshness: Current + resources: + - resource: machineautoscalers + responseKind: + group: autoscaling.openshift.io + kind: MachineAutoscaler + version: v1beta1 + scope: Namespaced + shortNames: + - ma + singularResource: machineautoscaler + subresources: + - responseKind: + group: autoscaling.openshift.io + kind: MachineAutoscaler + version: v1beta1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1beta1 +- metadata: + creationTimestamp: null + name: autoscalinge2e.example.com + versions: + - freshness: Current + resources: + - resource: testcrds + responseKind: + group: autoscalinge2e.example.com + kind: TestCRD + version: v1 + scope: Namespaced + singularResource: testcrd + subresources: + - responseKind: + group: autoscaling + kind: Scale + version: v1 + subresource: scale + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: cloudcredential.openshift.io + versions: + - freshness: Current + resources: + - resource: credentialsrequests + responseKind: + group: cloudcredential.openshift.io + kind: CredentialsRequest + version: v1 + scope: Namespaced + singularResource: credentialsrequest + subresources: + - responseKind: + group: cloudcredential.openshift.io + kind: CredentialsRequest + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: config.openshift.io + versions: + - freshness: Current + resources: + - resource: apiservers + responseKind: + group: config.openshift.io + kind: APIServer + version: v1 + scope: Cluster + singularResource: apiserver + subresources: + - responseKind: + group: config.openshift.io + kind: APIServer + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: authentications + responseKind: + group: config.openshift.io + kind: Authentication + version: v1 + scope: Cluster + singularResource: authentication + subresources: + - responseKind: + group: config.openshift.io + kind: Authentication + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: builds + responseKind: + group: config.openshift.io + kind: Build + version: v1 + scope: Cluster + singularResource: build + subresources: + - responseKind: + group: config.openshift.io + kind: Build + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: clusteroperators + responseKind: + group: config.openshift.io + kind: ClusterOperator + version: v1 + scope: Cluster + shortNames: + - co + singularResource: clusteroperator + subresources: + - responseKind: + group: config.openshift.io + kind: ClusterOperator + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: clusterversions + responseKind: + group: config.openshift.io + kind: ClusterVersion + version: v1 + scope: Cluster + singularResource: clusterversion + subresources: + - responseKind: + group: config.openshift.io + kind: ClusterVersion + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: consoles + responseKind: + group: config.openshift.io + kind: Console + version: v1 + scope: Cluster + singularResource: console + subresources: + - responseKind: + group: config.openshift.io + kind: Console + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: dnses + responseKind: + group: config.openshift.io + kind: DNS + version: v1 + scope: Cluster + singularResource: dns + subresources: + - responseKind: + group: config.openshift.io + kind: DNS + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: featuregates + responseKind: + group: config.openshift.io + kind: FeatureGate + version: v1 + scope: Cluster + singularResource: featuregate + subresources: + - responseKind: + group: config.openshift.io + kind: FeatureGate + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: imagecontentpolicies + responseKind: + group: config.openshift.io + kind: ImageContentPolicy + version: v1 + scope: Cluster + singularResource: imagecontentpolicy + subresources: + - responseKind: + group: config.openshift.io + kind: ImageContentPolicy + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: imagedigestmirrorsets + responseKind: + group: config.openshift.io + kind: ImageDigestMirrorSet + version: v1 + scope: Cluster + shortNames: + - idms + singularResource: imagedigestmirrorset + subresources: + - responseKind: + group: config.openshift.io + kind: ImageDigestMirrorSet + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: images + responseKind: + group: config.openshift.io + kind: Image + version: v1 + scope: Cluster + singularResource: image + subresources: + - responseKind: + group: config.openshift.io + kind: Image + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: imagetagmirrorsets + responseKind: + group: config.openshift.io + kind: ImageTagMirrorSet + version: v1 + scope: Cluster + shortNames: + - itms + singularResource: imagetagmirrorset + subresources: + - responseKind: + group: config.openshift.io + kind: ImageTagMirrorSet + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: infrastructures + responseKind: + group: config.openshift.io + kind: Infrastructure + version: v1 + scope: Cluster + singularResource: infrastructure + subresources: + - responseKind: + group: config.openshift.io + kind: Infrastructure + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: ingresses + responseKind: + group: config.openshift.io + kind: Ingress + version: v1 + scope: Cluster + singularResource: ingress + subresources: + - responseKind: + group: config.openshift.io + kind: Ingress + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: networks + responseKind: + group: config.openshift.io + kind: Network + version: v1 + scope: Cluster + singularResource: network + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: nodes + responseKind: + group: config.openshift.io + kind: Node + version: v1 + scope: Cluster + singularResource: node + subresources: + - responseKind: + group: config.openshift.io + kind: Node + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: oauths + responseKind: + group: config.openshift.io + kind: OAuth + version: v1 + scope: Cluster + singularResource: oauth + subresources: + - responseKind: + group: config.openshift.io + kind: OAuth + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: operatorhubs + responseKind: + group: config.openshift.io + kind: OperatorHub + version: v1 + scope: Cluster + singularResource: operatorhub + subresources: + - responseKind: + group: config.openshift.io + kind: OperatorHub + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: projects + responseKind: + group: config.openshift.io + kind: Project + version: v1 + scope: Cluster + singularResource: project + subresources: + - responseKind: + group: config.openshift.io + kind: Project + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: proxies + responseKind: + group: config.openshift.io + kind: Proxy + version: v1 + scope: Cluster + singularResource: proxy + subresources: + - responseKind: + group: config.openshift.io + kind: Proxy + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: schedulers + responseKind: + group: config.openshift.io + kind: Scheduler + version: v1 + scope: Cluster + singularResource: scheduler + subresources: + - responseKind: + group: config.openshift.io + kind: Scheduler + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: console.openshift.io + versions: + - freshness: Current + resources: + - resource: consoleclidownloads + responseKind: + group: console.openshift.io + kind: ConsoleCLIDownload + version: v1 + scope: Cluster + singularResource: consoleclidownload + subresources: + - responseKind: + group: console.openshift.io + kind: ConsoleCLIDownload + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: consoleexternalloglinks + responseKind: + group: console.openshift.io + kind: ConsoleExternalLogLink + version: v1 + scope: Cluster + singularResource: consoleexternalloglink + subresources: + - responseKind: + group: console.openshift.io + kind: ConsoleExternalLogLink + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: consolelinks + responseKind: + group: console.openshift.io + kind: ConsoleLink + version: v1 + scope: Cluster + singularResource: consolelink + subresources: + - responseKind: + group: console.openshift.io + kind: ConsoleLink + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: consolenotifications + responseKind: + group: console.openshift.io + kind: ConsoleNotification + version: v1 + scope: Cluster + singularResource: consolenotification + subresources: + - responseKind: + group: console.openshift.io + kind: ConsoleNotification + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: consoleplugins + responseKind: + group: console.openshift.io + kind: ConsolePlugin + version: v1 + scope: Cluster + singularResource: consoleplugin + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: consolequickstarts + responseKind: + group: console.openshift.io + kind: ConsoleQuickStart + version: v1 + scope: Cluster + singularResource: consolequickstart + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: consolesamples + responseKind: + group: console.openshift.io + kind: ConsoleSample + version: v1 + scope: Cluster + singularResource: consolesample + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: consoleyamlsamples + responseKind: + group: console.openshift.io + kind: ConsoleYAMLSample + version: v1 + scope: Cluster + singularResource: consoleyamlsample + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 + - freshness: Current + resources: + - resource: consoleplugins + responseKind: + group: console.openshift.io + kind: ConsolePlugin + version: v1alpha1 + scope: Cluster + singularResource: consoleplugin + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha1 +- metadata: + creationTimestamp: null + name: controlplane.operator.openshift.io + versions: + - freshness: Current + resources: + - resource: podnetworkconnectivitychecks + responseKind: + group: controlplane.operator.openshift.io + kind: PodNetworkConnectivityCheck + version: v1alpha1 + scope: Namespaced + singularResource: podnetworkconnectivitycheck + subresources: + - responseKind: + group: controlplane.operator.openshift.io + kind: PodNetworkConnectivityCheck + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha1 +- metadata: + creationTimestamp: null + name: helm.openshift.io + versions: + - freshness: Current + resources: + - resource: helmchartrepositories + responseKind: + group: helm.openshift.io + kind: HelmChartRepository + version: v1beta1 + scope: Cluster + singularResource: helmchartrepository + subresources: + - responseKind: + group: helm.openshift.io + kind: HelmChartRepository + version: v1beta1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: projecthelmchartrepositories + responseKind: + group: helm.openshift.io + kind: ProjectHelmChartRepository + version: v1beta1 + scope: Namespaced + singularResource: projecthelmchartrepository + subresources: + - responseKind: + group: helm.openshift.io + kind: ProjectHelmChartRepository + version: v1beta1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1beta1 +- metadata: + creationTimestamp: null + name: imageregistry.operator.openshift.io + versions: + - freshness: Current + resources: + - resource: configs + responseKind: + group: imageregistry.operator.openshift.io + kind: Config + version: v1 + scope: Cluster + singularResource: config + subresources: + - responseKind: + group: imageregistry.operator.openshift.io + kind: Config + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: imagepruners + responseKind: + group: imageregistry.operator.openshift.io + kind: ImagePruner + version: v1 + scope: Cluster + singularResource: imagepruner + subresources: + - responseKind: + group: imageregistry.operator.openshift.io + kind: ImagePruner + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: infrastructure.cluster.x-k8s.io + versions: + - freshness: Current + resources: + - categories: + - cluster-api + resource: metal3remediations + responseKind: + group: infrastructure.cluster.x-k8s.io + kind: Metal3Remediation + version: v1beta1 + scope: Namespaced + shortNames: + - m3r + - m3remediation + singularResource: metal3remediation + subresources: + - responseKind: + group: infrastructure.cluster.x-k8s.io + kind: Metal3Remediation + version: v1beta1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - cluster-api + resource: metal3remediationtemplates + responseKind: + group: infrastructure.cluster.x-k8s.io + kind: Metal3RemediationTemplate + version: v1beta1 + scope: Namespaced + shortNames: + - m3rt + - m3remediationtemplate + - m3remediationtemplates + - metal3rt + - metal3remediationtemplate + singularResource: metal3remediationtemplate + subresources: + - responseKind: + group: infrastructure.cluster.x-k8s.io + kind: Metal3RemediationTemplate + version: v1beta1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1beta1 + - freshness: Current + resources: + - categories: + - cluster-api + resource: metal3remediations + responseKind: + group: infrastructure.cluster.x-k8s.io + kind: Metal3Remediation + version: v1alpha5 + scope: Namespaced + shortNames: + - m3r + - m3remediation + singularResource: metal3remediation + subresources: + - responseKind: + group: infrastructure.cluster.x-k8s.io + kind: Metal3Remediation + version: v1alpha5 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - cluster-api + resource: metal3remediationtemplates + responseKind: + group: infrastructure.cluster.x-k8s.io + kind: Metal3RemediationTemplate + version: v1alpha5 + scope: Namespaced + shortNames: + - m3rt + - m3remediationtemplate + - m3remediationtemplates + - metal3rt + - metal3remediationtemplate + singularResource: metal3remediationtemplate + subresources: + - responseKind: + group: infrastructure.cluster.x-k8s.io + kind: Metal3RemediationTemplate + version: v1alpha5 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha5 +- metadata: + creationTimestamp: null + name: ingress.operator.openshift.io + versions: + - freshness: Current + resources: + - resource: dnsrecords + responseKind: + group: ingress.operator.openshift.io + kind: DNSRecord + version: v1 + scope: Namespaced + singularResource: dnsrecord + subresources: + - responseKind: + group: ingress.operator.openshift.io + kind: DNSRecord + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: ipam.cluster.x-k8s.io + versions: + - freshness: Current + resources: + - categories: + - cluster-api + resource: ipaddressclaims + responseKind: + group: ipam.cluster.x-k8s.io + kind: IPAddressClaim + version: v1beta1 + scope: Namespaced + singularResource: ipaddressclaim + subresources: + - responseKind: + group: ipam.cluster.x-k8s.io + kind: IPAddressClaim + version: v1beta1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - cluster-api + resource: ipaddresses + responseKind: + group: ipam.cluster.x-k8s.io + kind: IPAddress + version: v1beta1 + scope: Namespaced + singularResource: ipaddress + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1beta1 + - freshness: Current + resources: + - categories: + - cluster-api + resource: ipaddressclaims + responseKind: + group: ipam.cluster.x-k8s.io + kind: IPAddressClaim + version: v1alpha1 + scope: Namespaced + singularResource: ipaddressclaim + subresources: + - responseKind: + group: ipam.cluster.x-k8s.io + kind: IPAddressClaim + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - cluster-api + resource: ipaddresses + responseKind: + group: ipam.cluster.x-k8s.io + kind: IPAddress + version: v1alpha1 + scope: Namespaced + singularResource: ipaddress + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha1 +- metadata: + creationTimestamp: null + name: k8s.cni.cncf.io + versions: + - freshness: Current + resources: + - resource: network-attachment-definitions + responseKind: + group: k8s.cni.cncf.io + kind: NetworkAttachmentDefinition + version: v1 + scope: Namespaced + shortNames: + - net-attach-def + singularResource: network-attachment-definition + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: k8s.ovn.org + versions: + - freshness: Current + resources: + - resource: adminpolicybasedexternalroutes + responseKind: + group: k8s.ovn.org + kind: AdminPolicyBasedExternalRoute + version: v1 + scope: Cluster + shortNames: + - apbexternalroute + singularResource: adminpolicybasedexternalroute + subresources: + - responseKind: + group: k8s.ovn.org + kind: AdminPolicyBasedExternalRoute + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: egressfirewalls + responseKind: + group: k8s.ovn.org + kind: EgressFirewall + version: v1 + scope: Namespaced + singularResource: egressfirewall + subresources: + - responseKind: + group: k8s.ovn.org + kind: EgressFirewall + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: egressips + responseKind: + group: k8s.ovn.org + kind: EgressIP + version: v1 + scope: Cluster + shortNames: + - eip + singularResource: egressip + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: egressqoses + responseKind: + group: k8s.ovn.org + kind: EgressQoS + version: v1 + scope: Namespaced + singularResource: egressqos + subresources: + - responseKind: + group: k8s.ovn.org + kind: EgressQoS + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: egressservices + responseKind: + group: k8s.ovn.org + kind: EgressService + version: v1 + scope: Namespaced + singularResource: egressservice + subresources: + - responseKind: + group: k8s.ovn.org + kind: EgressService + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: machine.openshift.io + versions: + - freshness: Current + resources: + - resource: controlplanemachinesets + responseKind: + group: machine.openshift.io + kind: ControlPlaneMachineSet + version: v1 + scope: Namespaced + singularResource: controlplanemachineset + subresources: + - responseKind: + group: machine.openshift.io + kind: ControlPlaneMachineSet + version: v1 + subresource: status + verbs: + - get + - patch + - update + - responseKind: + group: autoscaling + kind: Scale + version: v1 + subresource: scale + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 + - freshness: Current + resources: + - resource: machinehealthchecks + responseKind: + group: machine.openshift.io + kind: MachineHealthCheck + version: v1beta1 + scope: Namespaced + shortNames: + - mhc + - mhcs + singularResource: machinehealthcheck + subresources: + - responseKind: + group: machine.openshift.io + kind: MachineHealthCheck + version: v1beta1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: machines + responseKind: + group: machine.openshift.io + kind: Machine + version: v1beta1 + scope: Namespaced + singularResource: machine + subresources: + - responseKind: + group: machine.openshift.io + kind: Machine + version: v1beta1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: machinesets + responseKind: + group: machine.openshift.io + kind: MachineSet + version: v1beta1 + scope: Namespaced + singularResource: machineset + subresources: + - responseKind: + group: machine.openshift.io + kind: MachineSet + version: v1beta1 + subresource: status + verbs: + - get + - patch + - update + - responseKind: + group: autoscaling + kind: Scale + version: v1 + subresource: scale + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1beta1 +- metadata: + creationTimestamp: null + name: machineconfiguration.openshift.io + versions: + - freshness: Current + resources: + - resource: containerruntimeconfigs + responseKind: + group: machineconfiguration.openshift.io + kind: ContainerRuntimeConfig + version: v1 + scope: Cluster + shortNames: + - ctrcfg + singularResource: containerruntimeconfig + subresources: + - responseKind: + group: machineconfiguration.openshift.io + kind: ContainerRuntimeConfig + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: controllerconfigs + responseKind: + group: machineconfiguration.openshift.io + kind: ControllerConfig + version: v1 + scope: Cluster + singularResource: controllerconfig + subresources: + - responseKind: + group: machineconfiguration.openshift.io + kind: ControllerConfig + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: kubeletconfigs + responseKind: + group: machineconfiguration.openshift.io + kind: KubeletConfig + version: v1 + scope: Cluster + singularResource: kubeletconfig + subresources: + - responseKind: + group: machineconfiguration.openshift.io + kind: KubeletConfig + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: machineconfigpools + responseKind: + group: machineconfiguration.openshift.io + kind: MachineConfigPool + version: v1 + scope: Cluster + shortNames: + - mcp + singularResource: machineconfigpool + subresources: + - responseKind: + group: machineconfiguration.openshift.io + kind: MachineConfigPool + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: machineconfigs + responseKind: + group: machineconfiguration.openshift.io + kind: MachineConfig + version: v1 + scope: Cluster + shortNames: + - mc + singularResource: machineconfig + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: metal3.io + versions: + - freshness: Current + resources: + - resource: baremetalhosts + responseKind: + group: metal3.io + kind: BareMetalHost + version: v1alpha1 + scope: Namespaced + shortNames: + - bmh + - bmhost + singularResource: baremetalhost + subresources: + - responseKind: + group: metal3.io + kind: BareMetalHost + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: bmceventsubscriptions + responseKind: + group: metal3.io + kind: BMCEventSubscription + version: v1alpha1 + scope: Namespaced + shortNames: + - bes + - bmcevent + singularResource: bmceventsubscription + subresources: + - responseKind: + group: metal3.io + kind: BMCEventSubscription + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: dataimages + responseKind: + group: metal3.io + kind: DataImage + version: v1alpha1 + scope: Namespaced + singularResource: dataimage + subresources: + - responseKind: + group: metal3.io + kind: DataImage + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: firmwareschemas + responseKind: + group: metal3.io + kind: FirmwareSchema + version: v1alpha1 + scope: Namespaced + singularResource: firmwareschema + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: hardwaredata + responseKind: + group: metal3.io + kind: HardwareData + version: v1alpha1 + scope: Namespaced + shortNames: + - hd + singularResource: hardwaredata + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: hostfirmwarecomponents + responseKind: + group: metal3.io + kind: HostFirmwareComponents + version: v1alpha1 + scope: Namespaced + singularResource: hostfirmwarecomponents + subresources: + - responseKind: + group: metal3.io + kind: HostFirmwareComponents + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: hostfirmwaresettings + responseKind: + group: metal3.io + kind: HostFirmwareSettings + version: v1alpha1 + scope: Namespaced + shortNames: + - hfs + singularResource: hostfirmwaresettings + subresources: + - responseKind: + group: metal3.io + kind: HostFirmwareSettings + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: hostupdatepolicies + responseKind: + group: metal3.io + kind: HostUpdatePolicy + version: v1alpha1 + scope: Namespaced + singularResource: hostupdatepolicy + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: preprovisioningimages + responseKind: + group: metal3.io + kind: PreprovisioningImage + version: v1alpha1 + scope: Namespaced + shortNames: + - ppimg + singularResource: preprovisioningimage + subresources: + - responseKind: + group: metal3.io + kind: PreprovisioningImage + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: provisionings + responseKind: + group: metal3.io + kind: Provisioning + version: v1alpha1 + scope: Cluster + singularResource: provisioning + subresources: + - responseKind: + group: metal3.io + kind: Provisioning + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha1 +- metadata: + creationTimestamp: null + name: migration.k8s.io + versions: + - freshness: Current + resources: + - resource: storagestates + responseKind: + group: migration.k8s.io + kind: StorageState + version: v1alpha1 + scope: Cluster + singularResource: storagestate + subresources: + - responseKind: + group: migration.k8s.io + kind: StorageState + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: storageversionmigrations + responseKind: + group: migration.k8s.io + kind: StorageVersionMigration + version: v1alpha1 + scope: Cluster + singularResource: storageversionmigration + subresources: + - responseKind: + group: migration.k8s.io + kind: StorageVersionMigration + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha1 +- metadata: + creationTimestamp: null + name: monitoring.coreos.com + versions: + - freshness: Current + resources: + - categories: + - prometheus-operator + resource: alertmanagers + responseKind: + group: monitoring.coreos.com + kind: Alertmanager + version: v1 + scope: Namespaced + shortNames: + - am + singularResource: alertmanager + subresources: + - responseKind: + group: monitoring.coreos.com + kind: Alertmanager + version: v1 + subresource: status + verbs: + - get + - patch + - update + - responseKind: + group: autoscaling + kind: Scale + version: v1 + subresource: scale + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - prometheus-operator + resource: podmonitors + responseKind: + group: monitoring.coreos.com + kind: PodMonitor + version: v1 + scope: Namespaced + shortNames: + - pmon + singularResource: podmonitor + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - prometheus-operator + resource: probes + responseKind: + group: monitoring.coreos.com + kind: Probe + version: v1 + scope: Namespaced + shortNames: + - prb + singularResource: probe + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - prometheus-operator + resource: prometheuses + responseKind: + group: monitoring.coreos.com + kind: Prometheus + version: v1 + scope: Namespaced + shortNames: + - prom + singularResource: prometheus + subresources: + - responseKind: + group: monitoring.coreos.com + kind: Prometheus + version: v1 + subresource: status + verbs: + - get + - patch + - update + - responseKind: + group: autoscaling + kind: Scale + version: v1 + subresource: scale + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - prometheus-operator + resource: prometheusrules + responseKind: + group: monitoring.coreos.com + kind: PrometheusRule + version: v1 + scope: Namespaced + shortNames: + - promrule + singularResource: prometheusrule + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - prometheus-operator + resource: servicemonitors + responseKind: + group: monitoring.coreos.com + kind: ServiceMonitor + version: v1 + scope: Namespaced + shortNames: + - smon + singularResource: servicemonitor + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - prometheus-operator + resource: thanosrulers + responseKind: + group: monitoring.coreos.com + kind: ThanosRuler + version: v1 + scope: Namespaced + shortNames: + - ruler + singularResource: thanosruler + subresources: + - responseKind: + group: monitoring.coreos.com + kind: ThanosRuler + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 + - freshness: Current + resources: + - categories: + - prometheus-operator + resource: alertmanagerconfigs + responseKind: + group: monitoring.coreos.com + kind: AlertmanagerConfig + version: v1beta1 + scope: Namespaced + shortNames: + - amcfg + singularResource: alertmanagerconfig + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1beta1 + - freshness: Current + resources: + - categories: + - prometheus-operator + resource: alertmanagerconfigs + responseKind: + group: monitoring.coreos.com + kind: AlertmanagerConfig + version: v1alpha1 + scope: Namespaced + shortNames: + - amcfg + singularResource: alertmanagerconfig + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha1 +- metadata: + creationTimestamp: null + name: monitoring.openshift.io + versions: + - freshness: Current + resources: + - resource: alertingrules + responseKind: + group: monitoring.openshift.io + kind: AlertingRule + version: v1 + scope: Namespaced + singularResource: alertingrule + subresources: + - responseKind: + group: monitoring.openshift.io + kind: AlertingRule + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: alertrelabelconfigs + responseKind: + group: monitoring.openshift.io + kind: AlertRelabelConfig + version: v1 + scope: Namespaced + singularResource: alertrelabelconfig + subresources: + - responseKind: + group: monitoring.openshift.io + kind: AlertRelabelConfig + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: network.operator.openshift.io + versions: + - freshness: Current + resources: + - resource: egressrouters + responseKind: + group: network.operator.openshift.io + kind: EgressRouter + version: v1 + scope: Namespaced + singularResource: egressrouter + subresources: + - responseKind: + group: network.operator.openshift.io + kind: EgressRouter + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: operatorpkis + responseKind: + group: network.operator.openshift.io + kind: OperatorPKI + version: v1 + scope: Namespaced + singularResource: operatorpki + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: operator.openshift.io + versions: + - freshness: Current + resources: + - resource: authentications + responseKind: + group: operator.openshift.io + kind: Authentication + version: v1 + scope: Cluster + singularResource: authentication + subresources: + - responseKind: + group: operator.openshift.io + kind: Authentication + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: cloudcredentials + responseKind: + group: operator.openshift.io + kind: CloudCredential + version: v1 + scope: Cluster + singularResource: cloudcredential + subresources: + - responseKind: + group: operator.openshift.io + kind: CloudCredential + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: clustercsidrivers + responseKind: + group: operator.openshift.io + kind: ClusterCSIDriver + version: v1 + scope: Cluster + singularResource: clustercsidriver + subresources: + - responseKind: + group: operator.openshift.io + kind: ClusterCSIDriver + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - coreoperators + resource: configs + responseKind: + group: operator.openshift.io + kind: Config + version: v1 + scope: Cluster + singularResource: config + subresources: + - responseKind: + group: operator.openshift.io + kind: Config + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: consoles + responseKind: + group: operator.openshift.io + kind: Console + version: v1 + scope: Cluster + singularResource: console + subresources: + - responseKind: + group: operator.openshift.io + kind: Console + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: csisnapshotcontrollers + responseKind: + group: operator.openshift.io + kind: CSISnapshotController + version: v1 + scope: Cluster + singularResource: csisnapshotcontroller + subresources: + - responseKind: + group: operator.openshift.io + kind: CSISnapshotController + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: dnses + responseKind: + group: operator.openshift.io + kind: DNS + version: v1 + scope: Cluster + singularResource: dns + subresources: + - responseKind: + group: operator.openshift.io + kind: DNS + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - coreoperators + resource: etcds + responseKind: + group: operator.openshift.io + kind: Etcd + version: v1 + scope: Cluster + singularResource: etcd + subresources: + - responseKind: + group: operator.openshift.io + kind: Etcd + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: ingresscontrollers + responseKind: + group: operator.openshift.io + kind: IngressController + version: v1 + scope: Namespaced + singularResource: ingresscontroller + subresources: + - responseKind: + group: operator.openshift.io + kind: IngressController + version: v1 + subresource: status + verbs: + - get + - patch + - update + - responseKind: + group: autoscaling + kind: Scale + version: v1 + subresource: scale + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: insightsoperators + responseKind: + group: operator.openshift.io + kind: InsightsOperator + version: v1 + scope: Cluster + singularResource: insightsoperator + subresources: + - responseKind: + group: operator.openshift.io + kind: InsightsOperator + version: v1 + subresource: status + verbs: + - get + - patch + - update + - responseKind: + group: autoscaling + kind: Scale + version: v1 + subresource: scale + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - coreoperators + resource: kubeapiservers + responseKind: + group: operator.openshift.io + kind: KubeAPIServer + version: v1 + scope: Cluster + singularResource: kubeapiserver + subresources: + - responseKind: + group: operator.openshift.io + kind: KubeAPIServer + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - coreoperators + resource: kubecontrollermanagers + responseKind: + group: operator.openshift.io + kind: KubeControllerManager + version: v1 + scope: Cluster + singularResource: kubecontrollermanager + subresources: + - responseKind: + group: operator.openshift.io + kind: KubeControllerManager + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - coreoperators + resource: kubeschedulers + responseKind: + group: operator.openshift.io + kind: KubeScheduler + version: v1 + scope: Cluster + singularResource: kubescheduler + subresources: + - responseKind: + group: operator.openshift.io + kind: KubeScheduler + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: kubestorageversionmigrators + responseKind: + group: operator.openshift.io + kind: KubeStorageVersionMigrator + version: v1 + scope: Cluster + singularResource: kubestorageversionmigrator + subresources: + - responseKind: + group: operator.openshift.io + kind: KubeStorageVersionMigrator + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: machineconfigurations + responseKind: + group: operator.openshift.io + kind: MachineConfiguration + version: v1 + scope: Cluster + singularResource: machineconfiguration + subresources: + - responseKind: + group: operator.openshift.io + kind: MachineConfiguration + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: networks + responseKind: + group: operator.openshift.io + kind: Network + version: v1 + scope: Cluster + singularResource: network + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - coreoperators + resource: openshiftapiservers + responseKind: + group: operator.openshift.io + kind: OpenShiftAPIServer + version: v1 + scope: Cluster + singularResource: openshiftapiserver + subresources: + - responseKind: + group: operator.openshift.io + kind: OpenShiftAPIServer + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - coreoperators + resource: openshiftcontrollermanagers + responseKind: + group: operator.openshift.io + kind: OpenShiftControllerManager + version: v1 + scope: Cluster + singularResource: openshiftcontrollermanager + subresources: + - responseKind: + group: operator.openshift.io + kind: OpenShiftControllerManager + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: servicecas + responseKind: + group: operator.openshift.io + kind: ServiceCA + version: v1 + scope: Cluster + singularResource: serviceca + subresources: + - responseKind: + group: operator.openshift.io + kind: ServiceCA + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: storages + responseKind: + group: operator.openshift.io + kind: Storage + version: v1 + scope: Cluster + singularResource: storage + subresources: + - responseKind: + group: operator.openshift.io + kind: Storage + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 + - freshness: Current + resources: + - resource: imagecontentsourcepolicies + responseKind: + group: operator.openshift.io + kind: ImageContentSourcePolicy + version: v1alpha1 + scope: Cluster + singularResource: imagecontentsourcepolicy + subresources: + - responseKind: + group: operator.openshift.io + kind: ImageContentSourcePolicy + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha1 +- metadata: + creationTimestamp: null + name: operators.coreos.com + versions: + - freshness: Current + resources: + - categories: + - olm + resource: operatorconditions + responseKind: + group: operators.coreos.com + kind: OperatorCondition + version: v2 + scope: Namespaced + shortNames: + - condition + singularResource: operatorcondition + subresources: + - responseKind: + group: operators.coreos.com + kind: OperatorCondition + version: v2 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v2 + - freshness: Current + resources: + - categories: + - olm + resource: olmconfigs + responseKind: + group: operators.coreos.com + kind: OLMConfig + version: v1 + scope: Cluster + singularResource: olmconfig + subresources: + - responseKind: + group: operators.coreos.com + kind: OLMConfig + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - olm + resource: operatorconditions + responseKind: + group: operators.coreos.com + kind: OperatorCondition + version: v1 + scope: Namespaced + shortNames: + - condition + singularResource: operatorcondition + subresources: + - responseKind: + group: operators.coreos.com + kind: OperatorCondition + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - olm + resource: operatorgroups + responseKind: + group: operators.coreos.com + kind: OperatorGroup + version: v1 + scope: Namespaced + shortNames: + - og + singularResource: operatorgroup + subresources: + - responseKind: + group: operators.coreos.com + kind: OperatorGroup + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - olm + resource: operators + responseKind: + group: operators.coreos.com + kind: Operator + version: v1 + scope: Cluster + singularResource: operator + subresources: + - responseKind: + group: operators.coreos.com + kind: Operator + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 + - freshness: Current + resources: + - categories: + - olm + resource: operatorgroups + responseKind: + group: operators.coreos.com + kind: OperatorGroup + version: v1alpha2 + scope: Namespaced + shortNames: + - og + singularResource: operatorgroup + subresources: + - responseKind: + group: operators.coreos.com + kind: OperatorGroup + version: v1alpha2 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha2 + - freshness: Current + resources: + - categories: + - olm + resource: catalogsources + responseKind: + group: operators.coreos.com + kind: CatalogSource + version: v1alpha1 + scope: Namespaced + shortNames: + - catsrc + singularResource: catalogsource + subresources: + - responseKind: + group: operators.coreos.com + kind: CatalogSource + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - olm + resource: clusterserviceversions + responseKind: + group: operators.coreos.com + kind: ClusterServiceVersion + version: v1alpha1 + scope: Namespaced + shortNames: + - csv + - csvs + singularResource: clusterserviceversion + subresources: + - responseKind: + group: operators.coreos.com + kind: ClusterServiceVersion + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - olm + resource: installplans + responseKind: + group: operators.coreos.com + kind: InstallPlan + version: v1alpha1 + scope: Namespaced + shortNames: + - ip + singularResource: installplan + subresources: + - responseKind: + group: operators.coreos.com + kind: InstallPlan + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - categories: + - olm + resource: subscriptions + responseKind: + group: operators.coreos.com + kind: Subscription + version: v1alpha1 + scope: Namespaced + shortNames: + - sub + - subs + singularResource: subscription + subresources: + - responseKind: + group: operators.coreos.com + kind: Subscription + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha1 +- metadata: + creationTimestamp: null + name: performance.openshift.io + versions: + - freshness: Current + resources: + - resource: performanceprofiles + responseKind: + group: performance.openshift.io + kind: PerformanceProfile + version: v2 + scope: Cluster + singularResource: performanceprofile + subresources: + - responseKind: + group: performance.openshift.io + kind: PerformanceProfile + version: v2 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v2 + - freshness: Current + resources: + - resource: performanceprofiles + responseKind: + group: performance.openshift.io + kind: PerformanceProfile + version: v1 + scope: Cluster + singularResource: performanceprofile + subresources: + - responseKind: + group: performance.openshift.io + kind: PerformanceProfile + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 + - freshness: Current + resources: + - resource: performanceprofiles + responseKind: + group: performance.openshift.io + kind: PerformanceProfile + version: v1alpha1 + scope: Cluster + singularResource: performanceprofile + subresources: + - responseKind: + group: performance.openshift.io + kind: PerformanceProfile + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha1 +- metadata: + creationTimestamp: null + name: policy.networking.k8s.io + versions: + - freshness: Current + resources: + - resource: adminnetworkpolicies + responseKind: + group: policy.networking.k8s.io + kind: AdminNetworkPolicy + version: v1alpha1 + scope: Cluster + shortNames: + - anp + singularResource: adminnetworkpolicy + subresources: + - responseKind: + group: policy.networking.k8s.io + kind: AdminNetworkPolicy + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: baselineadminnetworkpolicies + responseKind: + group: policy.networking.k8s.io + kind: BaselineAdminNetworkPolicy + version: v1alpha1 + scope: Cluster + shortNames: + - banp + singularResource: baselineadminnetworkpolicy + subresources: + - responseKind: + group: policy.networking.k8s.io + kind: BaselineAdminNetworkPolicy + version: v1alpha1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha1 +- metadata: + creationTimestamp: null + name: samples.operator.openshift.io + versions: + - freshness: Current + resources: + - resource: configs + responseKind: + group: samples.operator.openshift.io + kind: Config + version: v1 + scope: Cluster + singularResource: config + subresources: + - responseKind: + group: samples.operator.openshift.io + kind: Config + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: security.internal.openshift.io + versions: + - freshness: Current + resources: + - resource: rangeallocations + responseKind: + group: security.internal.openshift.io + kind: RangeAllocation + version: v1 + scope: Cluster + singularResource: rangeallocation + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: snapshot.storage.k8s.io + versions: + - freshness: Current + resources: + - resource: volumesnapshotclasses + responseKind: + group: snapshot.storage.k8s.io + kind: VolumeSnapshotClass + version: v1 + scope: Cluster + shortNames: + - vsclass + - vsclasses + singularResource: volumesnapshotclass + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: volumesnapshotcontents + responseKind: + group: snapshot.storage.k8s.io + kind: VolumeSnapshotContent + version: v1 + scope: Cluster + shortNames: + - vsc + - vscs + singularResource: volumesnapshotcontent + subresources: + - responseKind: + group: snapshot.storage.k8s.io + kind: VolumeSnapshotContent + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: volumesnapshots + responseKind: + group: snapshot.storage.k8s.io + kind: VolumeSnapshot + version: v1 + scope: Namespaced + shortNames: + - vs + singularResource: volumesnapshot + subresources: + - responseKind: + group: snapshot.storage.k8s.io + kind: VolumeSnapshot + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: tuned.openshift.io + versions: + - freshness: Current + resources: + - resource: profiles + responseKind: + group: tuned.openshift.io + kind: Profile + version: v1 + scope: Namespaced + singularResource: profile + subresources: + - responseKind: + group: tuned.openshift.io + kind: Profile + version: v1 + subresource: status + verbs: + - get + - patch + - update + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: tuneds + responseKind: + group: tuned.openshift.io + kind: Tuned + version: v1 + scope: Namespaced + singularResource: tuned + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1 +- metadata: + creationTimestamp: null + name: whereabouts.cni.cncf.io + versions: + - freshness: Current + resources: + - resource: ippools + responseKind: + group: whereabouts.cni.cncf.io + kind: IPPool + version: v1alpha1 + scope: Namespaced + singularResource: ippool + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + - resource: overlappingrangeipreservations + responseKind: + group: whereabouts.cni.cncf.io + kind: OverlappingRangeIPReservation + version: v1alpha1 + scope: Namespaced + singularResource: overlappingrangeipreservation + verbs: + - delete + - deletecollection + - get + - list + - patch + - create + - update + - watch + version: v1alpha1 +- metadata: + creationTimestamp: null + name: metrics.k8s.io + versions: + - freshness: Current + resources: + - resource: nodes + responseKind: + group: "" + kind: NodeMetrics + version: "" + scope: Cluster + singularResource: "" + verbs: + - get + - list + - resource: pods + responseKind: + group: "" + kind: PodMetrics + version: "" + scope: Namespaced + singularResource: "" + verbs: + - get + - list + version: v1beta1 +kind: APIGroupDiscoveryList +metadata: {} diff --git a/vendor/github.com/openshift/library-go/pkg/manifestclient/group_resource_discovery.go b/vendor/github.com/openshift/library-go/pkg/manifestclient/group_resource_discovery.go index 60ef6fb1d..1b4a10f7a 100644 --- a/vendor/github.com/openshift/library-go/pkg/manifestclient/group_resource_discovery.go +++ b/vendor/github.com/openshift/library-go/pkg/manifestclient/group_resource_discovery.go @@ -4,7 +4,10 @@ import ( "errors" "fmt" "io/fs" + "k8s.io/apimachinery/pkg/util/json" + "os" "path/filepath" + "sigs.k8s.io/yaml" "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -12,6 +15,47 @@ import ( ) func (mrt *manifestRoundTripper) getGroupResourceDiscovery(requestInfo *apirequest.RequestInfo) ([]byte, error) { + switch { + case requestInfo.Path == "/api": + ret, err := mrt.getAggregatedDiscoveryForURL("aggregated-discovery-api.yaml", requestInfo.Path) + if os.IsNotExist(err) { + return mrt.getLegacyGroupResourceDiscovery(requestInfo) + } + return ret, err + case requestInfo.Path == "/apis": + ret, err := mrt.getAggregatedDiscoveryForURL("aggregated-discovery-apis.yaml", requestInfo.Path) + if os.IsNotExist(err) { + return mrt.getLegacyGroupResourceDiscovery(requestInfo) + } + return ret, err + default: + // TODO can probably do better + return mrt.getLegacyGroupResourceDiscovery(requestInfo) + } +} + +func (mrt *manifestRoundTripper) getAggregatedDiscoveryForURL(filename, url string) ([]byte, error) { + discoveryBytes, err := fs.ReadFile(mrt.sourceFS, filename) + if os.IsNotExist(err) { + discoveryBytes, err = fs.ReadFile(defaultDiscovery, filepath.Join("default-discovery", filename)) + } + if err != nil { + return nil, fmt.Errorf("error reading discovery: %w", err) + } + + apiMap := map[string]interface{}{} + if err := yaml.Unmarshal(discoveryBytes, &apiMap); err != nil { + return nil, fmt.Errorf("discovery %q unmarshal failed: %w", url, err) + } + apiJSON, err := json.Marshal(apiMap) + if err != nil { + return nil, fmt.Errorf("discovery %q marshal failed: %w", url, err) + } + + return apiJSON, err +} + +func (mrt *manifestRoundTripper) getLegacyGroupResourceDiscovery(requestInfo *apirequest.RequestInfo) ([]byte, error) { if len(requestInfo.Path) == 0 { return nil, fmt.Errorf("path required for group resource discovery") } diff --git a/vendor/github.com/openshift/library-go/pkg/manifestclient/list.go b/vendor/github.com/openshift/library-go/pkg/manifestclient/list.go index bcd2f0d66..211684e1b 100644 --- a/vendor/github.com/openshift/library-go/pkg/manifestclient/list.go +++ b/vendor/github.com/openshift/library-go/pkg/manifestclient/list.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" "io/fs" - "os" + "k8s.io/apimachinery/pkg/runtime/schema" "path/filepath" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -35,6 +35,16 @@ func (mrt *manifestRoundTripper) listAll(requestInfo *apirequest.RequestInfo) ([ return mrt.listAllNamespaces() } + gvr := schema.GroupVersionResource{ + Group: requestInfo.APIGroup, + Version: requestInfo.APIVersion, + Resource: requestInfo.Resource, + } + + kind, err := mrt.getKindForResource(gvr) + if err != nil { + return nil, fmt.Errorf("unable to determine list kind: %w", err) + } possibleListFiles, err := allPossibleListFileLocations(mrt.sourceFS, requestInfo) if err != nil { return nil, fmt.Errorf("unable to determine list file locations: %w", err) @@ -58,6 +68,9 @@ func (mrt *manifestRoundTripper) listAll(requestInfo *apirequest.RequestInfo) ([ } } if retList != nil { + if retList.GroupVersionKind() != kind.listKind { + return nil, fmt.Errorf("inconsistent list kind: got %v, expected %v", retList.GroupVersionKind(), kind.listKind) + } ret, err := serializeListObjToJSON(retList) if err != nil { return nil, fmt.Errorf("failed to serialize: %v", err) @@ -69,6 +82,7 @@ func (mrt *manifestRoundTripper) listAll(requestInfo *apirequest.RequestInfo) ([ Object: map[string]interface{}{}, Items: nil, } + retList.SetGroupVersionKind(kind.listKind) individualFiles, err := allIndividualFileLocations(mrt.sourceFS, requestInfo) if err != nil { return nil, fmt.Errorf("unable to determine individual file locations: %w", err) @@ -86,81 +100,24 @@ func (mrt *manifestRoundTripper) listAll(requestInfo *apirequest.RequestInfo) ([ retList.Items = append(retList.Items, *currInstance) } if len(retList.Items) > 0 { - retList.SetKind(retList.Items[0].GetKind() + "List") - retList.SetAPIVersion(retList.Items[0].GetAPIVersion()) - - ret, err := serializeListObjToJSON(retList) - if err != nil { - return nil, fmt.Errorf("failed to serialize: %v", err) - } - return []byte(ret), nil - } - - if len(requestInfo.Namespace) == 0 { - return nil, fmt.Errorf("unable to read any file so we have no Kind for cluster scoped resource") - } - - // if we get here, there is no list file and no individual files in the expected namespace, but we might have a kind in another namespace. - // we will always assume that empty list is kinder than 404 since we want informers to be synchronized. - possibleListFilesFromOtherNamespaces, err := allPossibleNamespacedListFilesInAnyNamespace(mrt.sourceFS, requestInfo) - if err != nil { - return nil, fmt.Errorf("unable to determine list file alternative locations: %w", err) - } - for _, listFile := range possibleListFilesFromOtherNamespaces { - currList, err := readListFile(mrt.sourceFS, listFile) - switch { - case errors.Is(err, fs.ErrNotExist): - // do nothing, it's possible, not guaranteed - continue - case err != nil: - return nil, fmt.Errorf("unable to determine read alternative list file %v: %w", listFile, err) + if retList.Items[0].GroupVersionKind() != kind.kind { + return nil, fmt.Errorf("inconsistent item kind: got %v, expected %v", retList.Items[0].GroupVersionKind(), kind.kind) } - - retList = &unstructured.UnstructuredList{ - Object: map[string]interface{}{}, - Items: nil, - } - retList.SetKind(currList.GetKind()) - retList.SetAPIVersion(currList.GetAPIVersion()) - ret, err := serializeListObjToJSON(retList) if err != nil { return nil, fmt.Errorf("failed to serialize: %v", err) } - return []byte(ret), nil } - possibleIndividualFilesFromOtherNamespaces, err := allPossibleNamespacedIndividualFilesInAnyNamespace(mrt.sourceFS, requestInfo) + // if we get here, there is no list file and no individual files. + // the namespace must exist or we would have returned long ago. Return an empty list. + ret, err := serializeListObjToJSON(retList) if err != nil { - return nil, fmt.Errorf("unable to determine list file alternative individual files: %w", err) - } - for _, individualFile := range possibleIndividualFilesFromOtherNamespaces { - currList, err := readIndividualFile(mrt.sourceFS, individualFile) - switch { - case errors.Is(err, fs.ErrNotExist): - // do nothing, it's possible, not guaranteed - continue - case err != nil: - return nil, fmt.Errorf("unable to determine read alternative individual file %v: %w", individualFile, err) - } - - retList = &unstructured.UnstructuredList{ - Object: map[string]interface{}{}, - Items: nil, - } - retList.SetKind(currList.GetKind() + "List") - retList.SetAPIVersion(currList.GetAPIVersion()) - - ret, err := serializeListObjToJSON(retList) - if err != nil { - return nil, fmt.Errorf("failed to serialize: %v", err) - } - - return []byte(ret), nil + return nil, fmt.Errorf("failed to serialize: %v", err) } - return nil, fmt.Errorf("unable to read any file in any namespaceso we have no Kind for namespaced resource") + return []byte(ret), nil } func (mrt *manifestRoundTripper) listAllNamespaces() ([]byte, error) { @@ -263,7 +220,7 @@ func allPossibleListFileLocations(sourceFS fs.FS, requestInfo *apirequest.Reques namespaces, err := allNamespacesWithData(sourceFS) if err != nil { - return nil, fmt.Errorf("unable to read namespaces") + return nil, fmt.Errorf("unable to read namespaces: %w", err) } for _, ns := range namespaces { nsParts := append([]string{"namespaces", ns}, resourceListFileParts...) @@ -288,88 +245,6 @@ func allNamespacesWithData(sourceFS fs.FS) ([]string, error) { return ret, nil } -func allPossibleNamespacedListFilesInAnyNamespace(sourceFS fs.FS, requestInfo *apirequest.RequestInfo) ([]string, error) { - if len(requestInfo.Namespace) == 0 { - return nil, fmt.Errorf("namespace must be specified for allPossibleNamespacedListFilesInAnyNamespace") - } - - resourceListFileParts := []string{} - if len(requestInfo.APIGroup) > 0 { - resourceListFileParts = append(resourceListFileParts, requestInfo.APIGroup) - } else { - resourceListFileParts = append(resourceListFileParts, "core") - } - resourceListFileParts = append(resourceListFileParts, fmt.Sprintf("%s.yaml", requestInfo.Resource)) - - allPossibleListFileLocations := []string{} - if len(requestInfo.Namespace) > 0 { - namespaces, err := allNamespacesWithData(sourceFS) - if err != nil { - return nil, fmt.Errorf("unable to read namespaces") - } - - for _, namespace := range namespaces { - parts := append([]string{"namespaces", namespace}, resourceListFileParts...) - allPossibleListFileLocations = append(allPossibleListFileLocations, filepath.Join(parts...)) - } - } - - return allPossibleListFileLocations, nil -} - -func allPossibleNamespacedIndividualFilesInAnyNamespace(sourceFS fs.FS, requestInfo *apirequest.RequestInfo) ([]string, error) { - if len(requestInfo.Namespace) == 0 { - return nil, fmt.Errorf("namespace must be specified for allPossibleNamespacedListFilesInAnyNamespace") - } - - resourceDirFileParts := []string{} - if len(requestInfo.APIGroup) > 0 { - resourceDirFileParts = append(resourceDirFileParts, requestInfo.APIGroup) - } else { - resourceDirFileParts = append(resourceDirFileParts, "core") - } - resourceDirFileParts = append(resourceDirFileParts, requestInfo.Resource) - - allPossibleListFileLocations := []string{} - if len(requestInfo.Namespace) > 0 { - namespaces, err := allNamespacesWithData(sourceFS) - if err != nil { - return nil, fmt.Errorf("unable to read namespaces: %w", err) - } - - for _, namespace := range namespaces { - parts := append([]string{"namespaces", namespace}, resourceDirFileParts...) - individualFiles, err := allIndividualFilesInResourceDirWithData(sourceFS, filepath.Join(parts...)) - if err != nil { - return nil, fmt.Errorf("unable to read resourcefiles: %w", err) - } - for _, individualFilename := range individualFiles { - individualFileParts := append(parts, individualFilename) - allPossibleListFileLocations = append(allPossibleListFileLocations, filepath.Join(individualFileParts...)) - } - } - } - - return allPossibleListFileLocations, nil -} - -func allIndividualFilesInResourceDirWithData(sourceFS fs.FS, resourceDir string) ([]string, error) { - individualFiles, err := fs.ReadDir(sourceFS, resourceDir) - if os.IsNotExist(err) { // not all the namespaces will have the resourceDir - return nil, nil - } - if err != nil { - return nil, fmt.Errorf("failed to read allIndividualFilesInResourceDirWithData: %w", err) - } - - ret := []string{} - for _, curr := range individualFiles { - ret = append(ret, curr.Name()) - } - - return ret, nil -} - func allPossibleNamespaceFiles(sourceFS fs.FS) ([]string, error) { allPossibleListFileLocations := []string{} namespaces, err := allNamespacesWithData(sourceFS) diff --git a/vendor/github.com/openshift/library-go/pkg/manifestclient/read_roundtripper.go b/vendor/github.com/openshift/library-go/pkg/manifestclient/read_roundtripper.go index dc1b5b005..77d5beb5e 100644 --- a/vendor/github.com/openshift/library-go/pkg/manifestclient/read_roundtripper.go +++ b/vendor/github.com/openshift/library-go/pkg/manifestclient/read_roundtripper.go @@ -2,20 +2,22 @@ package manifestclient import ( "bytes" + "embed" "fmt" "io" "io/fs" + apidiscoveryv2 "k8s.io/api/apidiscovery/v2" + "k8s.io/apimachinery/pkg/util/json" "net/http" "strconv" "strings" + "sync" "time" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" - - apirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apimachinery/pkg/util/sets" + apirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/server" ) @@ -24,6 +26,15 @@ type manifestRoundTripper struct { // requestInfoResolver is the same type constructed the same way as the kube-apiserver requestInfoResolver *apirequest.RequestInfoFactory + + lock sync.RWMutex + kindForResource map[schema.GroupVersionResource]kindData +} + +type kindData struct { + kind schema.GroupVersionKind + listKind schema.GroupVersionKind + err error } func newReadRoundTripper(content fs.FS) *manifestRoundTripper { @@ -32,6 +43,7 @@ func newReadRoundTripper(content fs.FS) *manifestRoundTripper { requestInfoResolver: server.NewRequestInfoResolver(&server.Config{ LegacyAPIGroupPrefixes: sets.NewString(server.DefaultLegacyAPIPrefix), }), + kindForResource: make(map[schema.GroupVersionResource]kindData), } } @@ -128,6 +140,9 @@ func isServerGroupResourceDiscovery(path string) bool { if path == "/api/v1" { return true } + if path == "/api" { + return true + } parts := strings.Split(path, "/") if len(parts) != 4 { @@ -135,3 +150,84 @@ func isServerGroupResourceDiscovery(path string) bool { } return parts[0] == "" && parts[1] == "apis" } + +//go:embed default-discovery +var defaultDiscovery embed.FS + +func (mrt *manifestRoundTripper) getKindForResource(gvr schema.GroupVersionResource) (kindData, error) { + mrt.lock.RLock() + kindForGVR, ok := mrt.kindForResource[gvr] + if ok { + defer mrt.lock.RUnlock() + return kindForGVR, kindForGVR.err + } + mrt.lock.RUnlock() + + mrt.lock.Lock() + defer mrt.lock.Unlock() + + kindForGVR, ok = mrt.kindForResource[gvr] + if ok { + return kindForGVR, kindForGVR.err + } + + discoveryPath := "/apis" + if len(gvr.Group) == 0 { + discoveryPath = "/api" + } + discoveryBytes, err := mrt.getGroupResourceDiscovery(&apirequest.RequestInfo{Path: discoveryPath}) + if err != nil { + kindForGVR.err = fmt.Errorf("error reading discovery: %w", err) + mrt.kindForResource[gvr] = kindForGVR + return kindForGVR, kindForGVR.err + } + + discoveryInfo := &apidiscoveryv2.APIGroupDiscoveryList{} + if err := json.Unmarshal(discoveryBytes, discoveryInfo); err != nil { + kindForGVR.err = fmt.Errorf("error unmarshalling discovery: %w", err) + mrt.kindForResource[gvr] = kindForGVR + return kindForGVR, kindForGVR.err + } + + kindForGVR.err = fmt.Errorf("did not find kind for %v\n", gvr) + for _, groupInfo := range discoveryInfo.Items { + if groupInfo.Name != gvr.Group { + continue + } + for _, versionInfo := range groupInfo.Versions { + if versionInfo.Version != gvr.Version { + continue + } + for _, resourceInfo := range versionInfo.Resources { + if resourceInfo.Resource != gvr.Resource { + continue + } + if resourceInfo.ResponseKind == nil { + continue + } + kindForGVR.kind = schema.GroupVersionKind{ + Group: gvr.Group, + Version: gvr.Version, + Kind: resourceInfo.ResponseKind.Kind, + } + if len(resourceInfo.ResponseKind.Group) > 0 { + kindForGVR.kind.Group = resourceInfo.ResponseKind.Group + } + if len(resourceInfo.ResponseKind.Version) > 0 { + kindForGVR.kind.Version = resourceInfo.ResponseKind.Version + } + kindForGVR.listKind = schema.GroupVersionKind{ + Group: kindForGVR.kind.Group, + Version: kindForGVR.kind.Version, + Kind: resourceInfo.ResponseKind.Kind + "List", + } + kindForGVR.err = nil + mrt.kindForResource[gvr] = kindForGVR + return kindForGVR, kindForGVR.err + } + } + } + + mrt.kindForResource[gvr] = kindForGVR + return kindForGVR, kindForGVR.err +} diff --git a/vendor/github.com/openshift/library-go/pkg/operator/apiserver/controller/workload/workload.go b/vendor/github.com/openshift/library-go/pkg/operator/apiserver/controller/workload/workload.go index 443c1ed8f..560799503 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/apiserver/controller/workload/workload.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/apiserver/controller/workload/workload.go @@ -20,10 +20,10 @@ import ( operatorv1 "github.com/openshift/api/operator/v1" openshiftconfigclientv1 "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" + applyoperatorv1 "github.com/openshift/client-go/operator/applyconfigurations/operator/v1" "github.com/openshift/library-go/pkg/apps/deployment" "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/operator/events" - "github.com/openshift/library-go/pkg/operator/resource/resourcemerge" "github.com/openshift/library-go/pkg/operator/status" "github.com/openshift/library-go/pkg/operator/v1helpers" ) @@ -49,6 +49,7 @@ type Delegate interface { // Callers must provide a sync function for delegation. It should bring the desired workload into operation. // The returned state along with errors will be converted into conditions and persisted in the status field. type Controller struct { + controllerInstanceName string // conditionsPrefix an optional prefix that will be used as operator's condition type field for example APIServerDeploymentDegraded where APIServer indicates the prefix conditionsPrefix string operatorNamespace string @@ -71,13 +72,13 @@ type Controller struct { // NewController creates a brand new Controller instance. // -// the "name" param will be used to set conditions in the status field. It will be suffixed with "WorkloadController", +// the "instanceName" param will be used to set conditions in the status field. It will be suffixed with "WorkloadController", // so it can end up in the condition in the form of "OAuthAPIWorkloadControllerDeploymentAvailable" // // the "operatorNamespace" is used to set "version-mapping" in the correct namespace // // the "targetNamespace" represent the namespace for the managed resource (DaemonSet) -func NewController(name, operatorNamespace, targetNamespace, targetOperandVersion, operandNamePrefix, conditionsPrefix string, +func NewController(instanceName, operatorNamespace, targetNamespace, targetOperandVersion, operandNamePrefix, conditionsPrefix string, operatorClient v1helpers.OperatorClient, kubeClient kubernetes.Interface, podLister corev1listers.PodLister, @@ -89,6 +90,7 @@ func NewController(name, operatorNamespace, targetNamespace, targetOperandVersio versionRecorder status.VersionGetter, ) factory.Controller { controllerRef := &Controller{ + controllerInstanceName: factory.ControllerInstanceName(instanceName, "Workload"), operatorNamespace: operatorNamespace, targetNamespace: targetNamespace, targetOperandVersion: targetOperandVersion, @@ -100,7 +102,7 @@ func NewController(name, operatorNamespace, targetNamespace, targetOperandVersio delegate: delegate, openshiftClusterConfigClient: openshiftClusterConfigClient, versionRecorder: versionRecorder, - queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), name), + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), instanceName), } c := factory.New() @@ -111,7 +113,7 @@ func NewController(name, operatorNamespace, targetNamespace, targetOperandVersio return c.WithSync(controllerRef.sync). WithInformers(informers...). ToController( - fmt.Sprintf("%sWorkloadController", name), // don't change what is passed here unless you also remove the old FooDegraded condition + fmt.Sprintf("%sWorkloadController", controllerRef.controllerInstanceName), eventRecorder, ) } @@ -161,40 +163,40 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o errs = []error{} } - deploymentAvailableCondition := operatorv1.OperatorCondition{ - Type: fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeAvailable), - Status: operatorv1.ConditionTrue, - } + deploymentAvailableCondition := applyoperatorv1.OperatorCondition(). + WithType(fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeAvailable)) - workloadDegradedCondition := operatorv1.OperatorCondition{ - Type: fmt.Sprintf("%sWorkloadDegraded", c.conditionsPrefix), - Status: operatorv1.ConditionFalse, - } + workloadDegradedCondition := applyoperatorv1.OperatorCondition(). + WithType(fmt.Sprintf("%sWorkloadDegraded", c.conditionsPrefix)) - deploymentDegradedCondition := operatorv1.OperatorCondition{ - Type: fmt.Sprintf("%sDeploymentDegraded", c.conditionsPrefix), - Status: operatorv1.ConditionFalse, - } + deploymentDegradedCondition := applyoperatorv1.OperatorCondition(). + WithType(fmt.Sprintf("%sDeploymentDegraded", c.conditionsPrefix)) - deploymentProgressingCondition := operatorv1.OperatorCondition{ - Type: fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeProgressing), - Status: operatorv1.ConditionFalse, + deploymentProgressingCondition := applyoperatorv1.OperatorCondition(). + WithType(fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeProgressing)) + + status := applyoperatorv1.OperatorStatus() + if workload != nil { + // The Hash field is not required since the LastGeneration field is enough to uniquely identify a Deployment's desired state + status = status.WithGenerations(applyoperatorv1.GenerationStatus(). + WithGroup("apps"). + WithResource("deployments"). + WithNamespace(workload.Namespace). + WithName(workload.Name). + WithLastGeneration(workload.Generation), + ) } - // only set updateGenerationFn to update the observed generation if everything is available - var updateGenerationFn func(newStatus *operatorv1.OperatorStatus) error defer func() { - updates := []v1helpers.UpdateStatusFunc{ - v1helpers.UpdateConditionFn(deploymentAvailableCondition), - v1helpers.UpdateConditionFn(deploymentDegradedCondition), - v1helpers.UpdateConditionFn(deploymentProgressingCondition), - v1helpers.UpdateConditionFn(workloadDegradedCondition), - } - if updateGenerationFn != nil { - updates = append(updates, updateGenerationFn) - } - if _, _, updateError := v1helpers.UpdateStatus(ctx, c.operatorClient, updates...); updateError != nil { - err = updateError + status = status.WithConditions( + deploymentAvailableCondition, + deploymentDegradedCondition, + deploymentProgressingCondition, + workloadDegradedCondition, + ) + + if applyError := c.operatorClient.ApplyOperatorStatus(ctx, c.controllerInstanceName, status); applyError != nil { + err = applyError } }() @@ -209,15 +211,23 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o // we are degraded, not available and we are not progressing - deploymentDegradedCondition.Status = operatorv1.ConditionTrue - deploymentDegradedCondition.Reason = "PreconditionNotFulfilled" - deploymentDegradedCondition.Message = message + deploymentDegradedCondition = deploymentDegradedCondition. + WithStatus(operatorv1.ConditionTrue). + WithReason("PreconditionNotFulfilled"). + WithMessage(message) - deploymentAvailableCondition.Status = operatorv1.ConditionFalse - deploymentAvailableCondition.Reason = "PreconditionNotFulfilled" + deploymentAvailableCondition = deploymentAvailableCondition. + WithStatus(operatorv1.ConditionFalse). + WithReason("PreconditionNotFulfilled") - deploymentProgressingCondition.Status = operatorv1.ConditionFalse - deploymentProgressingCondition.Reason = "PreconditionNotFulfilled" + deploymentProgressingCondition = deploymentProgressingCondition. + WithStatus(operatorv1.ConditionFalse). + WithReason("PreconditionNotFulfilled") + + workloadDegradedCondition = workloadDegradedCondition. + WithStatus(operatorv1.ConditionTrue). + WithReason("PreconditionNotFulfilled"). + WithMessage(message) return kerrors.NewAggregate(errs) } @@ -227,37 +237,49 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o for _, err := range errs { message = message + err.Error() + "\n" } - workloadDegradedCondition.Status = operatorv1.ConditionTrue - workloadDegradedCondition.Reason = "SyncError" - workloadDegradedCondition.Message = message + workloadDegradedCondition = workloadDegradedCondition. + WithStatus(operatorv1.ConditionTrue). + WithReason("SyncError"). + WithMessage(message) + } else if workload == nil { + workloadDegradedCondition = workloadDegradedCondition. + WithStatus(operatorv1.ConditionTrue). + WithReason("NoDeployment"). + WithMessage(fmt.Sprintf("deployment/%s: could not be retrieved", c.targetNamespace)) } else { - workloadDegradedCondition.Status = operatorv1.ConditionFalse + workloadDegradedCondition = workloadDegradedCondition. + WithStatus(operatorv1.ConditionFalse) } if workload == nil { message := fmt.Sprintf("deployment/%s: could not be retrieved", c.targetNamespace) - deploymentAvailableCondition.Status = operatorv1.ConditionFalse - deploymentAvailableCondition.Reason = "NoDeployment" - deploymentAvailableCondition.Message = message + deploymentAvailableCondition = deploymentAvailableCondition. + WithStatus(operatorv1.ConditionFalse). + WithReason("NoDeployment"). + WithMessage(message) - deploymentProgressingCondition.Status = operatorv1.ConditionTrue - deploymentProgressingCondition.Reason = "NoDeployment" - deploymentProgressingCondition.Message = message + deploymentProgressingCondition = deploymentProgressingCondition. + WithStatus(operatorv1.ConditionTrue). + WithReason("NoDeployment"). + WithMessage(message) - deploymentDegradedCondition.Status = operatorv1.ConditionTrue - deploymentDegradedCondition.Reason = "NoDeployment" - deploymentDegradedCondition.Message = message + deploymentDegradedCondition = deploymentDegradedCondition. + WithStatus(operatorv1.ConditionTrue). + WithReason("NoDeployment"). + WithMessage(message) return kerrors.NewAggregate(errs) } if workload.Status.AvailableReplicas == 0 { - deploymentAvailableCondition.Status = operatorv1.ConditionFalse - deploymentAvailableCondition.Reason = "NoPod" - deploymentAvailableCondition.Message = fmt.Sprintf("no %s.%s pods available on any node.", workload.Name, c.targetNamespace) + deploymentAvailableCondition = deploymentAvailableCondition. + WithStatus(operatorv1.ConditionFalse). + WithReason("NoPod"). + WithMessage(fmt.Sprintf("no %s.%s pods available on any node.", workload.Name, c.targetNamespace)) } else { - deploymentAvailableCondition.Status = operatorv1.ConditionTrue - deploymentAvailableCondition.Reason = "AsExpected" + deploymentAvailableCondition = deploymentAvailableCondition. + WithStatus(operatorv1.ConditionTrue). + WithReason("AsExpected") } desiredReplicas := int32(1) @@ -268,18 +290,21 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o // If the workload is up to date, then we are no longer progressing workloadAtHighestGeneration := workload.ObjectMeta.Generation == workload.Status.ObservedGeneration workloadIsBeingUpdated := workload.Status.UpdatedReplicas < desiredReplicas - workloadIsBeingUpdatedTooLong, err := isUpdatingTooLong(previousStatus, deploymentProgressingCondition.Type) + workloadIsBeingUpdatedTooLong, err := isUpdatingTooLong(previousStatus, *deploymentProgressingCondition.Type) if !workloadAtHighestGeneration { - deploymentProgressingCondition.Status = operatorv1.ConditionTrue - deploymentProgressingCondition.Reason = "NewGeneration" - deploymentProgressingCondition.Message = fmt.Sprintf("deployment/%s.%s: observed generation is %d, desired generation is %d.", workload.Name, c.targetNamespace, workload.Status.ObservedGeneration, workload.ObjectMeta.Generation) + deploymentProgressingCondition = deploymentProgressingCondition. + WithStatus(operatorv1.ConditionTrue). + WithReason("NewGeneration"). + WithMessage(fmt.Sprintf("deployment/%s.%s: observed generation is %d, desired generation is %d.", workload.Name, c.targetNamespace, workload.Status.ObservedGeneration, workload.ObjectMeta.Generation)) } else if workloadIsBeingUpdated { - deploymentProgressingCondition.Status = operatorv1.ConditionTrue - deploymentProgressingCondition.Reason = "PodsUpdating" - deploymentProgressingCondition.Message = fmt.Sprintf("deployment/%s.%s: %d/%d pods have been updated to the latest generation", workload.Name, c.targetNamespace, workload.Status.UpdatedReplicas, desiredReplicas) + deploymentProgressingCondition = deploymentProgressingCondition. + WithStatus(operatorv1.ConditionTrue). + WithReason("PodsUpdating"). + WithMessage(fmt.Sprintf("deployment/%s.%s: %d/%d pods have been updated to the latest generation", workload.Name, c.targetNamespace, workload.Status.UpdatedReplicas, desiredReplicas)) } else { - deploymentProgressingCondition.Status = operatorv1.ConditionFalse - deploymentProgressingCondition.Reason = "AsExpected" + deploymentProgressingCondition = deploymentProgressingCondition. + WithStatus(operatorv1.ConditionFalse). + WithReason("AsExpected") } // During a rollout the default maxSurge (25%) will allow the available @@ -288,17 +313,19 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o workloadHasAllPodsAvailable := workload.Status.AvailableReplicas >= desiredReplicas if !workloadHasAllPodsAvailable && (!workloadIsBeingUpdated || workloadIsBeingUpdatedTooLong) { numNonAvailablePods := desiredReplicas - workload.Status.AvailableReplicas - deploymentDegradedCondition.Status = operatorv1.ConditionTrue - deploymentDegradedCondition.Reason = "UnavailablePod" + deploymentDegradedCondition = deploymentDegradedCondition. + WithStatus(operatorv1.ConditionTrue). + WithReason("UnavailablePod") podContainersStatus, err := deployment.PodContainersStatus(workload, c.podsLister) if err != nil { podContainersStatus = []string{fmt.Sprintf("failed to get pod containers details: %v", err)} } - deploymentDegradedCondition.Message = fmt.Sprintf("%v of %v requested instances are unavailable for %s.%s (%s)", numNonAvailablePods, desiredReplicas, workload.Name, c.targetNamespace, - strings.Join(podContainersStatus, ", ")) + deploymentDegradedCondition = deploymentDegradedCondition. + WithMessage(fmt.Sprintf("%v of %v requested instances are unavailable for %s.%s (%s)", numNonAvailablePods, desiredReplicas, workload.Name, c.targetNamespace, strings.Join(podContainersStatus, ", "))) } else { - deploymentDegradedCondition.Status = operatorv1.ConditionFalse - deploymentDegradedCondition.Reason = "AsExpected" + deploymentDegradedCondition = deploymentDegradedCondition. + WithStatus(operatorv1.ConditionFalse). + WithReason("AsExpected") } // if the deployment is all available and at the expected generation, then update the version to the latest @@ -313,12 +340,6 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o c.versionRecorder.SetVersion(operandName, c.targetOperandVersion) } - // set updateGenerationFn so that it is invoked in defer - updateGenerationFn = func(newStatus *operatorv1.OperatorStatus) error { - resourcemerge.SetDeploymentGeneration(&newStatus.Generations, workload) - return nil - } - if len(errs) > 0 { return kerrors.NewAggregate(errs) } diff --git a/vendor/github.com/openshift/library-go/pkg/operator/apiserver/controllerset/apiservercontrollerset.go b/vendor/github.com/openshift/library-go/pkg/operator/apiserver/controllerset/apiservercontrollerset.go index 59a43d630..b6813c39a 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/apiserver/controllerset/apiservercontrollerset.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/apiserver/controllerset/apiservercontrollerset.go @@ -3,6 +3,7 @@ package apiservercontrollerset import ( "context" "fmt" + "k8s.io/utils/clock" "regexp" "time" @@ -74,6 +75,7 @@ type APIServerControllerSet struct { name string operatorClient v1helpers.OperatorClient eventRecorder events.Recorder + clock clock.PassiveClock apiServiceController controllerWrapper auditPolicyController controllerWrapper @@ -92,11 +94,13 @@ func NewAPIServerControllerSet( name string, operatorClient v1helpers.OperatorClient, eventRecorder events.Recorder, + clock clock.PassiveClock, ) *APIServerControllerSet { apiServerControllerSet := &APIServerControllerSet{ name: name, operatorClient: operatorClient, eventRecorder: eventRecorder, + clock: clock, } return apiServerControllerSet @@ -143,6 +147,7 @@ func (cs *APIServerControllerSet) WithClusterOperatorStatusController( cs.operatorClient, versionRecorder, cs.eventRecorder, + cs.clock, ) for _, opt := range options { s = opt(s) diff --git a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/monitoring.go b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/monitoring.go index 99f3ecb73..d0996a2af 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/monitoring.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/monitoring.go @@ -2,23 +2,17 @@ package resourceapply import ( "context" - errorsstdlib "errors" - "fmt" + "github.com/openshift/library-go/pkg/operator/events" + "github.com/openshift/library-go/pkg/operator/resource/resourcehelper" + "github.com/openshift/library-go/pkg/operator/resource/resourcemerge" "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/klog/v2" - "k8s.io/utils/ptr" - - "github.com/openshift/library-go/pkg/operator/events" - "github.com/openshift/library-go/pkg/operator/resource/resourcehelper" - - "github.com/openshift/library-go/pkg/operator/resource/resourcemerge" ) var alertmanagerGVR = schema.GroupVersionResource{Group: "monitoring.coreos.com", Version: "v1", Resource: "alertmanagers"} @@ -88,10 +82,10 @@ func ApplyUnstructuredResourceImproved( } existing, err := client.Resource(resourceGVR).Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) if errors.IsNotFound(err) { - want, err := client.Resource(resourceGVR).Namespace(namespace).Create(ctx, required, metav1.CreateOptions{}) - resourcehelper.ReportCreateEvent(recorder, required, err) + want, errCreate := client.Resource(resourceGVR).Namespace(namespace).Create(ctx, required, metav1.CreateOptions{}) + resourcehelper.ReportCreateEvent(recorder, required, errCreate) cache.UpdateCachedResourceMetadata(required, want) - return want, true, err + return want, true, errCreate } if err != nil { return nil, false, err @@ -102,44 +96,15 @@ func ApplyUnstructuredResourceImproved( return existing, false, nil } - // Ensure metadata field is present on the object. existingCopy := existing.DeepCopy() - existingObjectMeta, found, err := unstructured.NestedMap(existingCopy.Object, "metadata") - if err != nil { - return nil, false, err - } - if !found { - return nil, false, errorsstdlib.New(fmt.Sprintf("metadata not found in the existing object: %s/%s", existing.GetNamespace(), existingCopy.GetName())) - } - requiredObjectMeta, found, err := unstructured.NestedMap(required.Object, "metadata") - if err != nil { - return nil, false, err - } - if !found { - return nil, false, errorsstdlib.New(fmt.Sprintf("metadata not found in the required object: %s/%s", required.GetNamespace(), required.GetName())) - } - // Cast the metadata to the correct type. - var existingObjectMetaTyped, requiredObjectMetaTyped metav1.ObjectMeta - err = runtime.DefaultUnstructuredConverter.FromUnstructured(existingObjectMeta, &existingObjectMetaTyped) - if err != nil { - return nil, false, err - } - err = runtime.DefaultUnstructuredConverter.FromUnstructured(requiredObjectMeta, &requiredObjectMetaTyped) + // Replace and/or merge certain metadata fields. + didMetadataModify := false + err = resourcemerge.EnsureObjectMetaForUnstructured(&didMetadataModify, existingCopy, required) if err != nil { return nil, false, err } - // Fail-fast if the resource versions differ. - if requiredObjectMetaTyped.ResourceVersion != "" && existingObjectMetaTyped.ResourceVersion != requiredObjectMetaTyped.ResourceVersion { - err = errors.NewConflict(resourceGVR.GroupResource(), name, fmt.Errorf("rejected to update %s %s because the object has been modified: desired/actual ResourceVersion: %v/%v", existing.GetKind(), existing.GetName(), requiredObjectMetaTyped.ResourceVersion, existingObjectMetaTyped.ResourceVersion)) - return nil, false, err - } - - // Check if the metadata objects differ. - didMetadataModify := ptr.To(false) - resourcemerge.EnsureObjectMeta(didMetadataModify, &existingObjectMetaTyped, requiredObjectMetaTyped) - // Deep-check the spec objects for equality, and update the cache in either case. if defaultingFunc == nil { defaultingFunc = noDefaulting @@ -147,26 +112,26 @@ func ApplyUnstructuredResourceImproved( if equalityChecker == nil { equalityChecker = equality.Semantic } - existingCopy, didSpecModify, err := ensureGenericSpec(required, existingCopy, defaultingFunc, equalityChecker) + didSpecModify := false + err = ensureGenericSpec(&didSpecModify, required, existingCopy, defaultingFunc, equalityChecker) if err != nil { return nil, false, err } - if !didSpecModify && !*didMetadataModify { + if !didSpecModify && !didMetadataModify { // Update cache even if certain fields are not modified, in order to maintain a consistent cache based on the // resource hash. The resource hash depends on the entire metadata, not just the fields that were checked above, cache.UpdateCachedResourceMetadata(required, existingCopy) return existingCopy, false, nil } + // Perform update if resource exists but different from the required (desired) one. if klog.V(4).Enabled() { klog.Infof("%s %q changes: %v", resourceGVR.String(), namespace+"/"+name, JSONPatchNoError(existing, existingCopy)) } - - // Perform update if resource exists but different from the required (desired) one. - actual, err := client.Resource(resourceGVR).Namespace(namespace).Update(ctx, required, metav1.UpdateOptions{}) - resourcehelper.ReportUpdateEvent(recorder, required, err) - cache.UpdateCachedResourceMetadata(required, actual) - return actual, true, err + actual, errUpdate := client.Resource(resourceGVR).Namespace(namespace).Update(ctx, existingCopy, metav1.UpdateOptions{}) + resourcehelper.ReportUpdateEvent(recorder, existingCopy, errUpdate) + cache.UpdateCachedResourceMetadata(existingCopy, actual) + return actual, true, errUpdate } // DeleteUnstructuredResource deletes the unstructured resource. @@ -182,27 +147,27 @@ func DeleteUnstructuredResource(ctx context.Context, client dynamic.Interface, r return nil, true, nil } -func ensureGenericSpec(required, existing *unstructured.Unstructured, mimicDefaultingFn mimicDefaultingFunc, equalityChecker equalityChecker) (*unstructured.Unstructured, bool, error) { +func ensureGenericSpec(didSpecModify *bool, required, existing *unstructured.Unstructured, mimicDefaultingFn mimicDefaultingFunc, equalityChecker equalityChecker) error { mimicDefaultingFn(required) requiredSpec, _, err := unstructured.NestedMap(required.UnstructuredContent(), "spec") if err != nil { - return nil, false, err + return err } existingSpec, _, err := unstructured.NestedMap(existing.UnstructuredContent(), "spec") if err != nil { - return nil, false, err + return err } if equalityChecker.DeepEqual(existingSpec, requiredSpec) { - return existing, false, nil + return nil } - existingCopy := existing.DeepCopy() - if err := unstructured.SetNestedMap(existingCopy.UnstructuredContent(), requiredSpec, "spec"); err != nil { - return nil, true, err + if err = unstructured.SetNestedMap(existing.UnstructuredContent(), requiredSpec, "spec"); err != nil { + return err } + *didSpecModify = true - return existingCopy, true, nil + return nil } // mimicDefaultingFunc is used to set fields that are defaulted. This allows for sparse manifests to apply correctly. diff --git a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourcemerge/object_merger.go b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourcemerge/object_merger.go index 4c5dcacaa..20e19a78f 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourcemerge/object_merger.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourcemerge/object_merger.go @@ -1,10 +1,14 @@ package resourcemerge import ( + errorsstdlib "errors" + "fmt" "reflect" "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -18,6 +22,48 @@ func EnsureObjectMeta(modified *bool, existing *metav1.ObjectMeta, required meta MergeOwnerRefs(modified, &existing.OwnerReferences, required.OwnerReferences) } +func EnsureObjectMetaForUnstructured(modified *bool, existing *unstructured.Unstructured, required *unstructured.Unstructured) error { + + // Ensure metadata field is present on the object. + existingObjectMeta, found, err := unstructured.NestedMap(existing.Object, "metadata") + if err != nil { + return err + } + if !found { + return errorsstdlib.New(fmt.Sprintf("metadata not found in the existing object: %s/%s", existing.GetNamespace(), existing.GetName())) + } + var requiredObjectMeta map[string]interface{} + requiredObjectMeta, found, err = unstructured.NestedMap(required.Object, "metadata") + if err != nil { + return err + } + if !found { + return errorsstdlib.New(fmt.Sprintf("metadata not found in the required object: %s/%s", required.GetNamespace(), required.GetName())) + } + + // Cast the metadata to the correct type. + var existingObjectMetaTyped, requiredObjectMetaTyped metav1.ObjectMeta + err = runtime.DefaultUnstructuredConverter.FromUnstructured(existingObjectMeta, &existingObjectMetaTyped) + if err != nil { + return err + } + err = runtime.DefaultUnstructuredConverter.FromUnstructured(requiredObjectMeta, &requiredObjectMetaTyped) + if err != nil { + return err + } + + // Check if the metadata objects differ. This only checks for selective fields (excluding the resource version, among others). + EnsureObjectMeta(modified, &existingObjectMetaTyped, requiredObjectMetaTyped) + if *modified { + existing.Object["metadata"], err = runtime.DefaultUnstructuredConverter.ToUnstructured(&existingObjectMetaTyped) + if err != nil { + return err + } + } + + return nil +} + // WithCleanLabelsAndAnnotations cleans the metadata off the removal annotations/labels/ownerrefs // (those that end with trailing "-") func WithCleanLabelsAndAnnotations(obj metav1.Object) metav1.Object { diff --git a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceread/admission.go b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceread/admission.go index 572b915bc..11326c89d 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceread/admission.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceread/admission.go @@ -53,3 +53,21 @@ func ReadValidatingAdmissionPolicyBindingV1beta1OrDie(objBytes []byte) *admissio return requiredObj.(*admissionv1beta1.ValidatingAdmissionPolicyBinding) } + +func ReadValidatingAdmissionPolicyV1OrDie(objBytes []byte) *admissionv1.ValidatingAdmissionPolicy { + requiredObj, err := runtime.Decode(admissionCodecs.UniversalDecoder(admissionv1.SchemeGroupVersion), objBytes) + if err != nil { + panic(err) + } + + return requiredObj.(*admissionv1.ValidatingAdmissionPolicy) +} + +func ReadValidatingAdmissionPolicyBindingV1OrDie(objBytes []byte) *admissionv1.ValidatingAdmissionPolicyBinding { + requiredObj, err := runtime.Decode(admissionCodecs.UniversalDecoder(admissionv1.SchemeGroupVersion), objBytes) + if err != nil { + panic(err) + } + + return requiredObj.(*admissionv1.ValidatingAdmissionPolicyBinding) +} diff --git a/vendor/github.com/openshift/library-go/pkg/operator/status/condition.go b/vendor/github.com/openshift/library-go/pkg/operator/status/condition.go index 56e1496b8..edcafa0b6 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/status/condition.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/status/condition.go @@ -2,6 +2,8 @@ package status import ( "fmt" + applyconfigv1 "github.com/openshift/client-go/config/applyconfigurations/config/v1" + "k8s.io/utils/ptr" "sort" "strings" "time" @@ -89,18 +91,18 @@ func UnionCondition(conditionType string, defaultConditionStatus operatorv1.Cond // on true, but Available merges on false. Thing of it like an anti-default. // // If inertia is non-nil, then resist returning a condition with a status opposite the defaultConditionStatus. -func UnionClusterCondition(conditionType configv1.ClusterStatusConditionType, defaultConditionStatus operatorv1.ConditionStatus, inertia Inertia, allConditions ...operatorv1.OperatorCondition) configv1.ClusterOperatorStatusCondition { +func UnionClusterCondition(conditionType configv1.ClusterStatusConditionType, defaultConditionStatus operatorv1.ConditionStatus, inertia Inertia, allConditions ...operatorv1.OperatorCondition) *applyconfigv1.ClusterOperatorStatusConditionApplyConfiguration { cnd := UnionCondition(string(conditionType), defaultConditionStatus, inertia, allConditions...) return OperatorConditionToClusterOperatorCondition(cnd) } -func OperatorConditionToClusterOperatorCondition(condition operatorv1.OperatorCondition) configv1.ClusterOperatorStatusCondition { - return configv1.ClusterOperatorStatusCondition{ - Type: configv1.ClusterStatusConditionType(condition.Type), - Status: configv1.ConditionStatus(condition.Status), - LastTransitionTime: condition.LastTransitionTime, - Reason: condition.Reason, - Message: condition.Message, +func OperatorConditionToClusterOperatorCondition(condition operatorv1.OperatorCondition) *applyconfigv1.ClusterOperatorStatusConditionApplyConfiguration { + return &applyconfigv1.ClusterOperatorStatusConditionApplyConfiguration{ + Type: ptr.To(configv1.ClusterStatusConditionType(condition.Type)), + Status: ptr.To(configv1.ConditionStatus(condition.Status)), + LastTransitionTime: ptr.To(condition.LastTransitionTime), + Reason: ptr.To(condition.Reason), + Message: ptr.To(condition.Message), } } func latestTransitionTime(conditions []operatorv1.OperatorCondition) metav1.Time { diff --git a/vendor/github.com/openshift/library-go/pkg/operator/status/status_controller.go b/vendor/github.com/openshift/library-go/pkg/operator/status/status_controller.go index 2f7bf95d7..dc64d8b8c 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/status/status_controller.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/status/status_controller.go @@ -2,6 +2,10 @@ package status import ( "context" + "fmt" + applyconfigv1 "github.com/openshift/client-go/config/applyconfigurations/config/v1" + "k8s.io/utils/clock" + "k8s.io/utils/ptr" "strings" "time" @@ -12,7 +16,6 @@ import ( configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" configv1informers "github.com/openshift/client-go/config/informers/externalversions/config/v1" configv1listers "github.com/openshift/client-go/config/listers/config/v1" - "k8s.io/apimachinery/pkg/api/equality" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -37,9 +40,11 @@ type VersionGetter interface { type RelatedObjectsFunc func() (isset bool, objs []configv1.ObjectReference) type StatusSyncer struct { - clusterOperatorName string - relatedObjects []configv1.ObjectReference - relatedObjectsFunc RelatedObjectsFunc + controllerInstanceName string + clusterOperatorName string + relatedObjects []configv1.ObjectReference + relatedObjectsFunc RelatedObjectsFunc + clock clock.PassiveClock versionGetter VersionGetter operatorClient operatorv1helpers.OperatorClient @@ -67,15 +72,19 @@ func NewClusterOperatorStatusController( operatorClient operatorv1helpers.OperatorClient, versionGetter VersionGetter, recorder events.Recorder, + clock clock.PassiveClock, ) *StatusSyncer { + instanceName := name return &StatusSyncer{ - clusterOperatorName: name, - relatedObjects: relatedObjects, - versionGetter: versionGetter, - clusterOperatorClient: clusterOperatorClient, - clusterOperatorLister: clusterOperatorInformer.Lister(), - operatorClient: operatorClient, - degradedInertia: MustNewInertia(2 * time.Minute).Inertia, + controllerInstanceName: factory.ControllerInstanceName(instanceName, "ClusterOperatorStatus"), + clusterOperatorName: name, + relatedObjects: relatedObjects, + clock: clock, + versionGetter: versionGetter, + clusterOperatorClient: clusterOperatorClient, + clusterOperatorLister: clusterOperatorInformer.Lister(), + operatorClient: operatorClient, + degradedInertia: MustNewInertia(2 * time.Minute).Inertia, controllerFactory: factory.New().ResyncEvery(time.Minute).WithInformers( operatorClient.Informer(), clusterOperatorInformer.Informer(), @@ -104,7 +113,7 @@ func (c *StatusSyncer) Run(ctx context.Context, workers int) { WithPostStartHooks(c.watchVersionGetterPostRunHook). WithSync(c.Sync). ToController( - "StatusSyncer_"+c.Name(), // don't change what is passed here unless you also remove the old FooDegraded condition + c.controllerInstanceName, c.recorder, ). Run(ctx, workers) @@ -151,9 +160,7 @@ func (c StatusSyncer) Sync(ctx context.Context, syncCtx factory.SyncContext) err if originalClusterOperatorObj == nil || apierrors.IsNotFound(err) { klog.Infof("clusteroperator/%s not found", c.clusterOperatorName) var createErr error - originalClusterOperatorObj, createErr = c.clusterOperatorClient.ClusterOperators().Create(ctx, &configv1.ClusterOperator{ - ObjectMeta: metav1.ObjectMeta{Name: c.clusterOperatorName}, - }, metav1.CreateOptions{}) + _, createErr = c.clusterOperatorClient.ClusterOperators().Apply(ctx, applyconfigv1.ClusterOperator(c.clusterOperatorName), metav1.ApplyOptions{FieldManager: c.controllerInstanceName}) if apierrors.IsNotFound(createErr) { // this means that the API isn't present. We did not fail. Try again later klog.Infof("ClusterOperator API not created") @@ -164,110 +171,203 @@ func (c StatusSyncer) Sync(ctx context.Context, syncCtx factory.SyncContext) err syncCtx.Recorder().Warningf("StatusCreateFailed", "Failed to create operator status: %v", createErr) return createErr } + // it's ok to create and then ApplyStatus with different content because Appy and ApplyStatus are tracked as different fieldManagers in metadata + // re-enter the loop + return nil } - clusterOperatorObj := originalClusterOperatorObj.DeepCopy() - if detailedSpec.ManagementState == operatorv1.Unmanaged && !management.IsOperatorAlwaysManaged() { - configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, configv1.ClusterOperatorStatusCondition{Type: configv1.OperatorAvailable, Status: configv1.ConditionUnknown, Reason: "Unmanaged"}) - configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, configv1.ClusterOperatorStatusCondition{Type: configv1.OperatorProgressing, Status: configv1.ConditionUnknown, Reason: "Unmanaged"}) - configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, configv1.ClusterOperatorStatusCondition{Type: configv1.OperatorDegraded, Status: configv1.ConditionUnknown, Reason: "Unmanaged"}) - configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, configv1.ClusterOperatorStatusCondition{Type: configv1.OperatorUpgradeable, Status: configv1.ConditionUnknown, Reason: "Unmanaged"}) - configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, configv1.ClusterOperatorStatusCondition{Type: configv1.EvaluationConditionsDetected, Status: configv1.ConditionUnknown, Reason: "Unmanaged"}) + previouslyDesiredStatus, err := applyconfigv1.ExtractClusterOperatorStatus(originalClusterOperatorObj, c.controllerInstanceName) + if err != nil && !apierrors.IsNotFound(err) { + syncCtx.Recorder().Warningf("StatusFailed", "Unable to get extract operator status for clusteroperator/%s: %v", c.clusterOperatorName, err) + return err + } - if equality.Semantic.DeepEqual(clusterOperatorObj, originalClusterOperatorObj) { + if detailedSpec.ManagementState == operatorv1.Unmanaged && !management.IsOperatorAlwaysManaged() { + desiredStatus := applyconfigv1.ClusterOperatorStatus() + desiredStatus.WithConditions( + applyconfigv1.ClusterOperatorStatusCondition(). + WithType(configv1.OperatorAvailable). + WithStatus(configv1.ConditionUnknown). + WithReason("Unmanaged"), + applyconfigv1.ClusterOperatorStatusCondition(). + WithType(configv1.OperatorProgressing). + WithStatus(configv1.ConditionUnknown). + WithReason("Unmanaged"), + applyconfigv1.ClusterOperatorStatusCondition(). + WithType(configv1.OperatorDegraded). + WithStatus(configv1.ConditionUnknown). + WithReason("Unmanaged"), + applyconfigv1.ClusterOperatorStatusCondition(). + WithType(configv1.OperatorUpgradeable). + WithStatus(configv1.ConditionUnknown). + WithReason("Unmanaged"), + applyconfigv1.ClusterOperatorStatusCondition(). + WithType(configv1.EvaluationConditionsDetected). + WithStatus(configv1.ConditionUnknown). + WithReason("Unmanaged"), + ) + operatorv1helpers.SetClusterOperatorApplyConditionsLastTransitionTime(c.clock, &desiredStatus.Conditions, previouslyDesiredStatus.Status.Conditions) + + if equivalent, err := operatorv1helpers.AreClusterOperatorStatusEquivalent(desiredStatus, previouslyDesiredStatus.Status); err != nil { + return fmt.Errorf("unable to compare desired and existing: %w", err) + } else if equivalent { return nil } - if _, err := c.clusterOperatorClient.ClusterOperators().UpdateStatus(ctx, clusterOperatorObj, metav1.UpdateOptions{}); err != nil { + + if _, err := c.clusterOperatorClient.ClusterOperators().ApplyStatus(ctx, applyconfigv1.ClusterOperator(c.Name()).WithStatus(desiredStatus), metav1.ApplyOptions{ + Force: true, + FieldManager: c.controllerInstanceName, + }); err != nil { return err } - if !skipOperatorStatusChangedEvent(originalClusterOperatorObj.Status, clusterOperatorObj.Status) { - syncCtx.Recorder().Eventf("OperatorStatusChanged", "Status for operator %s changed: %s", c.clusterOperatorName, configv1helpers.GetStatusDiff(originalClusterOperatorObj.Status, clusterOperatorObj.Status)) + + if !skipOperatorStatusChangedEvent(previouslyDesiredStatus.Status, desiredStatus) { + syncCtx.Recorder().Eventf("OperatorStatusChanged", "Status for operator %s changed: %s", c.clusterOperatorName, configv1helpers.GetStatusDiff(previouslyDesiredStatus.Status, desiredStatus)) } return nil } + desiredStatus := applyconfigv1.ClusterOperatorStatus() if c.relatedObjectsFunc != nil { isSet, ro := c.relatedObjectsFunc() + + newRelatedObjects := []*applyconfigv1.ObjectReferenceApplyConfiguration{} if !isSet { // temporarily unknown - copy over from existing object - ro = clusterOperatorObj.Status.RelatedObjects + if previouslyDesiredStatus.Status != nil { + for i := range previouslyDesiredStatus.Status.RelatedObjects { + newRelatedObjects = append(newRelatedObjects, &previouslyDesiredStatus.Status.RelatedObjects[i]) + } + } + } else { + for _, obj := range ro { + newRelatedObjects = append(newRelatedObjects, operatorv1helpers.ToApplyClusterOperatorRelatedObj(obj)) + } } // merge in any static objects for _, obj := range c.relatedObjects { + applyObj := operatorv1helpers.ToApplyClusterOperatorRelatedObj(obj) found := false - for _, existingObj := range ro { - if obj == existingObj { + for _, existingObj := range newRelatedObjects { + if applyObj == existingObj { found = true break } } if !found { - ro = append(ro, obj) + newRelatedObjects = append(newRelatedObjects, applyObj) } } - clusterOperatorObj.Status.RelatedObjects = ro + desiredStatus.WithRelatedObjects(newRelatedObjects...) } else { - clusterOperatorObj.Status.RelatedObjects = c.relatedObjects + for _, obj := range c.relatedObjects { + desiredStatus.WithRelatedObjects(operatorv1helpers.ToApplyClusterOperatorRelatedObj(obj)) + } } - configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, UnionClusterCondition(configv1.OperatorDegraded, operatorv1.ConditionFalse, c.degradedInertia, currentDetailedStatus.Conditions...)) - configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, UnionClusterCondition(configv1.OperatorProgressing, operatorv1.ConditionFalse, nil, currentDetailedStatus.Conditions...)) - configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, UnionClusterCondition(configv1.OperatorAvailable, operatorv1.ConditionTrue, nil, currentDetailedStatus.Conditions...)) - configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, UnionClusterCondition(configv1.OperatorUpgradeable, operatorv1.ConditionTrue, nil, currentDetailedStatus.Conditions...)) - configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, UnionClusterCondition(configv1.EvaluationConditionsDetected, operatorv1.ConditionFalse, nil, currentDetailedStatus.Conditions...)) + desiredStatus.WithConditions( + UnionClusterCondition(configv1.OperatorDegraded, operatorv1.ConditionFalse, c.degradedInertia, currentDetailedStatus.Conditions...), + UnionClusterCondition(configv1.OperatorProgressing, operatorv1.ConditionFalse, nil, currentDetailedStatus.Conditions...), + UnionClusterCondition(configv1.OperatorAvailable, operatorv1.ConditionTrue, nil, currentDetailedStatus.Conditions...), + UnionClusterCondition(configv1.OperatorUpgradeable, operatorv1.ConditionTrue, nil, currentDetailedStatus.Conditions...), + UnionClusterCondition(configv1.EvaluationConditionsDetected, operatorv1.ConditionFalse, nil, currentDetailedStatus.Conditions...), + ) - c.syncStatusVersions(clusterOperatorObj, syncCtx) + desiredStatus.WithVersions(c.createNewOperatorVersions(previouslyDesiredStatus.Status, syncCtx)...) // if we have no diff, just return - if equality.Semantic.DeepEqual(clusterOperatorObj, originalClusterOperatorObj) { + if equivalent, err := operatorv1helpers.AreClusterOperatorStatusEquivalent(desiredStatus, previouslyDesiredStatus.Status); err != nil { + return fmt.Errorf("unable to compare desired and existing: %w", err) + } else if equivalent { return nil } - klog.V(2).Infof("clusteroperator/%s diff %v", c.clusterOperatorName, resourceapply.JSONPatchNoError(originalClusterOperatorObj, clusterOperatorObj)) - if _, updateErr := c.clusterOperatorClient.ClusterOperators().UpdateStatus(ctx, clusterOperatorObj, metav1.UpdateOptions{}); updateErr != nil { + if klog.V(2).Enabled() { + previousClusterOperator := &configv1.ClusterOperator{} + if previouslyDesiredStatus != nil { + s, _ := operatorv1helpers.ToClusterOperator(previouslyDesiredStatus.Status) + previousClusterOperator.Status = *s + } + desiredClusterOperator := &configv1.ClusterOperator{} + s, _ := operatorv1helpers.ToClusterOperator(desiredStatus) + desiredClusterOperator.Status = *s + + klog.V(2).Infof("clusteroperator/%s diff %v", c.clusterOperatorName, resourceapply.JSONPatchNoError(previousClusterOperator, desiredClusterOperator)) + } + + if _, updateErr := c.clusterOperatorClient.ClusterOperators().ApplyStatus(ctx, applyconfigv1.ClusterOperator(c.Name()).WithStatus(desiredStatus), metav1.ApplyOptions{ + Force: true, + FieldManager: c.controllerInstanceName, + }); updateErr != nil { return updateErr } - if !skipOperatorStatusChangedEvent(originalClusterOperatorObj.Status, clusterOperatorObj.Status) { - syncCtx.Recorder().Eventf("OperatorStatusChanged", "Status for clusteroperator/%s changed: %s", c.clusterOperatorName, configv1helpers.GetStatusDiff(originalClusterOperatorObj.Status, clusterOperatorObj.Status)) + if !skipOperatorStatusChangedEvent(previouslyDesiredStatus.Status, desiredStatus) { + syncCtx.Recorder().Eventf("OperatorStatusChanged", "Status for clusteroperator/%s changed: %s", c.clusterOperatorName, configv1helpers.GetStatusDiff(previouslyDesiredStatus.Status, desiredStatus)) } return nil } -func skipOperatorStatusChangedEvent(originalStatus, newStatus configv1.ClusterOperatorStatus) bool { - originalCopy := *originalStatus.DeepCopy() - for i, condition := range originalCopy.Conditions { - switch condition.Type { - case configv1.OperatorAvailable, configv1.OperatorDegraded, configv1.OperatorProgressing, configv1.OperatorUpgradeable: - originalCopy.Conditions[i].Message = strings.TrimPrefix(condition.Message, "\ufeff") +func skipOperatorStatusChangedEvent(originalStatus, newStatus *applyconfigv1.ClusterOperatorStatusApplyConfiguration) bool { + originalWithScrubbedConditions := originalStatus + if originalStatus != nil { + originalWithScrubbedConditions = &applyconfigv1.ClusterOperatorStatusApplyConfiguration{ + Conditions: nil, + Versions: originalStatus.Versions, + RelatedObjects: originalStatus.RelatedObjects, + Extension: originalStatus.Extension, + } + + for _, curr := range originalStatus.Conditions { + switch ptr.Deref(curr.Type, "") { + case configv1.OperatorAvailable, configv1.OperatorDegraded, configv1.OperatorProgressing, configv1.OperatorUpgradeable: + scrubbedCondition := &applyconfigv1.ClusterOperatorStatusConditionApplyConfiguration{ + Type: curr.Type, + Status: curr.Status, + LastTransitionTime: curr.LastTransitionTime, + Reason: curr.Reason, + Message: ptr.To(strings.TrimPrefix(ptr.Deref(curr.Message, ""), "\ufeff")), + } + originalWithScrubbedConditions.WithConditions(scrubbedCondition) + default: + originalWithScrubbedConditions.WithConditions(&curr) + } } } - return len(configv1helpers.GetStatusDiff(originalCopy, newStatus)) == 0 + return len(configv1helpers.GetStatusDiff(originalWithScrubbedConditions, newStatus)) == 0 } -func (c *StatusSyncer) syncStatusVersions(clusterOperatorObj *configv1.ClusterOperator, syncCtx factory.SyncContext) { +func (c *StatusSyncer) createNewOperatorVersions(previousStatus *applyconfigv1.ClusterOperatorStatusApplyConfiguration, syncCtx factory.SyncContext) []*applyconfigv1.OperandVersionApplyConfiguration { + ret := []*applyconfigv1.OperandVersionApplyConfiguration{} versions := c.versionGetter.GetVersions() // Add new versions from versionGetter to status for operand, version := range versions { - previousVersion := operatorv1helpers.SetOperandVersion(&clusterOperatorObj.Status.Versions, configv1.OperandVersion{Name: operand, Version: version}) - if previousVersion != version { - // having this message will give us a marker in events when the operator updated compared to when the operand is updated - syncCtx.Recorder().Eventf("OperatorVersionChanged", "clusteroperator/%s version %q changed from %q to %q", c.clusterOperatorName, operand, previousVersion, version) + ret = append(ret, applyconfigv1.OperandVersion().WithName(operand).WithVersion(version)) + + if previousStatus != nil { + previousVersion := operatorv1helpers.FindOperandVersion(previousStatus.Versions, operand) + if previousVersion == nil || ptr.Deref(previousVersion.Version, "") != version { + // having this message will give us a marker in events when the operator updated compared to when the operand is updated + syncCtx.Recorder().Eventf("OperatorVersionChanged", "clusteroperator/%s version %q changed from %q to %q", c.clusterOperatorName, operand, previousVersion, version) + } } } - if !c.removeUnusedVersions { - return + if c.removeUnusedVersions { + return ret } - // Filter out all versions from status that are not in versionGetter - filteredVersions := make([]configv1.OperandVersion, 0, len(clusterOperatorObj.Status.Versions)) - for _, version := range clusterOperatorObj.Status.Versions { - if _, found := versions[version.Name]; found { - filteredVersions = append(filteredVersions, version) + // if we are here, we should keep all existing versions. This seems a little weird, but I don't remember the history + if previousStatus != nil { + for i := range previousStatus.Versions { + previousOperandVersion := previousStatus.Versions[i] + desiredVersion := operatorv1helpers.FindOperandVersionPtr(ret, ptr.Deref(previousOperandVersion.Name, "")) + if desiredVersion == nil { + ret = append(ret, &previousOperandVersion) + } } } - clusterOperatorObj.Status.Versions = filteredVersions + return ret } func (c *StatusSyncer) watchVersionGetterPostRunHook(ctx context.Context, syncCtx factory.SyncContext) error { diff --git a/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/clusteroperator_status_helpers.go b/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/clusteroperator_status_helpers.go new file mode 100644 index 000000000..7fb7350b5 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/clusteroperator_status_helpers.go @@ -0,0 +1,130 @@ +package v1helpers + +import ( + "fmt" + configv1 "github.com/openshift/api/config/v1" + applyconfigv1 "github.com/openshift/client-go/config/applyconfigurations/config/v1" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/util/json" + "slices" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/clock" + "k8s.io/utils/ptr" +) + +func AreClusterOperatorStatusEquivalent(lhs, rhs *applyconfigv1.ClusterOperatorStatusApplyConfiguration) (bool, error) { + CanonicalizeClusterOperatorStatus(lhs) + CanonicalizeClusterOperatorStatus(rhs) + lhsObj, err := ToClusterOperator(lhs) + if err != nil { + return false, err + } + rhsObj, err := ToClusterOperator(rhs) + if err != nil { + return false, err + } + if equality.Semantic.DeepEqual(rhsObj, lhsObj) { + return true, nil + } + + return false, nil +} + +func ToApplyClusterOperatorRelatedObj(in configv1.ObjectReference) *applyconfigv1.ObjectReferenceApplyConfiguration { + return applyconfigv1.ObjectReference().WithName(in.Name).WithNamespace(in.Namespace).WithGroup(in.Group).WithResource(in.Resource) +} + +// ToStaticPodOperator returns the equivalent typed kind for the applyconfiguration. Due to differences in serialization like +// omitempty on strings versus pointers, the returned values can be slightly different. This is an expensive way to diff the +// result, but it is an effective one. +func ToClusterOperator(in *applyconfigv1.ClusterOperatorStatusApplyConfiguration) (*configv1.ClusterOperatorStatus, error) { + if in == nil { + return nil, nil + } + jsonBytes, err := json.Marshal(in) + if err != nil { + return nil, fmt.Errorf("unable to serialize: %w", err) + } + + ret := &configv1.ClusterOperatorStatus{} + if err := json.Unmarshal(jsonBytes, ret); err != nil { + return nil, fmt.Errorf("unable to deserialize: %w", err) + } + + return ret, nil +} + +func SetClusterOperatorApplyConditionsLastTransitionTime(clock clock.PassiveClock, newConditions *[]applyconfigv1.ClusterOperatorStatusConditionApplyConfiguration, oldConditions []applyconfigv1.ClusterOperatorStatusConditionApplyConfiguration) { + if newConditions == nil { + return + } + + now := metav1.NewTime(clock.Now()) + for i := range *newConditions { + newCondition := (*newConditions)[i] + + // if the condition status is the same, then the lastTransitionTime doesn't change + if existingCondition := FindClusterOperatorApplyCondition(oldConditions, newCondition.Type); existingCondition != nil && ptr.Equal(existingCondition.Status, newCondition.Status) { + newCondition.LastTransitionTime = existingCondition.LastTransitionTime + } + + // backstop to handle upgrade case too. If the newCondition doesn't have a lastTransitionTime it needs something + if newCondition.LastTransitionTime == nil { + newCondition.LastTransitionTime = &now + } + + (*newConditions)[i] = newCondition + } +} + +func FindClusterOperatorApplyCondition(haystack []applyconfigv1.ClusterOperatorStatusConditionApplyConfiguration, conditionType *configv1.ClusterStatusConditionType) *applyconfigv1.ClusterOperatorStatusConditionApplyConfiguration { + for i := range haystack { + curr := haystack[i] + if ptr.Equal(curr.Type, conditionType) { + return &curr + } + } + + return nil +} + +func CanonicalizeClusterOperatorStatus(obj *applyconfigv1.ClusterOperatorStatusApplyConfiguration) { + if obj == nil { + return + } + slices.SortStableFunc(obj.Conditions, CompareClusterOperatorConditionByType) + slices.SortStableFunc(obj.Versions, CompareClusterOperatorVersionsByName) + slices.SortStableFunc(obj.RelatedObjects, CompareClusterOperatorRelatedObjects) +} + +func CompareClusterOperatorConditionByType(a, b applyconfigv1.ClusterOperatorStatusConditionApplyConfiguration) int { + return strings.Compare(string(ptr.Deref(a.Type, "")), string(ptr.Deref(b.Type, ""))) +} + +func CompareClusterOperatorVersionsByName(a, b applyconfigv1.OperandVersionApplyConfiguration) int { + if cmp := strings.Compare(ptr.Deref(a.Name, ""), ptr.Deref(b.Name, "")); cmp != 0 { + return cmp + } + if cmp := strings.Compare(ptr.Deref(a.Version, ""), ptr.Deref(b.Version, "")); cmp != 0 { + return cmp + } + return 0 +} + +func CompareClusterOperatorRelatedObjects(a, b applyconfigv1.ObjectReferenceApplyConfiguration) int { + if cmp := strings.Compare(ptr.Deref(a.Group, ""), ptr.Deref(b.Group, "")); cmp != 0 { + return cmp + } + if cmp := strings.Compare(ptr.Deref(a.Resource, ""), ptr.Deref(b.Resource, "")); cmp != 0 { + return cmp + } + if cmp := strings.Compare(ptr.Deref(a.Namespace, ""), ptr.Deref(b.Namespace, "")); cmp != 0 { + return cmp + } + if cmp := strings.Compare(ptr.Deref(a.Name, ""), ptr.Deref(b.Name, "")); cmp != 0 { + return cmp + } + return 0 +} diff --git a/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/helpers.go b/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/helpers.go index e69ec34af..a1fc1e2e3 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/helpers.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/helpers.go @@ -4,13 +4,14 @@ import ( "context" "errors" "fmt" + applyconfigv1 "github.com/openshift/client-go/config/applyconfigurations/config/v1" + "k8s.io/utils/ptr" "os" "sort" "strings" "time" "github.com/google/go-cmp/cmp" - configv1 "github.com/openshift/api/config/v1" operatorv1 "github.com/openshift/api/operator/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" @@ -23,29 +24,25 @@ import ( "sigs.k8s.io/yaml" ) -// SetOperandVersion sets the new version and returns the previous value. -func SetOperandVersion(versions *[]configv1.OperandVersion, operandVersion configv1.OperandVersion) string { +func FindOperandVersion(versions []applyconfigv1.OperandVersionApplyConfiguration, name string) *applyconfigv1.OperandVersionApplyConfiguration { if versions == nil { - versions = &[]configv1.OperandVersion{} + return nil } - existingVersion := FindOperandVersion(*versions, operandVersion.Name) - if existingVersion == nil { - *versions = append(*versions, operandVersion) - return "" + for i := range versions { + if ptr.Deref(versions[i].Name, "") == name { + return &versions[i] + } } - - previous := existingVersion.Version - existingVersion.Version = operandVersion.Version - return previous + return nil } -func FindOperandVersion(versions []configv1.OperandVersion, name string) *configv1.OperandVersion { +func FindOperandVersionPtr(versions []*applyconfigv1.OperandVersionApplyConfiguration, name string) *applyconfigv1.OperandVersionApplyConfiguration { if versions == nil { return nil } for i := range versions { - if versions[i].Name == name { - return &versions[i] + if ptr.Deref(versions[i].Name, "") == name { + return versions[i] } } return nil diff --git a/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/canonicalize.go b/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/operator_status_helpers.go similarity index 100% rename from vendor/github.com/openshift/library-go/pkg/operator/v1helpers/canonicalize.go rename to vendor/github.com/openshift/library-go/pkg/operator/v1helpers/operator_status_helpers.go diff --git a/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/test_helpers.go b/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/test_helpers.go index 5f37e5255..60dcb09bc 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/test_helpers.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/test_helpers.go @@ -183,6 +183,12 @@ func (c *fakeStaticPodOperatorClient) ApplyStaticPodOperatorSpec(ctx context.Con } func (c *fakeStaticPodOperatorClient) ApplyStaticPodOperatorStatus(ctx context.Context, fieldManager string, applyConfiguration *applyoperatorv1.StaticPodOperatorStatusApplyConfiguration) (err error) { + if c.triggerStatusUpdateError != nil { + operatorStatus := convertStaticPodOperatorStatusApplyConfiguration(applyConfiguration) + if err := c.triggerStatusUpdateError("", operatorStatus); err != nil { + return err + } + } c.fakeStaticPodOperatorStatus = convertStaticPodOperatorStatusApplyConfiguration(applyConfiguration) return nil } diff --git a/vendor/modules.txt b/vendor/modules.txt index 0b47a0008..3e9c4d58d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -309,7 +309,7 @@ github.com/openshift/client-go/user/applyconfigurations/internal github.com/openshift/client-go/user/applyconfigurations/user/v1 github.com/openshift/client-go/user/clientset/versioned/scheme github.com/openshift/client-go/user/clientset/versioned/typed/user/v1 -# github.com/openshift/library-go v0.0.0-20241016205830-b3a7a46e7136 +# github.com/openshift/library-go v0.0.0-20241016205830-b3a7a46e7136 => github.com/deads2k/library-go v0.0.0-20241022211154-c774bfa7f641 ## explicit; go 1.22.0 github.com/openshift/library-go/pkg/apiserver/jsonpatch github.com/openshift/library-go/pkg/apps/deployment @@ -1471,3 +1471,4 @@ sigs.k8s.io/structured-merge-diff/v4/value ## explicit; go 1.12 sigs.k8s.io/yaml sigs.k8s.io/yaml/goyaml.v2 +# github.com/openshift/library-go => github.com/deads2k/library-go v0.0.0-20241022211154-c774bfa7f641