From 11e99bd2da4c23ef8e714a97ab6eeeff69c4e9e1 Mon Sep 17 00:00:00 2001 From: Jan Steinke Date: Fri, 14 Jan 2022 16:28:57 +0100 Subject: [PATCH] autogenerate CRDs Co-authored-by: Guus van Weelden --- go.mod | 2 +- manifests/embed.go | 13 + .../etcd.database.coreos.com_etcdbackups.yaml | 196 +++ ...etcd.database.coreos.com_etcdclusters.yaml | 1321 +++++++++++++++++ ...etcd.database.coreos.com_etcdrestores.yaml | 155 ++ pkg/controller/backup-operator/operator.go | 2 +- pkg/controller/controller.go | 2 +- pkg/controller/restore-operator/operator.go | 2 +- pkg/util/k8sutil/crd.go | 55 +- pkg/util/k8sutil/crd_test.go | 42 + 10 files changed, 1760 insertions(+), 30 deletions(-) create mode 100644 manifests/embed.go create mode 100644 manifests/etcd.database.coreos.com_etcdbackups.yaml create mode 100644 manifests/etcd.database.coreos.com_etcdclusters.yaml create mode 100644 manifests/etcd.database.coreos.com_etcdrestores.yaml create mode 100644 pkg/util/k8sutil/crd_test.go diff --git a/go.mod b/go.mod index ba7be021c..8a061d5ed 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/coreos/etcd-operator -go 1.15 +go 1.16 require ( cloud.google.com/go/storage v1.6.0 diff --git a/manifests/embed.go b/manifests/embed.go new file mode 100644 index 000000000..122cfadcc --- /dev/null +++ b/manifests/embed.go @@ -0,0 +1,13 @@ +// Package manifests has the CRD manifests needed to bootstrap the operator +// the manifest files in this directory are autogenerated from the CRDs in +// this repository with +// +// ``` +// controller-gen crd paths=./pkg/apis/etcd/v1beta2/... output:crd:artifacts:config=./manifests +// ``` +package manifests + +import "embed" + +//go:embed *.yaml +var FS embed.FS diff --git a/manifests/etcd.database.coreos.com_etcdbackups.yaml b/manifests/etcd.database.coreos.com_etcdbackups.yaml new file mode 100644 index 000000000..98522b21c --- /dev/null +++ b/manifests/etcd.database.coreos.com_etcdbackups.yaml @@ -0,0 +1,196 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: etcdbackups.etcd.database.coreos.com +spec: + group: etcd.database.coreos.com + names: + kind: EtcdBackup + listKind: EtcdBackupList + plural: etcdbackups + singular: etcdbackup + scope: Namespaced + versions: + - name: v1beta2 + schema: + openAPIV3Schema: + description: EtcdBackup represents a Kubernetes EtcdBackup Custom Resource. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: BackupSpec contains a backup specification for an etcd cluster. + properties: + abs: + description: ABS defines the ABS backup source spec. + properties: + absSecret: + description: The name of the secret object that stores the Azure + storage credential + type: string + path: + description: 'Path is the full abs path where the backup is saved. + The format of the path must be: "/" + e.g: "myabscontainer/etcd.backup"' + type: string + type: object + allowSelfSignedCertificates: + description: If AllowSelfSignedCertificates is true, set the InsecureSkipVerify + flag for TLS connections + type: boolean + backupPolicy: + description: BackupPolicy configures the backup process. + properties: + backupIntervalInSecond: + description: BackupIntervalInSecond is to specify how often operator + take snapshot 0 is magic number to indicate one-shot backup + format: int64 + type: integer + maxBackups: + description: MaxBackups is to specify how many backups we want + to keep 0 is magic number to indicate un-limited backups + type: integer + timeoutInSecond: + description: TimeoutInSecond is the maximal allowed time in second + of the entire backup process. + format: int64 + type: integer + type: object + clientTLSSecret: + description: 'ClientTLSSecret is the secret containing the etcd TLS + client certs and must contain the following data items: data: "etcd-client.crt": + "etcd-client.key": "etcd-client-ca.crt": + ' + type: string + etcdEndpoints: + description: EtcdEndpoints specifies the endpoints of an etcd cluster. + When multiple endpoints are given, the backup operator retrieves + the backup from the endpoint that has the most up-to-date state. + The given endpoints must belong to the same etcd cluster. + items: + type: string + type: array + gcs: + description: GCS defines the GCS backup source spec. + properties: + gcpSecret: + description: "The name of the secret object that stores the Google + storage credential containing at most ONE of the following: + An access token with file name of 'access-token'. JSON credentials + with file name of 'credentials.json'. \n If omitted, client + will use the default application credentials." + type: string + path: + description: 'Path is the full GCS path where the backup is saved. + The format of the path must be: "/" + e.g: "mygcsbucket/etcd.backup"' + type: string + type: object + oss: + description: OSS defines the OSS backup source spec. + properties: + endpoint: + description: "Endpoint is the OSS service endpoint on alibaba + cloud, defaults to \"http://oss-cn-hangzhou.aliyuncs.com\". + \n Details about regions and endpoints, see: https://www.alibabacloud.com/help/doc-detail/31837.htm" + type: string + ossSecret: + description: "The name of the secret object that stores the credential + which will be used to access Alibaba Cloud OSS. \n The secret + must contain the following keys/fields: accessKeyID accessKeySecret + \n The format of secret: \n apiVersion: v1 kind: Secret metadata: + name: type: Opaque data: accessKeyID: accessKeySecret: " + type: string + path: + description: 'Path is the full abs path where the backup is saved. + The format of the path must be: "/" + e.g: "mybucket/etcd.backup"' + type: string + type: object + s3: + description: S3 defines the S3 backup source spec. + properties: + awsSecret: + description: "The name of the secret object that stores the AWS + credential and config files. The file name of the credential + MUST be 'credentials'. The file name of the config MUST be 'config'. + The profile to use in both files will be 'default'. \n AWSSecret + overwrites the default etcd operator wide AWS credential and + config." + type: string + endpoint: + description: Endpoint if blank points to aws. If specified, can + point to s3 compatible object stores. + type: string + forcePathStyle: + description: ForcePathStyle forces to use path style over the + default subdomain style. This is useful when you have an s3 + compatible endpoint that doesn't support subdomain buckets. + type: boolean + path: + description: 'Path is the full s3 path where the backup is saved. + The format of the path must be: "/" + e.g: "mybucket/etcd.backup"' + type: string + type: object + storageType: + description: StorageType is the etcd backup storage type. We need + this field because CRD doesn't support validation against invalid + fields and we cannot verify invalid backup storage source. + type: string + type: object + status: + description: BackupStatus represents the status of the EtcdBackup Custom + Resource. + properties: + Reason: + description: Reason indicates the reason for any backup related failures. + type: string + etcdRevision: + description: EtcdRevision is the revision of etcd's KV store where + the backup is performed on. + format: int64 + type: integer + etcdVersion: + description: EtcdVersion is the version of the backup etcd server. + type: string + lastExecutionDate: + description: Last execution date. First it will be creation timestamp, + later on it will be last execution date despite successful or failed + run. This field is used when pod is restarted ticked should be create + from this timestamp not current timestamp + format: date-time + type: string + lastSuccessDate: + description: LastSuccessDate indicate the time to get snapshot last + time + format: date-time + type: string + succeeded: + description: Succeeded indicates if the backup has Succeeded. + type: boolean + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/manifests/etcd.database.coreos.com_etcdclusters.yaml b/manifests/etcd.database.coreos.com_etcdclusters.yaml new file mode 100644 index 000000000..d0c13a634 --- /dev/null +++ b/manifests/etcd.database.coreos.com_etcdclusters.yaml @@ -0,0 +1,1321 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: etcdclusters.etcd.database.coreos.com +spec: + group: etcd.database.coreos.com + names: + kind: EtcdCluster + listKind: EtcdClusterList + plural: etcdclusters + singular: etcdcluster + scope: Namespaced + versions: + - name: v1beta2 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + TLS: + description: etcd cluster TLS configuration + properties: + static: + description: StaticTLS enables user to generate static x509 certificates + and keys, put them into Kubernetes secrets, and specify them + into here. + properties: + member: + description: Member contains secrets containing TLS certs + used by each etcd member pod. + properties: + peerSecret: + description: PeerSecret is the secret containing TLS certs + used by each etcd member pod for the communication between + etcd peers. + type: string + serverSecret: + description: ServerSecret is the secret containing TLS + certs used by each etcd member pod for the communication + between etcd server and its clients. + type: string + type: object + operatorSecret: + description: OperatorSecret is the secret containing TLS certs + used by operator to talk securely to this cluster. + type: string + type: object + type: object + paused: + description: Paused is to pause the control of the operator for the + etcd cluster. + type: boolean + pod: + description: "Pod defines the policy to create pod for the etcd pod. + \n Updating Pod does not take effect on any existing etcd pods." + properties: + ClusterDomain: + description: ClusterDomain is the cluster domain to use for member + URLs E.g. '.cluster.local'. The default is to not set a cluster + domain explicitly. + type: string + DNSTimeoutInSecond: + description: DNSTimeoutInSecond is the maximum allowed time for + the init container of the etcd pod to reverse DNS lookup its + IP given the hostname. The default is to wait indefinitely and + has a vaule of 0. + format: int64 + type: integer + affinity: + description: The scheduling constraints on etcd pods. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node matches the corresponding matchExpressions; + the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects + (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from + its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them are + ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to a pod label update), + the system may or may not try to eventually evict the + pod from its node. When there are multiple elements, + the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node that + violates one or more of the expressions. The node that + is most preferred is the one with the greatest sum of + weights, i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + anti-affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the pod + will not be scheduled onto the node. If the anti-affinity + requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod + label update), the system may or may not try to eventually + evict the pod from its node. When there are multiple + elements, the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + description: Annotations specifies the annotations to attach to + pods the operator creates for the etcd cluster. The "etcd.version" + annotation is reserved for the internal use of the etcd operator. + type: object + antiAffinity: + description: '**DEPRECATED**. Use Affinity instead.' + type: boolean + busyboxImage: + description: 'busybox init container image. default is busybox:1.28.0-glibc + busybox:latest uses uclibc which contains a bug that sometimes + prevents name resolution More info: https://github.com/docker-library/busybox/issues/27' + type: string + etcdEnv: + description: List of environment variables to set in the etcd + container. This is used to configure etcd process. etcd cluster + cannot be created, when bad environement variables are provided. + Do not overwrite any flags used to bootstrap the cluster (for + example `--initial-cluster` flag). This field cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previous defined environment variables in the + container and any service environment variables. If a + variable cannot be resolved, the reference in the input + string will be unchanged. The $(VAR_NAME) syntax can be + escaped with a double $$, ie: $$(VAR_NAME). Escaped references + will never be expanded, regardless of whether the variable + exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + labels: + additionalProperties: + type: string + description: Labels specifies the labels to attach to pods the + operator creates for the etcd cluster. "app" and "etcd_*" labels + are reserved for the internal use of the etcd operator. Do not + overwrite them. + type: object + nodeSelector: + additionalProperties: + type: string + description: NodeSelector specifies a map of key-value pairs. + For the pod to be eligible to run on a node, the node must have + each of the indicated key-value pairs as labels. + type: object + persistentVolumeClaimSpec: + description: PersistentVolumeClaimSpec is the spec to describe + PVC for the etcd container This field is optional. If no PVC + spec, etcd container will use emptyDir as volume Note. This + feature is in alpha stage. It is currently only used as non-stable + storage, not the stable storage. Future work need to make it + used as stable storage. + properties: + accessModes: + description: 'AccessModes contains the desired access modes + the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'This field can be used to specify either: * + An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) * An existing + custom resource that implements data population (Alpha) + In order to use custom resource types that implement data + population, the AnyVolumeDataSource feature gate must be + enabled. If the provisioner or an external controller can + support the specified data source, it will create a new + volume based on the contents of the specified data source.' + properties: + apiGroup: + description: APIGroup is the group for the resource being + referenced. If APIGroup is not specified, the specified + Kind must be in the core API group. For any other third-party + types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + resources: + description: 'Resources represents the minimum resources the + volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of + compute resources required. If Requests is omitted for + a container, it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + selector: + description: A label query over volumes to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. + This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + storageClassName: + description: 'Name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume is required + by the claim. Value of Filesystem is implied when not included + in claim spec. + type: string + volumeName: + description: VolumeName is the binding reference to the PersistentVolume + backing this claim. + type: string + type: object + resources: + description: Resources is the resource requirements for the etcd + container. This field cannot be updated once the cluster is + created. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + securityContext: + description: 'SecurityContext specifies the security context for + the entire pod More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context' + properties: + fsGroup: + description: "A special supplemental group that applies to + all containers in a pod. Some volume types allow the Kubelet + to change the ownership of that volume to be owned by the + pod: \n 1. The owning GID will be the FSGroup 2. The setgid + bit is set (new files created in the volume will be owned + by FSGroup) 3. The permission bits are OR'd with rw-rw---- + \n If unset, the Kubelet will not modify the ownership and + permissions of any volume." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. This field will only apply to volume types which + support fsGroup based ownership(and permissions). It will + have no effect on ephemeral volume types such as: secret, + configmaps and emptydir. Valid values are "OnRootMismatch" + and "Always". If not specified, "Always" is used.' + type: string + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in SecurityContext. If set + in both SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for that container. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + SecurityContext. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence + for that container. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers + in this pod. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process + run in each container, in addition to the container's primary + GID. If unspecified, no groups will be added to any container. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used + for the pod. Pods with unsupported sysctls (by the container + runtime) might fail to launch. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options within a container's + SecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + tmpfs: + description: Sets the 'emptyDir.medium' field for the etcd-data + volume to "Memory". The default is to not use memory as the + storage medium No effect if persistent volume is used + type: boolean + tolerations: + description: Tolerations specifies the pod's tolerations. + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + type: object + repository: + description: "Repository is the name of the repository that hosts + etcd container images. It should be direct clone of the repository + in official release: https://github.com/coreos/etcd/releases That + means, it should have exact same tags and the same meaning for the + tags. \n By default, it is `quay.io/coreos/etcd`." + type: string + service: + description: Service defines the policy to create etcd services + properties: + annotations: + additionalProperties: + type: string + description: Annotations specifies the annotations to attach to + services the operator creates for the etcd cluster. + type: object + name: + description: Overrides the name of the service created + type: string + selector: + additionalProperties: + type: string + description: 'Overrides the routing service traffic to pods with + label keys and values matching this selector. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + type: object + size: + description: Size is the expected size of the etcd cluster. The etcd-operator + will eventually make the size of the running cluster equal to the + expected size. The vaild range of the size is from 1 to 7. + type: integer + version: + description: "Version is the expected version of the etcd cluster. + The etcd-operator will eventually make the etcd cluster version + equal to the expected version. \n The version must follow the [semver]( + http://semver.org) format, for example \"3.2.13\". Only etcd released + versions are supported: https://github.com/coreos/etcd/releases + \n If version is not set, default is \"v3.2.13\"." + type: string + type: object + status: + properties: + clientPort: + description: ClientPort is the port for etcd client to access. It's + the same on client LB service and etcd nodes. + type: integer + conditions: + description: Condition keeps track of all cluster conditions, if they + exist. + items: + description: ClusterCondition represents one current condition of + an etcd cluster. A condition might not show up if it is not happening. + For example, if a cluster is not upgrading, the Upgrading condition + would not show up. If a cluster is upgrading and encountered a + problem that prevents the upgrade, the Upgrading condition's status + will would be False and communicate the problem back. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + type: string + lastUpdateTime: + description: The last time this condition was updated. + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of cluster condition. + type: string + type: object + type: array + controlPaused: + description: ControlPuased indicates the operator pauses the control + of the cluster. + type: boolean + currentVersion: + description: CurrentVersion is the current cluster version + type: string + members: + description: Members are the etcd members in the cluster + properties: + ready: + description: Ready are the etcd members that are ready to serve + requests The member names are the same as the etcd pod names + items: + type: string + type: array + unready: + description: Unready are the etcd members not ready to serve requests + items: + type: string + type: array + type: object + phase: + description: Phase is the cluster running phase + type: string + reason: + type: string + serviceName: + description: ServiceName is the LB service for accessing etcd nodes. + type: string + size: + description: Size is the current size of the cluster + type: integer + targetVersion: + description: TargetVersion is the version the cluster upgrading to. + If the cluster is not upgrading, TargetVersion is empty. + type: string + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/manifests/etcd.database.coreos.com_etcdrestores.yaml b/manifests/etcd.database.coreos.com_etcdrestores.yaml new file mode 100644 index 000000000..dde5c8d21 --- /dev/null +++ b/manifests/etcd.database.coreos.com_etcdrestores.yaml @@ -0,0 +1,155 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: etcdrestores.etcd.database.coreos.com +spec: + group: etcd.database.coreos.com + names: + kind: EtcdRestore + listKind: EtcdRestoreList + plural: etcdrestores + singular: etcdrestore + scope: Namespaced + versions: + - name: v1beta2 + schema: + openAPIV3Schema: + description: EtcdRestore represents a Kubernetes EtcdRestore Custom Resource. + The EtcdRestore CR name will be used as the name of the new restored cluster. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: RestoreSpec defines how to restore an etcd cluster from existing + backup. + properties: + abs: + description: ABS tells where on ABS the backup is saved and how to + fetch the backup. + properties: + absSecret: + description: The name of the secret object that stores the Azure + Blob Storage credential. + type: string + path: + description: 'Path is the full abs path where the backup is saved. + The format of the path must be: "/" + e.g: "myabscontainer/etcd.backup"' + type: string + type: object + backupStorageType: + description: BackupStorageType is the type of the backup storage which + is used as RestoreSource. + type: string + etcdCluster: + description: EtcdCluster references an EtcdCluster resource whose + metadata and spec will be used to create the new restored EtcdCluster + CR. This reference EtcdCluster CR and all its resources will be + deleted before the restored EtcdCluster CR is created. + properties: + name: + description: Name is the EtcdCluster resource name. This reference + EtcdCluster must be present in the same namespace as the restore-operator + type: string + type: object + gcs: + description: GCS tells where on GCS the backup is saved and how to + fetch the backup. + properties: + gcpSecret: + description: "The name of the secret object that stores the Google + storage credential containing at most ONE of the following: + An access token with file name of 'access-token'. JSON credentials + with file name of 'credentials.json'. \n If omitted, client + will use the default application credentials." + type: string + path: + description: 'Path is the full GCS path where the backup is saved. + The format of the path must be: "/" + e.g: "mygcsbucket/etcd.backup"' + type: string + type: object + oss: + description: OSS tells where on OSS the backup is saved and how to + fetch the backup. + properties: + endpoint: + description: "Endpoint is the OSS service endpoint on alibaba + cloud, defaults to \"http://oss-cn-hangzhou.aliyuncs.com\". + \n Details about regions and endpoints, see: https://www.alibabacloud.com/help/doc-detail/31837.htm" + type: string + ossSecret: + description: "The name of the secret object that stores the credential + which will be used to access Alibaba Cloud OSS. \n The secret + must contain the following keys/fields: accessKeyID accessKeySecret + \n The format of secret: \n apiVersion: v1 kind: Secret metadata: + name: type: Opaque data: accessKeyID: accessKeySecret: " + type: string + path: + description: 'Path is the full abs path where the backup is saved. + The format of the path must be: "/" + e.g: "myossbucket/etcd.backup"' + type: string + type: object + s3: + description: S3 tells where on S3 the backup is saved and how to fetch + the backup. + properties: + awsSecret: + description: "The name of the secret object that stores the AWS + credential and config files. The file name of the credential + MUST be 'credentials'. The file name of the config MUST be 'config'. + The profile to use in both files will be 'default'. \n AWSSecret + overwrites the default etcd operator wide AWS credential and + config." + type: string + endpoint: + description: Endpoint if blank points to aws. If specified, can + point to s3 compatible object stores. + type: string + forcePathStyle: + description: ForcePathStyle forces to use path style over the + default subdomain style. This is useful when you have an s3 + compatible endpoint that doesn't support subdomain buckets. + type: boolean + path: + description: 'Path is the full s3 path where the backup is saved. + The format of the path must be: "/" + e.g: "mybucket/etcd.backup"' + type: string + type: object + type: object + status: + description: RestoreStatus reports the status of this restore operation. + properties: + reason: + description: Reason indicates the reason for any backup related failures. + type: string + succeeded: + description: Succeeded indicates if the backup has Succeeded. + type: boolean + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/pkg/controller/backup-operator/operator.go b/pkg/controller/backup-operator/operator.go index 1b7d69dad..ea683448d 100644 --- a/pkg/controller/backup-operator/operator.go +++ b/pkg/controller/backup-operator/operator.go @@ -94,7 +94,7 @@ func (b *Backup) Start(ctx context.Context) error { } func (b *Backup) initCRD(ctx context.Context) error { - err := k8sutil.CreateCRD(ctx, b.kubeExtCli, api.EtcdBackupCRDName, api.EtcdBackupResourceKind, api.EtcdBackupResourcePlural, "") + err := k8sutil.CreateCRD(ctx, b.kubeExtCli, api.EtcdBackupResourcePlural) if err != nil { return fmt.Errorf("failed to create CRD: %v", err) } diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 9ab40ae27..c89433d90 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -129,7 +129,7 @@ func (c *Controller) makeClusterConfig() cluster.Config { } func (c *Controller) initCRD(ctx context.Context) error { - err := k8sutil.CreateCRD(ctx, c.KubeExtCli, api.EtcdClusterCRDName, api.EtcdClusterResourceKind, api.EtcdClusterResourcePlural, "etcd") + err := k8sutil.CreateCRD(ctx, c.KubeExtCli, api.EtcdClusterResourcePlural) if err != nil { return fmt.Errorf("failed to create CRD: %v", err) } diff --git a/pkg/controller/restore-operator/operator.go b/pkg/controller/restore-operator/operator.go index dfbf90366..8c6edc201 100644 --- a/pkg/controller/restore-operator/operator.go +++ b/pkg/controller/restore-operator/operator.go @@ -90,7 +90,7 @@ func (r *Restore) Start(ctx context.Context) error { } func (r *Restore) initCRD(ctx context.Context) error { - err := k8sutil.CreateCRD(ctx, r.kubeExtCli, api.EtcdRestoreCRDName, api.EtcdRestoreResourceKind, api.EtcdRestoreResourcePlural, "") + err := k8sutil.CreateCRD(ctx, r.kubeExtCli, api.EtcdRestoreResourcePlural) if err != nil { return fmt.Errorf("failed to create CRD: %v", err) } diff --git a/pkg/util/k8sutil/crd.go b/pkg/util/k8sutil/crd.go index 79da1eb7a..413072f6a 100644 --- a/pkg/util/k8sutil/crd.go +++ b/pkg/util/k8sutil/crd.go @@ -20,12 +20,15 @@ import ( "fmt" "time" + "github.com/coreos/etcd-operator/manifests" api "github.com/coreos/etcd-operator/pkg/apis/etcd/v1beta2" "github.com/coreos/etcd-operator/pkg/util/retryutil" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/client-go/rest" ) @@ -52,46 +55,46 @@ func listClustersURI(ns string) string { return fmt.Sprintf("/apis/%s/namespaces/%s/%s", api.SchemeGroupVersion.String(), ns, api.EtcdClusterResourcePlural) } -func CreateCRD(ctx context.Context, clientset apiextensionsclient.Interface, crdName, rkind, rplural, shortName string) error { - crd := &apiextensionsv1beta1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: crdName, - }, - Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ - Group: api.SchemeGroupVersion.Group, - Version: api.SchemeGroupVersion.Version, - Scope: apiextensionsv1beta1.NamespaceScoped, - Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ - Plural: rplural, - Kind: rkind, - }, - }, - } - if len(shortName) != 0 { - crd.Spec.Names.ShortNames = []string{shortName} +func CreateCRD(ctx context.Context, clientset apiextensionsclient.Interface, crdName string) error { + scheme := runtime.NewScheme() + _ = apiextensionsv1.AddToScheme(scheme) + decode := serializer.NewCodecFactory(scheme).UniversalDeserializer().Decode + stream, err := manifests.FS.ReadFile(fmt.Sprintf("etcd.database.coreos.com_%s.yaml", crdName)) + if err != nil { + return err } - _, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(ctx, crd, metav1.CreateOptions{}) - if err != nil && !IsKubernetesResourceAlreadyExistError(err) { + obj, _, err := decode(stream, nil, nil) + if err != nil { return err } + switch o := obj.(type) { + case *apiextensionsv1.CustomResourceDefinition: + _, err := clientset.ApiextensionsV1().CustomResourceDefinitions().Create(ctx, o, metav1.CreateOptions{}) + if err != nil && !IsKubernetesResourceAlreadyExistError(err) { + return err + } + default: + return fmt.Errorf("wrong k8s resource type") + } + return nil } func WaitCRDReady(ctx context.Context, clientset apiextensionsclient.Interface, crdName string) error { err := retryutil.Retry(5*time.Second, 20, func() (bool, error) { - crd, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Get(ctx, crdName, metav1.GetOptions{}) + crd, err := clientset.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, crdName, metav1.GetOptions{}) if err != nil { return false, err } for _, cond := range crd.Status.Conditions { switch cond.Type { - case apiextensionsv1beta1.Established: - if cond.Status == apiextensionsv1beta1.ConditionTrue { + case apiextensionsv1.Established: + if cond.Status == apiextensionsv1.ConditionTrue { return true, nil } - case apiextensionsv1beta1.NamesAccepted: - if cond.Status == apiextensionsv1beta1.ConditionFalse { - return false, fmt.Errorf("Name conflict: %v", cond.Reason) + case apiextensionsv1.NamesAccepted: + if cond.Status == apiextensionsv1.ConditionFalse { + return false, fmt.Errorf("name conflict: %v", cond.Reason) } } } diff --git a/pkg/util/k8sutil/crd_test.go b/pkg/util/k8sutil/crd_test.go new file mode 100644 index 000000000..00ebc3a4c --- /dev/null +++ b/pkg/util/k8sutil/crd_test.go @@ -0,0 +1,42 @@ +package k8sutil + +import ( + "context" + "testing" + + "github.com/coreos/etcd-operator/pkg/apis/etcd/v1beta2" + "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func TestCreateCRD(t *testing.T) { + cs := fake.NewSimpleClientset() + + err := CreateCRD(context.Background(), cs, v1beta2.EtcdBackupResourcePlural) + if err != nil { + t.Errorf("create CRD failed: %v", err) + } + + actions := cs.Actions() + + actual_num_actions := len(actions) + expected_num_actions := 1 + if actual_num_actions != expected_num_actions { + t.Errorf("expect actions=%d, got=%d", expected_num_actions, actual_num_actions) + } + + actual_resource := actions[0].GetResource() + expected_resource := schema.GroupVersionResource{ + Group: "apiextensions.k8s.io", + Version: "v1", + Resource: "customresourcedefinitions", + } + if actual_resource != expected_resource { + t.Errorf("expect action=%#v, got=%#v", expected_resource, actual_resource) + } + actual_verb := actions[0].GetVerb() + expected_verb := "create" + if actual_verb != expected_verb { + t.Errorf("expect action=%s, got=%s", expected_verb, actual_verb) + } +}