diff --git a/cmd/controller-manager/app/controllers.go b/cmd/controller-manager/app/controllers.go index d792f5928e70a5470b90748a0ded6f4d2f8deeee..0b1fb520f6145e8fe875cfa7497b2d73eb9fec03 100644 --- a/cmd/controller-manager/app/controllers.go +++ b/cmd/controller-manager/app/controllers.go @@ -37,6 +37,7 @@ import ( "kubesphere.io/kubesphere/pkg/controller/network/ippool" "kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy" "kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy/provider" + "kubesphere.io/kubesphere/pkg/controller/notification" "kubesphere.io/kubesphere/pkg/controller/pipeline" "kubesphere.io/kubesphere/pkg/controller/s2ibinary" "kubesphere.io/kubesphere/pkg/controller/s2irun" @@ -284,6 +285,11 @@ func addControllers( if multiClusterEnabled { controllers["globalrole-controller"] = globalRoleController + notificationController, err := notification.NewController(client.Kubernetes(), mgr.GetClient(), mgr.GetCache()) + if err != nil { + return err + } + controllers["notification-controller"] = notificationController } for name, ctrl := range controllers { diff --git a/config/crds/notification.kubesphere.io_configs.yaml b/config/crds/notification.kubesphere.io_configs.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d86a2b6f81bf0e7048142633a8f8f1c168cc754d --- /dev/null +++ b/config/crds/notification.kubesphere.io_configs.yaml @@ -0,0 +1,283 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: configs.notification.kubesphere.io +spec: + group: notification.kubesphere.io + names: + categories: + - notification-manager + kind: Config + listKind: ConfigList + plural: configs + shortNames: + - nc + singular: config + scope: Cluster + versions: + - name: v2beta1 + schema: + openAPIV3Schema: + description: DingTalkConfig is the Schema for the dingtalkconfigs API + 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: ConfigSpec defines the desired state of Config + properties: + dingtalk: + properties: + conversation: + description: Only needed when send alerts to the conversation. + properties: + appkey: + description: The key of the application with which to send messages. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + appsecret: + description: The key in the secret to be used. Must be a valid secret key. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + type: object + labels: + additionalProperties: + type: string + type: object + type: object + email: + properties: + authIdentify: + description: The identity for PLAIN authentication. + type: string + authPassword: + description: The secret contains the SMTP password for LOGIN and PLAIN authentications. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + authSecret: + description: The secret contains the SMTP secret for CRAM-MD5 authentication. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + authUsername: + description: The username for CRAM-MD5, LOGIN and PLAIN authentications. + type: string + from: + description: The sender address. + type: string + hello: + description: The hostname to use when identifying to the SMTP server. + type: string + labels: + additionalProperties: + type: string + type: object + requireTLS: + description: The default SMTP TLS requirement. + type: boolean + smartHost: + description: The address of the SMTP server. + properties: + host: + type: string + port: + type: integer + required: + - host + - port + type: object + tls: + description: TLSConfig configures the options for TLS connections. + properties: + clientCertificate: + description: The certificate of the client. + properties: + cert: + description: The client cert file for the targets. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + key: + description: The client key file for the targets. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + type: object + insecureSkipVerify: + description: Disable target certificate validation. + type: boolean + rootCA: + description: RootCA defines the root certificate authorities that clients use when verifying server certificates. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + serverName: + description: Used to verify the hostname for the targets. + type: string + type: object + required: + - from + - smartHost + type: object + slack: + properties: + labels: + additionalProperties: + type: string + type: object + slackTokenSecret: + description: The token of user or bot. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + type: object + webhook: + properties: + labels: + additionalProperties: + type: string + type: object + type: object + wechat: + properties: + labels: + additionalProperties: + type: string + type: object + wechatApiAgentId: + description: The id of the application which sending message. + type: string + wechatApiCorpId: + description: The corp id for authentication. + type: string + wechatApiSecret: + description: The API key to use when talking to the WeChat API. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + wechatApiUrl: + description: The WeChat API URL. + type: string + required: + - wechatApiAgentId + - wechatApiCorpId + - wechatApiSecret + type: object + type: object + status: + description: ConfigStatus defines the observed state of Config + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/notification.kubesphere.io_receivers.yaml b/config/crds/notification.kubesphere.io_receivers.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4b48a61d3f5aeff1ec2c193c06d7a9e8609a5746 --- /dev/null +++ b/config/crds/notification.kubesphere.io_receivers.yaml @@ -0,0 +1,590 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: receivers.notification.kubesphere.io +spec: + group: notification.kubesphere.io + names: + categories: + - notification-manager + kind: Receiver + listKind: ReceiverList + plural: receivers + shortNames: + - nr + singular: receiver + scope: Cluster + versions: + - name: v2beta1 + schema: + openAPIV3Schema: + description: Receiver is the Schema for the receivers API + 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: ReceiverSpec defines the desired state of Receiver + properties: + dingtalk: + properties: + alertSelector: + description: Selector to filter alerts. + 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 + chatbot: + description: Be careful, a ChatBot only can send 20 message per minute. + properties: + keywords: + description: Custom keywords of ChatBot + items: + type: string + type: array + secret: + description: Secret of ChatBot, you can get it after enabled Additional Signature of ChatBot. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + webhook: + description: The webhook of ChatBot which the message will send to. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + required: + - webhook + type: object + conversation: + description: The conversation which message will send to. + properties: + chatids: + items: + type: string + type: array + required: + - chatids + type: object + dingtalkConfigSelector: + description: DingTalkConfig to be selected for this receiver + 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 + enabled: + description: whether the receiver is enabled + type: boolean + type: object + email: + properties: + alertSelector: + description: Selector to filter alerts. + 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 + emailConfigSelector: + description: EmailConfig to be selected for this receiver + 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 + enabled: + description: whether the receiver is enabled + type: boolean + to: + description: Receivers' email addresses + items: + type: string + type: array + required: + - to + type: object + slack: + properties: + alertSelector: + description: Selector to filter alerts. + 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 + channels: + description: The channel or user to send notifications to. + items: + type: string + type: array + enabled: + description: whether the receiver is enabled + type: boolean + slackConfigSelector: + description: SlackConfig to be selected for this receiver + 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 + required: + - channels + type: object + webhook: + properties: + alertSelector: + description: Selector to filter alerts. + 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 + enabled: + description: whether the receiver is enabled + type: boolean + httpConfig: + description: HTTPClientConfig configures an HTTP client. + properties: + basicAuth: + description: The HTTP basic authentication credentials for the targets. + properties: + password: + description: SecretKeySelector selects a key of a Secret. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + username: + type: string + required: + - username + type: object + bearerToken: + description: The bearer token for the targets. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + proxyUrl: + description: HTTP proxy server to use to connect to the targets. + type: string + tlsConfig: + description: TLSConfig to use to connect to the targets. + properties: + clientCertificate: + description: The certificate of the client. + properties: + cert: + description: The client cert file for the targets. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + key: + description: The client key file for the targets. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + type: object + insecureSkipVerify: + description: Disable target certificate validation. + type: boolean + rootCA: + description: RootCA defines the root certificate authorities that clients use when verifying server certificates. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + serverName: + description: Used to verify the hostname for the targets. + type: string + type: object + type: object + service: + description: "`service` is a reference to the service for this webhook. Either `service` or `url` must be specified. \n If the webhook is running within the cluster, then you should use `service`." + properties: + name: + description: '`name` is the name of the service. Required' + type: string + namespace: + description: '`namespace` is the namespace of the service. Required' + type: string + path: + description: '`path` is an optional URL path which will be sent in any request to this service.' + type: string + port: + description: If specified, the port on the service that hosting webhook. Default to 443 for backward compatibility. `port` should be a valid port number (1-65535, inclusive). + format: int32 + type: integer + scheme: + description: Http scheme, default is http. + type: string + required: + - name + - namespace + type: object + url: + description: "`url` gives the location of the webhook, in standard URL form (`scheme://host:port/path`). Exactly one of `url` or `service` must be specified. \n The `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some api servers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address. \n Please note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster. \n A path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier. \n Attempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either." + type: string + webhookConfigSelector: + description: WebhookConfig to be selected for this receiver + 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 + type: object + wechat: + properties: + alertSelector: + description: Selector to filter alerts. + 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 + enabled: + description: whether the receiver is enabled + type: boolean + toParty: + items: + type: string + type: array + toTag: + items: + type: string + type: array + toUser: + items: + type: string + type: array + wechatConfigSelector: + description: WechatConfig to be selected for this receiver + 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 + type: object + type: object + status: + description: ReceiverStatus defines the observed state of Receiver + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/types.kubefed.io_federatednamespaces.yaml b/config/crds/types.kubefed.io_federatednamespaces.yaml new file mode 100644 index 0000000000000000000000000000000000000000..84dd1e61d297a63a640dbfb81671e3caec165518 --- /dev/null +++ b/config/crds/types.kubefed.io_federatednamespaces.yaml @@ -0,0 +1,164 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: federatednamespaces.types.kubefed.io +spec: + group: types.kubefed.io + names: + kind: FederatedNamespace + listKind: FederatedNamespaceList + plural: federatednamespaces + singular: federatednamespace + scope: Namespaced + versions: + - name: v1beta1 + 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: + overrides: + items: + properties: + clusterName: + type: string + clusterOverrides: + items: + properties: + op: + type: string + path: + type: string + value: + type: object + required: + - path + type: object + type: array + required: + - clusterName + type: object + type: array + placement: + properties: + clusterSelector: + description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. + 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 + clusters: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + type: object + template: + properties: + spec: + description: NamespaceSpec describes the attributes on a Namespace. + properties: + finalizers: + description: 'Finalizers is an opaque list of values that must be empty to permanently remove object from storage. More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/' + items: + description: FinalizerName is the name identifying a finalizer during namespace lifecycle. + type: string + type: array + type: object + type: object + required: + - placement + - template + type: object + status: + properties: + clusters: + items: + properties: + name: + type: string + status: + type: string + required: + - name + type: object + type: array + conditions: + items: + properties: + lastTransitionTime: + description: Last time the condition transit from one status to another. + type: string + lastUpdateTime: + description: Last time reconciliation resulted in an error or the last time a change was propagated to member clusters. + type: string + reason: + description: (brief) 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 + required: + - status + - type + type: object + type: array + observedGeneration: + format: int64 + type: integer + type: object + required: + - spec + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/types.kubefed.io_federatednotificationconfigs.yaml b/config/crds/types.kubefed.io_federatednotificationconfigs.yaml new file mode 100644 index 0000000000000000000000000000000000000000..739bcf8770f04527e26c094b8b1b0fdfa379ffc6 --- /dev/null +++ b/config/crds/types.kubefed.io_federatednotificationconfigs.yaml @@ -0,0 +1,393 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: federatednotificationconfigs.types.kubefed.io +spec: + group: types.kubefed.io + names: + kind: FederatedNotificationConfig + listKind: FederatedNotificationConfigList + plural: federatednotificationconfigs + singular: federatednotificationconfig + scope: Cluster + versions: + - name: v1beta1 + 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: + overrides: + items: + properties: + clusterName: + type: string + clusterOverrides: + items: + properties: + op: + type: string + path: + type: string + value: + type: object + required: + - path + type: object + type: array + required: + - clusterName + type: object + type: array + placement: + properties: + clusterSelector: + description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. + 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 + clusters: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + type: object + template: + properties: + metadata: + type: object + x-kubernetes-preserve-unknown-fields: true + spec: + description: ConfigSpec defines the desired state of Config + properties: + dingtalk: + properties: + conversation: + description: Only needed when send alerts to the conversation. + properties: + appkey: + description: The key of the application with which to send messages. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + appsecret: + description: The key in the secret to be used. Must be a valid secret key. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + type: object + labels: + additionalProperties: + type: string + type: object + type: object + email: + properties: + authIdentify: + description: The identity for PLAIN authentication. + type: string + authPassword: + description: The secret contains the SMTP password for LOGIN and PLAIN authentications. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + authSecret: + description: The secret contains the SMTP secret for CRAM-MD5 authentication. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + authUsername: + description: The username for CRAM-MD5, LOGIN and PLAIN authentications. + type: string + from: + description: The sender address. + type: string + hello: + description: The hostname to use when identifying to the SMTP server. + type: string + labels: + additionalProperties: + type: string + type: object + requireTLS: + description: The default SMTP TLS requirement. + type: boolean + smartHost: + description: The address of the SMTP server. + properties: + host: + type: string + port: + type: integer + required: + - host + - port + type: object + tls: + description: TLSConfig configures the options for TLS connections. + properties: + clientCertificate: + description: The certificate of the client. + properties: + cert: + description: The client cert file for the targets. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + key: + description: The client key file for the targets. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + type: object + insecureSkipVerify: + description: Disable target certificate validation. + type: boolean + rootCA: + description: RootCA defines the root certificate authorities that clients use when verifying server certificates. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + serverName: + description: Used to verify the hostname for the targets. + type: string + type: object + required: + - from + - smartHost + type: object + slack: + properties: + labels: + additionalProperties: + type: string + type: object + slackTokenSecret: + description: The token of user or bot. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + type: object + webhook: + properties: + labels: + additionalProperties: + type: string + type: object + type: object + wechat: + properties: + labels: + additionalProperties: + type: string + type: object + wechatApiAgentId: + description: The id of the application which sending message. + type: string + wechatApiCorpId: + description: The corp id for authentication. + type: string + wechatApiSecret: + description: The API key to use when talking to the WeChat API. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + wechatApiUrl: + description: The WeChat API URL. + type: string + required: + - wechatApiAgentId + - wechatApiCorpId + - wechatApiSecret + type: object + type: object + type: object + required: + - placement + - template + type: object + status: + properties: + clusters: + items: + properties: + name: + type: string + status: + type: string + required: + - name + type: object + type: array + conditions: + items: + properties: + lastTransitionTime: + description: Last time the condition transit from one status to another. + type: string + lastUpdateTime: + description: Last time reconciliation resulted in an error or the last time a change was propagated to member clusters. + type: string + reason: + description: (brief) 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 + required: + - status + - type + type: object + type: array + observedGeneration: + format: int64 + type: integer + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/types.kubefed.io_federatednotificationreceivers.yaml b/config/crds/types.kubefed.io_federatednotificationreceivers.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ab2258454e8ce6206c0e1828ed83ff59ffaf7aaf --- /dev/null +++ b/config/crds/types.kubefed.io_federatednotificationreceivers.yaml @@ -0,0 +1,700 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: federatednotificationreceivers.types.kubefed.io +spec: + group: types.kubefed.io + names: + kind: FederatedNotificationReceiver + listKind: FederatedNotificationReceiverList + plural: federatednotificationreceivers + singular: federatednotificationreceiver + scope: Cluster + versions: + - name: v1beta1 + 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: + overrides: + items: + properties: + clusterName: + type: string + clusterOverrides: + items: + properties: + op: + type: string + path: + type: string + value: + type: object + required: + - path + type: object + type: array + required: + - clusterName + type: object + type: array + placement: + properties: + clusterSelector: + description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. + 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 + clusters: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + type: object + template: + properties: + metadata: + type: object + x-kubernetes-preserve-unknown-fields: true + spec: + description: ReceiverSpec defines the desired state of Receiver + properties: + dingtalk: + properties: + alertSelector: + description: Selector to filter alerts. + 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 + chatbot: + description: Be careful, a ChatBot only can send 20 message per minute. + properties: + keywords: + description: Custom keywords of ChatBot + items: + type: string + type: array + secret: + description: Secret of ChatBot, you can get it after enabled Additional Signature of ChatBot. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + webhook: + description: The webhook of ChatBot which the message will send to. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + required: + - webhook + type: object + conversation: + description: The conversation which message will send to. + properties: + chatids: + items: + type: string + type: array + required: + - chatids + type: object + dingtalkConfigSelector: + description: DingTalkConfig to be selected for this receiver + 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 + enabled: + description: whether the receiver is enabled + type: boolean + type: object + email: + properties: + alertSelector: + description: Selector to filter alerts. + 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 + emailConfigSelector: + description: EmailConfig to be selected for this receiver + 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 + enabled: + description: whether the receiver is enabled + type: boolean + to: + description: Receivers' email addresses + items: + type: string + type: array + required: + - to + type: object + slack: + properties: + alertSelector: + description: Selector to filter alerts. + 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 + channels: + description: The channel or user to send notifications to. + items: + type: string + type: array + enabled: + description: whether the receiver is enabled + type: boolean + slackConfigSelector: + description: SlackConfig to be selected for this receiver + 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 + required: + - channels + type: object + webhook: + properties: + alertSelector: + description: Selector to filter alerts. + 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 + enabled: + description: whether the receiver is enabled + type: boolean + httpConfig: + description: HTTPClientConfig configures an HTTP client. + properties: + basicAuth: + description: The HTTP basic authentication credentials for the targets. + properties: + password: + description: SecretKeySelector selects a key of a Secret. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + username: + type: string + required: + - username + type: object + bearerToken: + description: The bearer token for the targets. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + proxyUrl: + description: HTTP proxy server to use to connect to the targets. + type: string + tlsConfig: + description: TLSConfig to use to connect to the targets. + properties: + clientCertificate: + description: The certificate of the client. + properties: + cert: + description: The client cert file for the targets. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + key: + description: The client key file for the targets. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + type: object + insecureSkipVerify: + description: Disable target certificate validation. + type: boolean + rootCA: + description: RootCA defines the root certificate authorities that clients use when verifying server certificates. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: The namespace of the secret, default to the pod's namespace. + type: string + required: + - key + type: object + serverName: + description: Used to verify the hostname for the targets. + type: string + type: object + type: object + service: + description: "`service` is a reference to the service for this webhook. Either `service` or `url` must be specified. \n If the webhook is running within the cluster, then you should use `service`." + properties: + name: + description: '`name` is the name of the service. Required' + type: string + namespace: + description: '`namespace` is the namespace of the service. Required' + type: string + path: + description: '`path` is an optional URL path which will be sent in any request to this service.' + type: string + port: + description: If specified, the port on the service that hosting webhook. Default to 443 for backward compatibility. `port` should be a valid port number (1-65535, inclusive). + format: int32 + type: integer + scheme: + description: Http scheme, default is http. + type: string + required: + - name + - namespace + type: object + url: + description: "`url` gives the location of the webhook, in standard URL form (`scheme://host:port/path`). Exactly one of `url` or `service` must be specified. \n The `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some api servers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address. \n Please note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster. \n A path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier. \n Attempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either." + type: string + webhookConfigSelector: + description: WebhookConfig to be selected for this receiver + 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 + type: object + wechat: + properties: + alertSelector: + description: Selector to filter alerts. + 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 + enabled: + description: whether the receiver is enabled + type: boolean + toParty: + items: + type: string + type: array + toTag: + items: + type: string + type: array + toUser: + items: + type: string + type: array + wechatConfigSelector: + description: WechatConfig to be selected for this receiver + 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 + type: object + type: object + type: object + required: + - placement + - template + type: object + status: + properties: + clusters: + items: + properties: + name: + type: string + status: + type: string + required: + - name + type: object + type: array + conditions: + items: + properties: + lastTransitionTime: + description: Last time the condition transit from one status to another. + type: string + lastUpdateTime: + description: Last time reconciliation resulted in an error or the last time a change was propagated to member clusters. + type: string + reason: + description: (brief) 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 + required: + - status + - type + type: object + type: array + observedGeneration: + format: int64 + type: integer + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/types.kubefed.io_federatedsecrets.yaml b/config/crds/types.kubefed.io_federatedsecrets.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a5fd549d165a9e5a1545b4c2ef7a7a19b92e8b72 --- /dev/null +++ b/config/crds/types.kubefed.io_federatedsecrets.yaml @@ -0,0 +1,165 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: federatedsecrets.types.kubefed.io +spec: + group: types.kubefed.io + names: + kind: FederatedSecret + listKind: FederatedSecretList + plural: federatedsecrets + singular: federatedsecret + scope: Namespaced + versions: + - name: v1beta1 + 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: + overrides: + items: + properties: + clusterName: + type: string + clusterOverrides: + items: + properties: + op: + type: string + path: + type: string + value: + type: object + required: + - path + type: object + type: array + required: + - clusterName + type: object + type: array + placement: + properties: + clusterSelector: + description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. + 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 + clusters: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + type: object + template: + properties: + data: + additionalProperties: + format: byte + type: string + type: object + stringData: + additionalProperties: + type: string + type: object + type: + type: string + type: object + required: + - placement + - template + type: object + status: + properties: + clusters: + items: + properties: + name: + type: string + status: + type: string + required: + - name + type: object + type: array + conditions: + items: + properties: + lastTransitionTime: + description: Last time the condition transit from one status to another. + type: string + lastUpdateTime: + description: Last time reconciliation resulted in an error or the last time a change was propagated to member clusters. + type: string + reason: + description: (brief) 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 + required: + - status + - type + type: object + type: array + observedGeneration: + format: int64 + type: integer + type: object + required: + - spec + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/hack/generate_client.sh b/hack/generate_client.sh index 9fc15dc03037fe8acff196d8be8bc016ca65bc82..a012df988ea76fe18180243632f02d1831d17520 100755 --- a/hack/generate_client.sh +++ b/hack/generate_client.sh @@ -2,8 +2,7 @@ set -e - -GV="network:v1alpha1 servicemesh:v1alpha2 tenant:v1alpha1 tenant:v1alpha2 devops:v1alpha1 iam:v1alpha2 devops:v1alpha3 cluster:v1alpha1 storage:v1alpha1 auditing:v1alpha1 types:v1beta1 quota:v1alpha2 application:v1alpha1" +GV="network:v1alpha1 servicemesh:v1alpha2 tenant:v1alpha1 tenant:v1alpha2 devops:v1alpha1 iam:v1alpha2 devops:v1alpha3 cluster:v1alpha1 storage:v1alpha1 auditing:v1alpha1 types:v1beta1 quota:v1alpha2 application:v1alpha1 notification:v2beta1" rm -rf ./pkg/client ./hack/generate_group.sh "client,lister,informer" kubesphere.io/kubesphere/pkg/client kubesphere.io/kubesphere/pkg/apis "$GV" --output-base=./ -h "$PWD/hack/boilerplate.go.txt" diff --git a/pkg/apis/addtoscheme_notification_v2beta1.go b/pkg/apis/addtoscheme_notification_v2beta1.go new file mode 100644 index 0000000000000000000000000000000000000000..139a3a44593ce51445386f3892599c8b99b11683 --- /dev/null +++ b/pkg/apis/addtoscheme_notification_v2beta1.go @@ -0,0 +1,25 @@ +/* +Copyright 2019 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package apis + +import ( + "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" +) + +func init() { + AddToSchemes = append(AddToSchemes, v2beta1.SchemeBuilder.AddToScheme) +} diff --git a/pkg/apis/notification/group.go b/pkg/apis/notification/group.go new file mode 100644 index 0000000000000000000000000000000000000000..699e849a495102c54bd68a4f641c2d14b2b15741 --- /dev/null +++ b/pkg/apis/notification/group.go @@ -0,0 +1,18 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package auditing contains auditing API versions +package notification diff --git a/pkg/apis/notification/v2beta1/config_types.go b/pkg/apis/notification/v2beta1/config_types.go new file mode 100644 index 0000000000000000000000000000000000000000..de1d4dea59530f20aa9c510a575f87ca99dccb90 --- /dev/null +++ b/pkg/apis/notification/v2beta1/config_types.go @@ -0,0 +1,161 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Configuration of conversation +type DingTalkApplicationConfig struct { + // The key of the application with which to send messages. + AppKey *SecretKeySelector `json:"appkey,omitempty"` + // The key in the secret to be used. Must be a valid secret key. + AppSecret *SecretKeySelector `json:"appsecret,omitempty"` +} + +type DingTalkConfig struct { + Labels map[string]string `json:"labels,omitempty"` + // Only needed when send alerts to the conversation. + Conversation *DingTalkApplicationConfig `json:"conversation,omitempty"` +} + +type ClientCertificate struct { + // The client cert file for the targets. + Cert *SecretKeySelector `json:"cert,omitempty"` + // The client key file for the targets. + Key *SecretKeySelector `json:"key,omitempty"` +} + +// TLSConfig configures the options for TLS connections. +type TLSConfig struct { + // RootCA defines the root certificate authorities + // that clients use when verifying server certificates. + RootCA *SecretKeySelector `json:"rootCA,omitempty"` + // The certificate of the client. + *ClientCertificate `json:"clientCertificate,omitempty"` + // Used to verify the hostname for the targets. + ServerName string `json:"serverName,omitempty"` + // Disable target certificate validation. + InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"` +} + +// BasicAuth contains basic HTTP authentication credentials. +type BasicAuth struct { + Username string `json:"username"` + Password *SecretKeySelector `json:"password,omitempty"` +} + +// HTTPClientConfig configures an HTTP client. +type HTTPClientConfig struct { + // The HTTP basic authentication credentials for the targets. + BasicAuth *BasicAuth `json:"basicAuth,omitempty"` + // The bearer token for the targets. + BearerToken *SecretKeySelector `json:"bearerToken,omitempty"` + // HTTP proxy server to use to connect to the targets. + ProxyURL string `json:"proxyUrl,omitempty"` + // TLSConfig to use to connect to the targets. + TLSConfig *TLSConfig `json:"tlsConfig,omitempty"` +} + +type HostPort struct { + Host string `json:"host"` + Port int `json:"port"` +} + +type EmailConfig struct { + Labels map[string]string `json:"labels,omitempty"` + // The sender address. + From string `json:"from"` + // The address of the SMTP server. + SmartHost HostPort `json:"smartHost"` + // The hostname to use when identifying to the SMTP server. + Hello *string `json:"hello,omitempty"` + // The username for CRAM-MD5, LOGIN and PLAIN authentications. + AuthUsername *string `json:"authUsername,omitempty"` + // The identity for PLAIN authentication. + AuthIdentify *string `json:"authIdentify,omitempty"` + // The secret contains the SMTP password for LOGIN and PLAIN authentications. + AuthPassword *SecretKeySelector `json:"authPassword,omitempty"` + // The secret contains the SMTP secret for CRAM-MD5 authentication. + AuthSecret *SecretKeySelector `json:"authSecret,omitempty"` + // The default SMTP TLS requirement. + RequireTLS *bool `json:"requireTLS,omitempty"` + TLS *TLSConfig `json:"tls,omitempty"` +} + +type SlackConfig struct { + Labels map[string]string `json:"labels,omitempty"` + // The token of user or bot. + SlackTokenSecret *SecretKeySelector `json:"slackTokenSecret,omitempty"` +} + +type WebhookConfig struct { + Labels map[string]string `json:"labels,omitempty"` +} + +type WechatConfig struct { + Labels map[string]string `json:"labels,omitempty"` + // The WeChat API URL. + WechatApiUrl string `json:"wechatApiUrl,omitempty"` + // The corp id for authentication. + WechatApiCorpId string `json:"wechatApiCorpId"` + // The id of the application which sending message. + WechatApiAgentId string `json:"wechatApiAgentId"` + // The API key to use when talking to the WeChat API. + WechatApiSecret *SecretKeySelector `json:"wechatApiSecret"` +} + +//ConfigSpec defines the desired state of Config +type ConfigSpec struct { + DingTalk *DingTalkConfig `json:"dingtalk,omitempty"` + Email *EmailConfig `json:"email,omitempty"` + Slack *SlackConfig `json:"slack,omitempty"` + Webhook *WebhookConfig `json:"webhook,omitempty"` + Wechat *WechatConfig `json:"wechat,omitempty"` +} + +// ConfigStatus defines the observed state of Config +type ConfigStatus struct { +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster,shortName=nc,categories=notification-manager +// +kubebuilder:subresource:status +// +genclient +// +genclient:nonNamespaced +// DingTalkConfig is the Schema for the dingtalkconfigs API +type Config struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ConfigSpec `json:"spec,omitempty"` + Status ConfigStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ConfigList contains a list of Config +type ConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Config `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Config{}, &ConfigList{}) +} diff --git a/pkg/apis/notification/v2beta1/doc.go b/pkg/apis/notification/v2beta1/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..bb3db191e2ae0d6b5399c9223dd5553da1135504 --- /dev/null +++ b/pkg/apis/notification/v2beta1/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v2beta1 contains API Schema definitions for the notification v2beta1 API group +// +groupName=notification.kubesphere.io +// +genclient +// +genclient:nonNamespaced +package v2beta1 diff --git a/pkg/apis/notification/v2beta1/notificationmanager_types.go b/pkg/apis/notification/v2beta1/notificationmanager_types.go new file mode 100644 index 0000000000000000000000000000000000000000..feb4115b0f850ed2c5b81585b24fb521c5f0e99b --- /dev/null +++ b/pkg/apis/notification/v2beta1/notificationmanager_types.go @@ -0,0 +1,213 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2beta1 + +import ( + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "time" +) + +// SecretKeySelector selects a key of a Secret. +type SecretKeySelector struct { + // The namespace of the secret, default to the pod's namespace. + // +optional + Namespace string `json:"namespace,omitempty" protobuf:"bytes,1,opt,name=namespace"` + // Name of the secret. + // +optional + Name string `json:"name" protobuf:"bytes,1,opt,name=name"` + // The key of the secret to select from. Must be a valid secret key. + Key string `json:"key" protobuf:"bytes,2,opt,name=key"` +} + +// NotificationManagerSpec defines the desired state of NotificationManager +type NotificationManagerSpec struct { + // Compute Resources required by container. + Resources v1.ResourceRequirements `json:"resources,omitempty"` + // Docker Image used to start Notification Manager container, + // for example kubesphere/notification-manager:v0.1.0 + Image *string `json:"image,omitempty"` + // Image pull policy. One of Always, Never, IfNotPresent. + // Defaults to IfNotPresent if not specified + ImagePullPolicy *v1.PullPolicy `json:"imagePullPolicy,omitempty"` + // Number of instances to deploy for Notification Manager deployment. + Replicas *int32 `json:"replicas,omitempty"` + // Define which Nodes the Pods will be scheduled to. + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + // Pod's scheduling constraints. + Affinity *v1.Affinity `json:"affinity,omitempty"` + // Pod's tolerations. + Tolerations []v1.Toleration `json:"tolerations,omitempty"` + // ServiceAccountName is the name of the ServiceAccount to use to run Notification Manager Pods. + // ServiceAccount 'default' in notification manager's namespace will be used if not specified. + ServiceAccountName string `json:"serviceAccountName,omitempty"` + // Port name used for the pods and service, defaults to webhook + PortName string `json:"portName,omitempty"` + // Default Email/Wechat/Slack/Webhook Config to be selected + DefaultConfigSelector *metav1.LabelSelector `json:"defaultConfigSelector,omitempty"` + // Receivers to send notifications to + Receivers *ReceiversSpec `json:"receivers"` + // The default namespace to which notification manager secrets belong. + DefaultSecretNamespace string `json:"defaultSecretNamespace,omitempty"` + // List of volumes that can be mounted by containers belonging to the pod. + Volumes []v1.Volume `json:"volumes,omitempty"` + // Pod volumes to mount into the container's filesystem. + // Cannot be updated. + VolumeMounts []v1.VolumeMount `json:"volumeMounts,omitempty"` + // Arguments to the entrypoint. + // The docker image's CMD is used if this is not provided. + // Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + // cannot be resolved, the reference in the input string will remain 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. + // Cannot be updated. + // +optional + Args []string `json:"args,omitempty"` +} + +type ReceiversSpec struct { + // Key used to identify tenant, default to be "namespace" if not specified + TenantKey string `json:"tenantKey"` + // Selector to find global notification receivers + // which will be used when tenant receivers cannot be found. + // Only matchLabels expression is allowed. + GlobalReceiverSelector *metav1.LabelSelector `json:"globalReceiverSelector"` + // Selector to find tenant notification receivers. + // Only matchLabels expression is allowed. + TenantReceiverSelector *metav1.LabelSelector `json:"tenantReceiverSelector"` + // Various receiver options + Options *Options `json:"options,omitempty"` +} + +type GlobalOptions struct { + // Template file path, must be a absolute path. + TemplateFiles []string `json:"templateFile,omitempty"` + // The name of the template to generate message. + // If the receiver dose not setup template, it will use this. + Template string `json:"template,omitempty"` + // The name of the cluster in which the notification manager is deployed. + Cluster string `json:"cluster,omitempty"` +} + +type EmailOptions struct { + // Notification Sending Timeout + NotificationTimeout *int32 `json:"notificationTimeout,omitempty"` + // Type of sending email, bulk or single + DeliveryType string `json:"deliveryType,omitempty"` + // The maximum size of receivers in one email. + MaxEmailReceivers int `json:"maxEmailReceivers,omitempty"` + // The name of the template to generate email message. + // If the global template is not set, it will use default. + Template string `json:"template,omitempty"` + // The name of the template to generate email subject + SubjectTemplate string `json:"subjectTemplate,omitempty"` +} + +type WechatOptions struct { + // Notification Sending Timeout + NotificationTimeout *int32 `json:"notificationTimeout,omitempty"` + // The name of the template to generate wechat message. + Template string `json:"template,omitempty"` + // The maximum message size that can be sent in a request. + MessageMaxSize int `json:"messageMaxSize,omitempty"` + // The time of token expired. + TokenExpires time.Duration `json:"tokenExpires,omitempty"` +} + +type SlackOptions struct { + // Notification Sending Timeout + NotificationTimeout *int32 `json:"notificationTimeout,omitempty"` + // The name of the template to generate slack message. + // If the global template is not set, it will use default. + Template string `json:"template,omitempty"` +} + +type WebhookOptions struct { + // Notification Sending Timeout + NotificationTimeout *int32 `json:"notificationTimeout,omitempty"` + // The name of the template to generate webhook message. + // If the global template is not set, it will use default. + Template string `json:"template,omitempty"` +} + +// The config of flow control. +type Throttle struct { + // The maximum calls in `Unit`. + Threshold int `json:"threshold,omitempty"` + Unit time.Duration `json:"unit,omitempty"` + // The maximum tolerable waiting time when the calls trigger flow control, if the actual waiting time is more than this time, it will + // return a error, else it will wait for the flow restriction lifted, and send the message. + // Nil means do not wait, the maximum value is `Unit`. + MaxWaitTime time.Duration `json:"maxWaitTime,omitempty"` +} + +type DingTalkOptions struct { + // Notification Sending Timeout + NotificationTimeout *int32 `json:"notificationTimeout,omitempty"` + // The name of the template to generate DingTalk message. + // If the global template is not set, it will use default. + Template string `json:"template,omitempty"` + // The time of token expired. + TokenExpires time.Duration `json:"tokenExpires,omitempty"` + // The maximum message size that can be sent to conversation in a request. + ConversationMessageMaxSize int `json:"conversationMessageMaxSize,omitempty"` + // The maximum message size that can be sent to chatbot in a request. + ChatbotMessageMaxSize int `json:"chatbotMessageMaxSize,omitempty"` + // The flow control fo chatbot. + ChatBotThrottle *Throttle `json:"chatBotThrottle,omitempty"` + // The flow control fo conversation. + ConversationThrottle *Throttle `json:"conversationThrottle,omitempty"` +} + +type Options struct { + Global *GlobalOptions `json:"global,omitempty"` + Email *EmailOptions `json:"email,omitempty"` + Wechat *WechatOptions `json:"wechat,omitempty"` + Slack *SlackOptions `json:"slack,omitempty"` + Webhook *WebhookOptions `json:"webhook,omitempty"` + DingTalk *DingTalkOptions `json:"dingtalk,omitempty"` +} + +// NotificationManagerStatus defines the observed state of NotificationManager +type NotificationManagerStatus struct { +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster,shortName=nm,categories=notification-manager +// +kubebuilder:subresource:status + +// NotificationManager is the Schema for the notificationmanagers API +type NotificationManager struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NotificationManagerSpec `json:"spec,omitempty"` + Status NotificationManagerStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// NotificationManagerList contains a list of NotificationManager +type NotificationManagerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NotificationManager `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NotificationManager{}, &NotificationManagerList{}) +} diff --git a/pkg/apis/notification/v2beta1/receiver_types.go b/pkg/apis/notification/v2beta1/receiver_types.go new file mode 100644 index 0000000000000000000000000000000000000000..5d39219463d650075120a799f7a74a294cabe987 --- /dev/null +++ b/pkg/apis/notification/v2beta1/receiver_types.go @@ -0,0 +1,197 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Configuration of ChatBot +type DingTalkChatBot struct { + // The webhook of ChatBot which the message will send to. + Webhook *SecretKeySelector `json:"webhook"` + + // Custom keywords of ChatBot + Keywords []string `json:"keywords,omitempty"` + + // Secret of ChatBot, you can get it after enabled Additional Signature of ChatBot. + Secret *SecretKeySelector `json:"secret,omitempty"` +} + +// Configuration of conversation +type DingTalkConversation struct { + ChatIDs []string `json:"chatids"` +} + +type DingTalkReceiver struct { + // whether the receiver is enabled + Enabled *bool `json:"enabled,omitempty"` + // DingTalkConfig to be selected for this receiver + DingTalkConfigSelector *metav1.LabelSelector `json:"dingtalkConfigSelector,omitempty"` + // Selector to filter alerts. + AlertSelector *metav1.LabelSelector `json:"alertSelector,omitempty"` + // Be careful, a ChatBot only can send 20 message per minute. + ChatBot *DingTalkChatBot `json:"chatbot,omitempty"` + // The conversation which message will send to. + Conversation *DingTalkConversation `json:"conversation,omitempty"` +} + +type EmailReceiver struct { + // whether the receiver is enabled + Enabled *bool `json:"enabled,omitempty"` + // Receivers' email addresses + To []string `json:"to"` + // EmailConfig to be selected for this receiver + EmailConfigSelector *metav1.LabelSelector `json:"emailConfigSelector,omitempty"` + // Selector to filter alerts. + AlertSelector *metav1.LabelSelector `json:"alertSelector,omitempty"` +} + +type SlackReceiver struct { + // whether the receiver is enabled + Enabled *bool `json:"enabled,omitempty"` + // SlackConfig to be selected for this receiver + SlackConfigSelector *metav1.LabelSelector `json:"slackConfigSelector,omitempty"` + // Selector to filter alerts. + AlertSelector *metav1.LabelSelector `json:"alertSelector,omitempty"` + // The channel or user to send notifications to. + Channels []string `json:"channels"` +} + +// ServiceReference holds a reference to Service.legacy.k8s.io +type ServiceReference struct { + // `namespace` is the namespace of the service. + // Required + Namespace string `json:"namespace"` + + // `name` is the name of the service. + // Required + Name string `json:"name"` + + // `path` is an optional URL path which will be sent in any request to + // this service. + // +optional + Path *string `json:"path,omitempty"` + + // If specified, the port on the service that hosting webhook. + // Default to 443 for backward compatibility. + // `port` should be a valid port number (1-65535, inclusive). + // +optional + Port *int32 `json:"port,omitempty"` + + // Http scheme, default is http. + // +optional + Scheme *string `json:"scheme,omitempty"` +} + +type WebhookReceiver struct { + // whether the receiver is enabled + Enabled *bool `json:"enabled,omitempty"` + // WebhookConfig to be selected for this receiver + WebhookConfigSelector *metav1.LabelSelector `json:"webhookConfigSelector,omitempty"` + // Selector to filter alerts. + AlertSelector *metav1.LabelSelector `json:"alertSelector,omitempty"` + // `url` gives the location of the webhook, in standard URL form + // (`scheme://host:port/path`). Exactly one of `url` or `service` + // must be specified. + // + // The `host` should not refer to a service running in the cluster; use + // the `service` field instead. The host might be resolved via external + // DNS in some api servers (e.g., `kube-apiserver` cannot resolve + // in-cluster DNS as that would be a layering violation). `host` may + // also be an IP address. + // + // Please note that using `localhost` or `127.0.0.1` as a `host` is + // risky unless you take great care to run this webhook on all hosts + // which run an apiserver which might need to make calls to this + // webhook. Such installs are likely to be non-portable, i.e., not easy + // to turn up in a new cluster. + // + // A path is optional, and if present may be any string permissible in + // a URL. You may use the path to pass an arbitrary string to the + // webhook, for example, a cluster identifier. + // + // Attempting to use a user or basic auth e.g. "user:password@" is not + // allowed. Fragments ("#...") and query parameters ("?...") are not + // allowed, either. + // + // +optional + URL *string `json:"url,omitempty"` + + // `service` is a reference to the service for this webhook. Either + // `service` or `url` must be specified. + // + // If the webhook is running within the cluster, then you should use `service`. + // + // +optional + Service *ServiceReference `json:"service,omitempty"` + + HTTPConfig *HTTPClientConfig `json:"httpConfig,omitempty"` +} + +type WechatReceiver struct { + // whether the receiver is enabled + Enabled *bool `json:"enabled,omitempty"` + // WechatConfig to be selected for this receiver + WechatConfigSelector *metav1.LabelSelector `json:"wechatConfigSelector,omitempty"` + // Selector to filter alerts. + AlertSelector *metav1.LabelSelector `json:"alertSelector,omitempty"` + // +optional + ToUser []string `json:"toUser,omitempty"` + ToParty []string `json:"toParty,omitempty"` + ToTag []string `json:"toTag,omitempty"` +} + +//ReceiverSpec defines the desired state of Receiver +type ReceiverSpec struct { + DingTalk *DingTalkReceiver `json:"dingtalk,omitempty"` + Email *EmailReceiver `json:"email,omitempty"` + Slack *SlackReceiver `json:"slack,omitempty"` + Webhook *WebhookReceiver `json:"webhook,omitempty"` + Wechat *WechatReceiver `json:"wechat,omitempty"` +} + +// ReceiverStatus defines the observed state of Receiver +type ReceiverStatus struct { +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster,shortName=nr,categories=notification-manager +// +kubebuilder:subresource:status +// +genclient +// +genclient:nonNamespaced +// Receiver is the Schema for the receivers API +type Receiver struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ReceiverSpec `json:"spec,omitempty"` + Status ReceiverStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ReceiverList contains a list of Receiver +type ReceiverList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Receiver `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Receiver{}, &ReceiverList{}) +} diff --git a/pkg/apis/notification/v2beta1/register.go b/pkg/apis/notification/v2beta1/register.go new file mode 100644 index 0000000000000000000000000000000000000000..f0d466ec6da1c6cd5f33ddaf15bdd696550e99e9 --- /dev/null +++ b/pkg/apis/notification/v2beta1/register.go @@ -0,0 +1,41 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// NOTE: Boilerplate only. Ignore this file. + +// Package v2beta1 contains API Schema definitions for the notification v2beta1 API group +// +k8s:deepcopy-gen=package,register +// +groupName=notification.kubesphere.io +package v2beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/runtime/scheme" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "notification.kubesphere.io", Version: "v2beta1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} + + AddToScheme = SchemeBuilder.AddToScheme +) + +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/pkg/apis/notification/v2beta1/types.go b/pkg/apis/notification/v2beta1/types.go new file mode 100644 index 0000000000000000000000000000000000000000..5db537274c664ef8b51c29b6acb2a12233fa2f42 --- /dev/null +++ b/pkg/apis/notification/v2beta1/types.go @@ -0,0 +1,27 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2beta1 + +const ( + ResourceKindConfig = "Configs" + ResourcesSingularConfig = "config" + ResourcesPluralConfig = "configs" + + ResourceKindReceiver = "Receiver" + ResourcesSingularReceiver = "receiver" + ResourcesPluralReceiver = "receivers" +) diff --git a/pkg/apis/notification/v2beta1/v2beta1_suite_test.go b/pkg/apis/notification/v2beta1/v2beta1_suite_test.go new file mode 100644 index 0000000000000000000000000000000000000000..437b7afd4437301b847e8a4363b0ba74fcab86dc --- /dev/null +++ b/pkg/apis/notification/v2beta1/v2beta1_suite_test.go @@ -0,0 +1,54 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2beta1 + +import ( + "log" + "os" + "path/filepath" + "testing" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" +) + +var cfg *rest.Config + +func TestMain(m *testing.M) { + t := &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")}, + } + + err := SchemeBuilder.AddToScheme(scheme.Scheme) + if err != nil { + log.Fatal(err) + } + + if cfg, err = t.Start(); err != nil { + log.Fatal(err) + } + + if _, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}); err != nil { + log.Fatal(err) + } + + code := m.Run() + _ = t.Stop() + os.Exit(code) +} diff --git a/pkg/apis/notification/v2beta1/zz_generated.deepcopy.go b/pkg/apis/notification/v2beta1/zz_generated.deepcopy.go new file mode 100644 index 0000000000000000000000000000000000000000..559800ecbcf5ae191e23ff42979f5ca8225414b7 --- /dev/null +++ b/pkg/apis/notification/v2beta1/zz_generated.deepcopy.go @@ -0,0 +1,1224 @@ +// +build !ignore_autogenerated + +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v2beta1 + +import ( + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BasicAuth) DeepCopyInto(out *BasicAuth) { + *out = *in + if in.Password != nil { + in, out := &in.Password, &out.Password + *out = new(SecretKeySelector) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuth. +func (in *BasicAuth) DeepCopy() *BasicAuth { + if in == nil { + return nil + } + out := new(BasicAuth) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientCertificate) DeepCopyInto(out *ClientCertificate) { + *out = *in + if in.Cert != nil { + in, out := &in.Cert, &out.Cert + *out = new(SecretKeySelector) + **out = **in + } + if in.Key != nil { + in, out := &in.Key, &out.Key + *out = new(SecretKeySelector) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientCertificate. +func (in *ClientCertificate) DeepCopy() *ClientCertificate { + if in == nil { + return nil + } + out := new(ClientCertificate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Config) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigList) DeepCopyInto(out *ConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Config, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigList. +func (in *ConfigList) DeepCopy() *ConfigList { + if in == nil { + return nil + } + out := new(ConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { + *out = *in + if in.DingTalk != nil { + in, out := &in.DingTalk, &out.DingTalk + *out = new(DingTalkConfig) + (*in).DeepCopyInto(*out) + } + if in.Email != nil { + in, out := &in.Email, &out.Email + *out = new(EmailConfig) + (*in).DeepCopyInto(*out) + } + if in.Slack != nil { + in, out := &in.Slack, &out.Slack + *out = new(SlackConfig) + (*in).DeepCopyInto(*out) + } + if in.Webhook != nil { + in, out := &in.Webhook, &out.Webhook + *out = new(WebhookConfig) + (*in).DeepCopyInto(*out) + } + if in.Wechat != nil { + in, out := &in.Wechat, &out.Wechat + *out = new(WechatConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. +func (in *ConfigSpec) DeepCopy() *ConfigSpec { + if in == nil { + return nil + } + out := new(ConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigStatus) DeepCopyInto(out *ConfigStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigStatus. +func (in *ConfigStatus) DeepCopy() *ConfigStatus { + if in == nil { + return nil + } + out := new(ConfigStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DingTalkApplicationConfig) DeepCopyInto(out *DingTalkApplicationConfig) { + *out = *in + if in.AppKey != nil { + in, out := &in.AppKey, &out.AppKey + *out = new(SecretKeySelector) + **out = **in + } + if in.AppSecret != nil { + in, out := &in.AppSecret, &out.AppSecret + *out = new(SecretKeySelector) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DingTalkApplicationConfig. +func (in *DingTalkApplicationConfig) DeepCopy() *DingTalkApplicationConfig { + if in == nil { + return nil + } + out := new(DingTalkApplicationConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DingTalkChatBot) DeepCopyInto(out *DingTalkChatBot) { + *out = *in + if in.Webhook != nil { + in, out := &in.Webhook, &out.Webhook + *out = new(SecretKeySelector) + **out = **in + } + if in.Keywords != nil { + in, out := &in.Keywords, &out.Keywords + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Secret != nil { + in, out := &in.Secret, &out.Secret + *out = new(SecretKeySelector) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DingTalkChatBot. +func (in *DingTalkChatBot) DeepCopy() *DingTalkChatBot { + if in == nil { + return nil + } + out := new(DingTalkChatBot) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DingTalkConfig) DeepCopyInto(out *DingTalkConfig) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Conversation != nil { + in, out := &in.Conversation, &out.Conversation + *out = new(DingTalkApplicationConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DingTalkConfig. +func (in *DingTalkConfig) DeepCopy() *DingTalkConfig { + if in == nil { + return nil + } + out := new(DingTalkConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DingTalkConversation) DeepCopyInto(out *DingTalkConversation) { + *out = *in + if in.ChatIDs != nil { + in, out := &in.ChatIDs, &out.ChatIDs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DingTalkConversation. +func (in *DingTalkConversation) DeepCopy() *DingTalkConversation { + if in == nil { + return nil + } + out := new(DingTalkConversation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DingTalkOptions) DeepCopyInto(out *DingTalkOptions) { + *out = *in + if in.NotificationTimeout != nil { + in, out := &in.NotificationTimeout, &out.NotificationTimeout + *out = new(int32) + **out = **in + } + if in.ChatBotThrottle != nil { + in, out := &in.ChatBotThrottle, &out.ChatBotThrottle + *out = new(Throttle) + **out = **in + } + if in.ConversationThrottle != nil { + in, out := &in.ConversationThrottle, &out.ConversationThrottle + *out = new(Throttle) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DingTalkOptions. +func (in *DingTalkOptions) DeepCopy() *DingTalkOptions { + if in == nil { + return nil + } + out := new(DingTalkOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DingTalkReceiver) DeepCopyInto(out *DingTalkReceiver) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.DingTalkConfigSelector != nil { + in, out := &in.DingTalkConfigSelector, &out.DingTalkConfigSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.AlertSelector != nil { + in, out := &in.AlertSelector, &out.AlertSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.ChatBot != nil { + in, out := &in.ChatBot, &out.ChatBot + *out = new(DingTalkChatBot) + (*in).DeepCopyInto(*out) + } + if in.Conversation != nil { + in, out := &in.Conversation, &out.Conversation + *out = new(DingTalkConversation) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DingTalkReceiver. +func (in *DingTalkReceiver) DeepCopy() *DingTalkReceiver { + if in == nil { + return nil + } + out := new(DingTalkReceiver) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EmailConfig) DeepCopyInto(out *EmailConfig) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.SmartHost = in.SmartHost + if in.Hello != nil { + in, out := &in.Hello, &out.Hello + *out = new(string) + **out = **in + } + if in.AuthUsername != nil { + in, out := &in.AuthUsername, &out.AuthUsername + *out = new(string) + **out = **in + } + if in.AuthIdentify != nil { + in, out := &in.AuthIdentify, &out.AuthIdentify + *out = new(string) + **out = **in + } + if in.AuthPassword != nil { + in, out := &in.AuthPassword, &out.AuthPassword + *out = new(SecretKeySelector) + **out = **in + } + if in.AuthSecret != nil { + in, out := &in.AuthSecret, &out.AuthSecret + *out = new(SecretKeySelector) + **out = **in + } + if in.RequireTLS != nil { + in, out := &in.RequireTLS, &out.RequireTLS + *out = new(bool) + **out = **in + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EmailConfig. +func (in *EmailConfig) DeepCopy() *EmailConfig { + if in == nil { + return nil + } + out := new(EmailConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EmailOptions) DeepCopyInto(out *EmailOptions) { + *out = *in + if in.NotificationTimeout != nil { + in, out := &in.NotificationTimeout, &out.NotificationTimeout + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EmailOptions. +func (in *EmailOptions) DeepCopy() *EmailOptions { + if in == nil { + return nil + } + out := new(EmailOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EmailReceiver) DeepCopyInto(out *EmailReceiver) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.To != nil { + in, out := &in.To, &out.To + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.EmailConfigSelector != nil { + in, out := &in.EmailConfigSelector, &out.EmailConfigSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.AlertSelector != nil { + in, out := &in.AlertSelector, &out.AlertSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EmailReceiver. +func (in *EmailReceiver) DeepCopy() *EmailReceiver { + if in == nil { + return nil + } + out := new(EmailReceiver) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalOptions) DeepCopyInto(out *GlobalOptions) { + *out = *in + if in.TemplateFiles != nil { + in, out := &in.TemplateFiles, &out.TemplateFiles + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalOptions. +func (in *GlobalOptions) DeepCopy() *GlobalOptions { + if in == nil { + return nil + } + out := new(GlobalOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPClientConfig) DeepCopyInto(out *HTTPClientConfig) { + *out = *in + if in.BasicAuth != nil { + in, out := &in.BasicAuth, &out.BasicAuth + *out = new(BasicAuth) + (*in).DeepCopyInto(*out) + } + if in.BearerToken != nil { + in, out := &in.BearerToken, &out.BearerToken + *out = new(SecretKeySelector) + **out = **in + } + if in.TLSConfig != nil { + in, out := &in.TLSConfig, &out.TLSConfig + *out = new(TLSConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPClientConfig. +func (in *HTTPClientConfig) DeepCopy() *HTTPClientConfig { + if in == nil { + return nil + } + out := new(HTTPClientConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HostPort) DeepCopyInto(out *HostPort) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostPort. +func (in *HostPort) DeepCopy() *HostPort { + if in == nil { + return nil + } + out := new(HostPort) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NotificationManager) DeepCopyInto(out *NotificationManager) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NotificationManager. +func (in *NotificationManager) DeepCopy() *NotificationManager { + if in == nil { + return nil + } + out := new(NotificationManager) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NotificationManager) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NotificationManagerList) DeepCopyInto(out *NotificationManagerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NotificationManager, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NotificationManagerList. +func (in *NotificationManagerList) DeepCopy() *NotificationManagerList { + if in == nil { + return nil + } + out := new(NotificationManagerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NotificationManagerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NotificationManagerSpec) DeepCopyInto(out *NotificationManagerSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(string) + **out = **in + } + if in.ImagePullPolicy != nil { + in, out := &in.ImagePullPolicy, &out.ImagePullPolicy + *out = new(v1.PullPolicy) + **out = **in + } + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.DefaultConfigSelector != nil { + in, out := &in.DefaultConfigSelector, &out.DefaultConfigSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.Receivers != nil { + in, out := &in.Receivers, &out.Receivers + *out = new(ReceiversSpec) + (*in).DeepCopyInto(*out) + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.VolumeMounts != nil { + in, out := &in.VolumeMounts, &out.VolumeMounts + *out = make([]v1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NotificationManagerSpec. +func (in *NotificationManagerSpec) DeepCopy() *NotificationManagerSpec { + if in == nil { + return nil + } + out := new(NotificationManagerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NotificationManagerStatus) DeepCopyInto(out *NotificationManagerStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NotificationManagerStatus. +func (in *NotificationManagerStatus) DeepCopy() *NotificationManagerStatus { + if in == nil { + return nil + } + out := new(NotificationManagerStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Options) DeepCopyInto(out *Options) { + *out = *in + if in.Global != nil { + in, out := &in.Global, &out.Global + *out = new(GlobalOptions) + (*in).DeepCopyInto(*out) + } + if in.Email != nil { + in, out := &in.Email, &out.Email + *out = new(EmailOptions) + (*in).DeepCopyInto(*out) + } + if in.Wechat != nil { + in, out := &in.Wechat, &out.Wechat + *out = new(WechatOptions) + (*in).DeepCopyInto(*out) + } + if in.Slack != nil { + in, out := &in.Slack, &out.Slack + *out = new(SlackOptions) + (*in).DeepCopyInto(*out) + } + if in.Webhook != nil { + in, out := &in.Webhook, &out.Webhook + *out = new(WebhookOptions) + (*in).DeepCopyInto(*out) + } + if in.DingTalk != nil { + in, out := &in.DingTalk, &out.DingTalk + *out = new(DingTalkOptions) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Options. +func (in *Options) DeepCopy() *Options { + if in == nil { + return nil + } + out := new(Options) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Receiver) DeepCopyInto(out *Receiver) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Receiver. +func (in *Receiver) DeepCopy() *Receiver { + if in == nil { + return nil + } + out := new(Receiver) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Receiver) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReceiverList) DeepCopyInto(out *ReceiverList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Receiver, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReceiverList. +func (in *ReceiverList) DeepCopy() *ReceiverList { + if in == nil { + return nil + } + out := new(ReceiverList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ReceiverList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReceiverSpec) DeepCopyInto(out *ReceiverSpec) { + *out = *in + if in.DingTalk != nil { + in, out := &in.DingTalk, &out.DingTalk + *out = new(DingTalkReceiver) + (*in).DeepCopyInto(*out) + } + if in.Email != nil { + in, out := &in.Email, &out.Email + *out = new(EmailReceiver) + (*in).DeepCopyInto(*out) + } + if in.Slack != nil { + in, out := &in.Slack, &out.Slack + *out = new(SlackReceiver) + (*in).DeepCopyInto(*out) + } + if in.Webhook != nil { + in, out := &in.Webhook, &out.Webhook + *out = new(WebhookReceiver) + (*in).DeepCopyInto(*out) + } + if in.Wechat != nil { + in, out := &in.Wechat, &out.Wechat + *out = new(WechatReceiver) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReceiverSpec. +func (in *ReceiverSpec) DeepCopy() *ReceiverSpec { + if in == nil { + return nil + } + out := new(ReceiverSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReceiverStatus) DeepCopyInto(out *ReceiverStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReceiverStatus. +func (in *ReceiverStatus) DeepCopy() *ReceiverStatus { + if in == nil { + return nil + } + out := new(ReceiverStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReceiversSpec) DeepCopyInto(out *ReceiversSpec) { + *out = *in + if in.GlobalReceiverSelector != nil { + in, out := &in.GlobalReceiverSelector, &out.GlobalReceiverSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.TenantReceiverSelector != nil { + in, out := &in.TenantReceiverSelector, &out.TenantReceiverSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.Options != nil { + in, out := &in.Options, &out.Options + *out = new(Options) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReceiversSpec. +func (in *ReceiversSpec) DeepCopy() *ReceiversSpec { + if in == nil { + return nil + } + out := new(ReceiversSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeySelector. +func (in *SecretKeySelector) DeepCopy() *SecretKeySelector { + if in == nil { + return nil + } + out := new(SecretKeySelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceReference) DeepCopyInto(out *ServiceReference) { + *out = *in + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = new(string) + **out = **in + } + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int32) + **out = **in + } + if in.Scheme != nil { + in, out := &in.Scheme, &out.Scheme + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceReference. +func (in *ServiceReference) DeepCopy() *ServiceReference { + if in == nil { + return nil + } + out := new(ServiceReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SlackConfig) DeepCopyInto(out *SlackConfig) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.SlackTokenSecret != nil { + in, out := &in.SlackTokenSecret, &out.SlackTokenSecret + *out = new(SecretKeySelector) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlackConfig. +func (in *SlackConfig) DeepCopy() *SlackConfig { + if in == nil { + return nil + } + out := new(SlackConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SlackOptions) DeepCopyInto(out *SlackOptions) { + *out = *in + if in.NotificationTimeout != nil { + in, out := &in.NotificationTimeout, &out.NotificationTimeout + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlackOptions. +func (in *SlackOptions) DeepCopy() *SlackOptions { + if in == nil { + return nil + } + out := new(SlackOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SlackReceiver) DeepCopyInto(out *SlackReceiver) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.SlackConfigSelector != nil { + in, out := &in.SlackConfigSelector, &out.SlackConfigSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.AlertSelector != nil { + in, out := &in.AlertSelector, &out.AlertSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.Channels != nil { + in, out := &in.Channels, &out.Channels + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlackReceiver. +func (in *SlackReceiver) DeepCopy() *SlackReceiver { + if in == nil { + return nil + } + out := new(SlackReceiver) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSConfig) DeepCopyInto(out *TLSConfig) { + *out = *in + if in.RootCA != nil { + in, out := &in.RootCA, &out.RootCA + *out = new(SecretKeySelector) + **out = **in + } + if in.ClientCertificate != nil { + in, out := &in.ClientCertificate, &out.ClientCertificate + *out = new(ClientCertificate) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSConfig. +func (in *TLSConfig) DeepCopy() *TLSConfig { + if in == nil { + return nil + } + out := new(TLSConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Throttle) DeepCopyInto(out *Throttle) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Throttle. +func (in *Throttle) DeepCopy() *Throttle { + if in == nil { + return nil + } + out := new(Throttle) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookConfig) DeepCopyInto(out *WebhookConfig) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookConfig. +func (in *WebhookConfig) DeepCopy() *WebhookConfig { + if in == nil { + return nil + } + out := new(WebhookConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookOptions) DeepCopyInto(out *WebhookOptions) { + *out = *in + if in.NotificationTimeout != nil { + in, out := &in.NotificationTimeout, &out.NotificationTimeout + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookOptions. +func (in *WebhookOptions) DeepCopy() *WebhookOptions { + if in == nil { + return nil + } + out := new(WebhookOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookReceiver) DeepCopyInto(out *WebhookReceiver) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.WebhookConfigSelector != nil { + in, out := &in.WebhookConfigSelector, &out.WebhookConfigSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.AlertSelector != nil { + in, out := &in.AlertSelector, &out.AlertSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.URL != nil { + in, out := &in.URL, &out.URL + *out = new(string) + **out = **in + } + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = new(ServiceReference) + (*in).DeepCopyInto(*out) + } + if in.HTTPConfig != nil { + in, out := &in.HTTPConfig, &out.HTTPConfig + *out = new(HTTPClientConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookReceiver. +func (in *WebhookReceiver) DeepCopy() *WebhookReceiver { + if in == nil { + return nil + } + out := new(WebhookReceiver) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WechatConfig) DeepCopyInto(out *WechatConfig) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.WechatApiSecret != nil { + in, out := &in.WechatApiSecret, &out.WechatApiSecret + *out = new(SecretKeySelector) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WechatConfig. +func (in *WechatConfig) DeepCopy() *WechatConfig { + if in == nil { + return nil + } + out := new(WechatConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WechatOptions) DeepCopyInto(out *WechatOptions) { + *out = *in + if in.NotificationTimeout != nil { + in, out := &in.NotificationTimeout, &out.NotificationTimeout + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WechatOptions. +func (in *WechatOptions) DeepCopy() *WechatOptions { + if in == nil { + return nil + } + out := new(WechatOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WechatReceiver) DeepCopyInto(out *WechatReceiver) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.WechatConfigSelector != nil { + in, out := &in.WechatConfigSelector, &out.WechatConfigSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.AlertSelector != nil { + in, out := &in.AlertSelector, &out.AlertSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.ToUser != nil { + in, out := &in.ToUser, &out.ToUser + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ToParty != nil { + in, out := &in.ToParty, &out.ToParty + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ToTag != nil { + in, out := &in.ToTag, &out.ToTag + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WechatReceiver. +func (in *WechatReceiver) DeepCopy() *WechatReceiver { + if in == nil { + return nil + } + out := new(WechatReceiver) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/types/v1beta1/federatednotificationconfig_types.go b/pkg/apis/types/v1beta1/federatednotificationconfig_types.go new file mode 100644 index 0000000000000000000000000000000000000000..c866303efc0f92498021ee96c9f5e3203647443f --- /dev/null +++ b/pkg/apis/types/v1beta1/federatednotificationconfig_types.go @@ -0,0 +1,63 @@ +/* +Copyright 2020 KubeSphere Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" +) + +const ( + ResourcePluralFederatedNotificationConfig = "federatednotificationconfigs" + ResourceSingularFederatedNotificationConfig = "federatednotificationconfig" + FederatedNotificationConfigKind = "FederatedNotificationConfig" +) + +// +genclient:nonNamespaced +// +k8s:deepcopy-gen=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +k8s:openapi-gen=true +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:subresource:status +type FederatedNotificationConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec FederatedNotificationConfigSpec `json:"spec"` + + Status *GenericFederatedStatus `json:"status,omitempty"` +} + +type FederatedNotificationConfigSpec struct { + Template NotificationConfigTemplate `json:"template"` + Placement GenericPlacementFields `json:"placement"` + Overrides []GenericOverrideItem `json:"overrides,omitempty"` +} + +type NotificationConfigTemplate struct { + // +kubebuilder:pruning:PreserveUnknownFields + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec v2beta1.ConfigSpec `json:"spec,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// FederatedNotificationConfigList contains a list of federatednotificationconfiglists +type FederatedNotificationConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []FederatedNotificationConfig `json:"items"` +} diff --git a/pkg/apis/types/v1beta1/federatednotificationreceiver_types.go b/pkg/apis/types/v1beta1/federatednotificationreceiver_types.go new file mode 100644 index 0000000000000000000000000000000000000000..0640ce2e3657750b4eb7f3e6a74ac942fa07bfc3 --- /dev/null +++ b/pkg/apis/types/v1beta1/federatednotificationreceiver_types.go @@ -0,0 +1,62 @@ +/* +Copyright 2020 KubeSphere Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" +) + +const ( + ResourcePluralFederatedNotificationReceiver = "federatednotificationreceivers" + ResourceSingularFederatedNotificationReceiver = "federatednotificationreceiver" + FederatedNotificationReceiverKind = "FederatedNotificationReceiver" +) + +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +k8s:openapi-gen=true +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:subresource:status +type FederatedNotificationReceiver struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec FederatedNotificationReceiverSpec `json:"spec"` + + Status *GenericFederatedStatus `json:"status,omitempty"` +} + +type FederatedNotificationReceiverSpec struct { + Template NotificationReceiverTemplate `json:"template"` + Placement GenericPlacementFields `json:"placement"` + Overrides []GenericOverrideItem `json:"overrides,omitempty"` +} + +type NotificationReceiverTemplate struct { + // +kubebuilder:pruning:PreserveUnknownFields + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec v2beta1.ReceiverSpec `json:"spec,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// FederatedNotificationConfigList contains a list of federatednotificationreceiverlists +type FederatedNotificationReceiverList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []FederatedNotificationReceiver `json:"items"` +} diff --git a/pkg/apis/types/v1beta1/register.go b/pkg/apis/types/v1beta1/register.go index b11654efcfcfd60ac5c12995ca657d821cb1c616..2016a5146124f8e718cf1eebe2171a2f51f8bb82 100644 --- a/pkg/apis/types/v1beta1/register.go +++ b/pkg/apis/types/v1beta1/register.go @@ -62,6 +62,10 @@ func init() { &FederatedLimitRangeList{}, &FederatedNamespace{}, &FederatedNamespaceList{}, + &FederatedNotificationConfig{}, + &FederatedNotificationConfigList{}, + &FederatedNotificationReceiver{}, + &FederatedNotificationReceiverList{}, &FederatedPersistentVolumeClaim{}, &FederatedPersistentVolumeClaimList{}, &FederatedResourceQuota{}, diff --git a/pkg/apis/types/v1beta1/zz_generated.deepcopy.go b/pkg/apis/types/v1beta1/zz_generated.deepcopy.go index 5bffec1cd4e6166fa431b999bd83e2170d51d72c..1309ebf1398d4bd79b3fe79fe119c331cb47095f 100644 --- a/pkg/apis/types/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/types/v1beta1/zz_generated.deepcopy.go @@ -636,6 +636,7 @@ func (in *FederatedGroupBinding) DeepCopyInto(out *FederatedGroupBinding) { *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedGroupBinding. @@ -756,6 +757,7 @@ func (in *FederatedGroupSpec) DeepCopyInto(out *FederatedGroupSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedGroupSpec. @@ -779,6 +781,7 @@ func (in *FederatedIngress) DeepCopyInto(out *FederatedIngress) { *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedIngress. @@ -811,6 +814,7 @@ func (in *FederatedIngressList) DeepCopyInto(out *FederatedIngressList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedIngressList. @@ -843,6 +847,7 @@ func (in *FederatedIngressSpec) DeepCopyInto(out *FederatedIngressSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedIngressSpec. @@ -866,6 +871,7 @@ func (in *FederatedJob) DeepCopyInto(out *FederatedJob) { *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedJob. @@ -898,6 +904,7 @@ func (in *FederatedJobList) DeepCopyInto(out *FederatedJobList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedJobList. @@ -930,6 +937,7 @@ func (in *FederatedJobSpec) DeepCopyInto(out *FederatedJobSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedJobSpec. @@ -953,6 +961,7 @@ func (in *FederatedLimitRange) DeepCopyInto(out *FederatedLimitRange) { *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedLimitRange. @@ -985,6 +994,7 @@ func (in *FederatedLimitRangeList) DeepCopyInto(out *FederatedLimitRangeList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedLimitRangeList. @@ -1017,6 +1027,7 @@ func (in *FederatedLimitRangeSpec) DeepCopyInto(out *FederatedLimitRangeSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedLimitRangeSpec. @@ -1040,6 +1051,7 @@ func (in *FederatedNamespace) DeepCopyInto(out *FederatedNamespace) { *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedNamespace. @@ -1072,6 +1084,7 @@ func (in *FederatedNamespaceList) DeepCopyInto(out *FederatedNamespaceList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedNamespaceList. @@ -1104,6 +1117,7 @@ func (in *FederatedNamespaceSpec) DeepCopyInto(out *FederatedNamespaceSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedNamespaceSpec. @@ -1116,6 +1130,186 @@ func (in *FederatedNamespaceSpec) DeepCopy() *FederatedNamespaceSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FederatedNotificationConfig) DeepCopyInto(out *FederatedNotificationConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(GenericFederatedStatus) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedNotificationConfig. +func (in *FederatedNotificationConfig) DeepCopy() *FederatedNotificationConfig { + if in == nil { + return nil + } + out := new(FederatedNotificationConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *FederatedNotificationConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FederatedNotificationConfigList) DeepCopyInto(out *FederatedNotificationConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]FederatedNotificationConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedNotificationConfigList. +func (in *FederatedNotificationConfigList) DeepCopy() *FederatedNotificationConfigList { + if in == nil { + return nil + } + out := new(FederatedNotificationConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *FederatedNotificationConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FederatedNotificationConfigSpec) DeepCopyInto(out *FederatedNotificationConfigSpec) { + *out = *in + in.Template.DeepCopyInto(&out.Template) + in.Placement.DeepCopyInto(&out.Placement) + if in.Overrides != nil { + in, out := &in.Overrides, &out.Overrides + *out = make([]GenericOverrideItem, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedNotificationConfigSpec. +func (in *FederatedNotificationConfigSpec) DeepCopy() *FederatedNotificationConfigSpec { + if in == nil { + return nil + } + out := new(FederatedNotificationConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FederatedNotificationReceiver) DeepCopyInto(out *FederatedNotificationReceiver) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(GenericFederatedStatus) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedNotificationReceiver. +func (in *FederatedNotificationReceiver) DeepCopy() *FederatedNotificationReceiver { + if in == nil { + return nil + } + out := new(FederatedNotificationReceiver) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *FederatedNotificationReceiver) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FederatedNotificationReceiverList) DeepCopyInto(out *FederatedNotificationReceiverList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]FederatedNotificationReceiver, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedNotificationReceiverList. +func (in *FederatedNotificationReceiverList) DeepCopy() *FederatedNotificationReceiverList { + if in == nil { + return nil + } + out := new(FederatedNotificationReceiverList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *FederatedNotificationReceiverList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FederatedNotificationReceiverSpec) DeepCopyInto(out *FederatedNotificationReceiverSpec) { + *out = *in + in.Template.DeepCopyInto(&out.Template) + in.Placement.DeepCopyInto(&out.Placement) + if in.Overrides != nil { + in, out := &in.Overrides, &out.Overrides + *out = make([]GenericOverrideItem, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedNotificationReceiverSpec. +func (in *FederatedNotificationReceiverSpec) DeepCopy() *FederatedNotificationReceiverSpec { + if in == nil { + return nil + } + out := new(FederatedNotificationReceiverSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FederatedPersistentVolumeClaim) DeepCopyInto(out *FederatedPersistentVolumeClaim) { *out = *in @@ -1127,6 +1321,7 @@ func (in *FederatedPersistentVolumeClaim) DeepCopyInto(out *FederatedPersistentV *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedPersistentVolumeClaim. @@ -1191,6 +1386,7 @@ func (in *FederatedPersistentVolumeClaimSpec) DeepCopyInto(out *FederatedPersist (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedPersistentVolumeClaimSpec. @@ -1214,6 +1410,7 @@ func (in *FederatedResourceQuota) DeepCopyInto(out *FederatedResourceQuota) { *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedResourceQuota. @@ -1246,6 +1443,7 @@ func (in *FederatedResourceQuotaList) DeepCopyInto(out *FederatedResourceQuotaLi (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedResourceQuotaList. @@ -1278,6 +1476,7 @@ func (in *FederatedResourceQuotaSpec) DeepCopyInto(out *FederatedResourceQuotaSp (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedResourceQuotaSpec. @@ -1301,6 +1500,7 @@ func (in *FederatedSecret) DeepCopyInto(out *FederatedSecret) { *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedSecret. @@ -1333,6 +1533,7 @@ func (in *FederatedSecretList) DeepCopyInto(out *FederatedSecretList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedSecretList. @@ -1365,6 +1566,7 @@ func (in *FederatedSecretSpec) DeepCopyInto(out *FederatedSecretSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedSecretSpec. @@ -1388,6 +1590,7 @@ func (in *FederatedService) DeepCopyInto(out *FederatedService) { *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedService. @@ -1420,6 +1623,7 @@ func (in *FederatedServiceList) DeepCopyInto(out *FederatedServiceList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedServiceList. @@ -1452,6 +1656,7 @@ func (in *FederatedServiceSpec) DeepCopyInto(out *FederatedServiceSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedServiceSpec. @@ -1475,6 +1680,7 @@ func (in *FederatedStatefulSet) DeepCopyInto(out *FederatedStatefulSet) { *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedStatefulSet. @@ -1507,6 +1713,7 @@ func (in *FederatedStatefulSetList) DeepCopyInto(out *FederatedStatefulSetList) (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedStatefulSetList. @@ -1539,6 +1746,7 @@ func (in *FederatedStatefulSetSpec) DeepCopyInto(out *FederatedStatefulSetSpec) (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedStatefulSetSpec. @@ -1562,6 +1770,7 @@ func (in *FederatedUser) DeepCopyInto(out *FederatedUser) { *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedUser. @@ -1594,6 +1803,7 @@ func (in *FederatedUserList) DeepCopyInto(out *FederatedUserList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedUserList. @@ -1626,6 +1836,7 @@ func (in *FederatedUserSpec) DeepCopyInto(out *FederatedUserSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedUserSpec. @@ -1649,6 +1860,7 @@ func (in *FederatedWorkspace) DeepCopyInto(out *FederatedWorkspace) { *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspace. @@ -1681,6 +1893,7 @@ func (in *FederatedWorkspaceList) DeepCopyInto(out *FederatedWorkspaceList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceList. @@ -1712,6 +1925,7 @@ func (in *FederatedWorkspaceRole) DeepCopyInto(out *FederatedWorkspaceRole) { *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceRole. @@ -1743,6 +1957,7 @@ func (in *FederatedWorkspaceRoleBinding) DeepCopyInto(out *FederatedWorkspaceRol *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceRoleBinding. @@ -1775,6 +1990,7 @@ func (in *FederatedWorkspaceRoleBindingList) DeepCopyInto(out *FederatedWorkspac (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceRoleBindingList. @@ -1807,6 +2023,7 @@ func (in *FederatedWorkspaceRoleBindingSpec) DeepCopyInto(out *FederatedWorkspac (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceRoleBindingSpec. @@ -1831,6 +2048,7 @@ func (in *FederatedWorkspaceRoleList) DeepCopyInto(out *FederatedWorkspaceRoleLi (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceRoleList. @@ -1863,6 +2081,7 @@ func (in *FederatedWorkspaceRoleSpec) DeepCopyInto(out *FederatedWorkspaceRoleSp (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceRoleSpec. @@ -1887,6 +2106,7 @@ func (in *FederatedWorkspaceSpec) DeepCopyInto(out *FederatedWorkspaceSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceSpec. @@ -1902,6 +2122,7 @@ func (in *FederatedWorkspaceSpec) DeepCopy() *FederatedWorkspaceSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GenericClusterReference) DeepCopyInto(out *GenericClusterReference) { *out = *in + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericClusterReference. @@ -1917,6 +2138,7 @@ func (in *GenericClusterReference) DeepCopy() *GenericClusterReference { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GenericClusterStatus) DeepCopyInto(out *GenericClusterStatus) { *out = *in + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericClusterStatus. @@ -1932,6 +2154,7 @@ func (in *GenericClusterStatus) DeepCopy() *GenericClusterStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GenericCondition) DeepCopyInto(out *GenericCondition) { *out = *in + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericCondition. @@ -1954,6 +2177,7 @@ func (in *GenericFederatedResource) DeepCopyInto(out *GenericFederatedResource) *out = new(GenericFederatedStatus) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericFederatedResource. @@ -1985,6 +2209,7 @@ func (in *GenericFederatedStatus) DeepCopyInto(out *GenericFederatedStatus) { *out = make([]GenericClusterStatus, len(*in)) copy(*out, *in) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericFederatedStatus. @@ -2007,6 +2232,7 @@ func (in *GenericOverride) DeepCopyInto(out *GenericOverride) { *out = new(GenericOverrideSpec) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericOverride. @@ -2029,6 +2255,7 @@ func (in *GenericOverrideItem) DeepCopyInto(out *GenericOverrideItem) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericOverrideItem. @@ -2051,6 +2278,7 @@ func (in *GenericOverrideSpec) DeepCopyInto(out *GenericOverrideSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericOverrideSpec. @@ -2069,6 +2297,7 @@ func (in *GenericPlacement) DeepCopyInto(out *GenericPlacement) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericPlacement. @@ -2094,6 +2323,7 @@ func (in *GenericPlacementFields) DeepCopyInto(out *GenericPlacementFields) { *out = new(metav1.LabelSelector) (*in).DeepCopyInto(*out) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericPlacementFields. @@ -2110,6 +2340,7 @@ func (in *GenericPlacementFields) DeepCopy() *GenericPlacementFields { func (in *GenericPlacementSpec) DeepCopyInto(out *GenericPlacementSpec) { *out = *in in.Placement.DeepCopyInto(&out.Placement) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericPlacementSpec. @@ -2132,6 +2363,7 @@ func (in *GroupBindingTemplate) DeepCopyInto(out *GroupBindingTemplate) { *out = make([]string, len(*in)) copy(*out, *in) } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GroupBindingTemplate. @@ -2149,6 +2381,7 @@ func (in *GroupTemplate) DeepCopyInto(out *GroupTemplate) { *out = *in in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GroupTemplate. @@ -2165,6 +2398,7 @@ func (in *GroupTemplate) DeepCopy() *GroupTemplate { func (in *IngressTemplate) DeepCopyInto(out *IngressTemplate) { *out = *in in.Spec.DeepCopyInto(&out.Spec) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressTemplate. @@ -2181,6 +2415,7 @@ func (in *IngressTemplate) DeepCopy() *IngressTemplate { func (in *JobTemplate) DeepCopyInto(out *JobTemplate) { *out = *in in.Spec.DeepCopyInto(&out.Spec) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JobTemplate. @@ -2197,6 +2432,7 @@ func (in *JobTemplate) DeepCopy() *JobTemplate { func (in *LimitRangeTemplate) DeepCopyInto(out *LimitRangeTemplate) { *out = *in in.Spec.DeepCopyInto(&out.Spec) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LimitRangeTemplate. @@ -2213,6 +2449,7 @@ func (in *LimitRangeTemplate) DeepCopy() *LimitRangeTemplate { func (in *NamespaceTemplate) DeepCopyInto(out *NamespaceTemplate) { *out = *in in.Spec.DeepCopyInto(&out.Spec) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceTemplate. @@ -2225,12 +2462,49 @@ func (in *NamespaceTemplate) DeepCopy() *NamespaceTemplate { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NotificationConfigTemplate) DeepCopyInto(out *NotificationConfigTemplate) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NotificationConfigTemplate. +func (in *NotificationConfigTemplate) DeepCopy() *NotificationConfigTemplate { + if in == nil { + return nil + } + out := new(NotificationConfigTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NotificationReceiverTemplate) DeepCopyInto(out *NotificationReceiverTemplate) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NotificationReceiverTemplate. +func (in *NotificationReceiverTemplate) DeepCopy() *NotificationReceiverTemplate { + if in == nil { + return nil + } + out := new(NotificationReceiverTemplate) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PersistentVolumeClaimTemplate) DeepCopyInto(out *PersistentVolumeClaimTemplate) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistentVolumeClaimTemplate. @@ -2247,6 +2521,7 @@ func (in *PersistentVolumeClaimTemplate) DeepCopy() *PersistentVolumeClaimTempla func (in *ResourceQuotaTemplate) DeepCopyInto(out *ResourceQuotaTemplate) { *out = *in in.Spec.DeepCopyInto(&out.Spec) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceQuotaTemplate. @@ -2284,6 +2559,7 @@ func (in *SecretTemplate) DeepCopyInto(out *SecretTemplate) { (*out)[key] = val } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretTemplate. @@ -2302,6 +2578,7 @@ func (in *ServiceTemplate) DeepCopyInto(out *ServiceTemplate) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceTemplate. @@ -2320,6 +2597,7 @@ func (in *StatefulSetTemplate) DeepCopyInto(out *StatefulSetTemplate) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StatefulSetTemplate. @@ -2336,6 +2614,7 @@ func (in *StatefulSetTemplate) DeepCopy() *StatefulSetTemplate { func (in *UserTemplate) DeepCopyInto(out *UserTemplate) { *out = *in in.Spec.DeepCopyInto(&out.Spec) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserTemplate. @@ -2358,6 +2637,7 @@ func (in *WorkspaceRoleBindingTemplate) DeepCopyInto(out *WorkspaceRoleBindingTe copy(*out, *in) } out.RoleRef = in.RoleRef + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleBindingTemplate. @@ -2381,6 +2661,7 @@ func (in *WorkspaceRoleTemplate) DeepCopyInto(out *WorkspaceRoleTemplate) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleTemplate. @@ -2398,6 +2679,7 @@ func (in *WorkspaceTemplate) DeepCopyInto(out *WorkspaceTemplate) { *out = *in in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) + return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceTemplate. diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index de26b8103916aa34fa7b0cb2d75bfb90b0059f38..625e160f8bc6bf67ab590cdafb9771044d565f5c 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -20,6 +20,7 @@ import ( "bytes" "context" "fmt" + "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" "net/http" rt "runtime" "time" @@ -37,6 +38,7 @@ import ( "k8s.io/klog" clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + notificationv2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" typesv1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1" audit "kubesphere.io/kubesphere/pkg/apiserver/auditing" @@ -68,6 +70,7 @@ import ( monitoringv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha3" networkv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/network/v1alpha2" notificationv1 "kubesphere.io/kubesphere/pkg/kapis/notification/v1" + notificationkapisv2beta1 "kubesphere.io/kubesphere/pkg/kapis/notification/v2beta1" "kubesphere.io/kubesphere/pkg/kapis/oauth" openpitrixv1 "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v1" operationsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/operations/v1alpha2" @@ -270,6 +273,8 @@ func (s *APIServer) installKubeSphereAPIs() { s.KubernetesClient.Prometheus(), s.AlertingClient, s.Config.AlertingOptions)) urlruntime.Must(version.AddToContainer(s.container, s.KubernetesClient.Discovery())) urlruntime.Must(kubeedgev1alpha1.AddToContainer(s.container, s.Config.KubeEdgeOptions.Endpoint)) + urlruntime.Must(notificationkapisv2beta1.AddToContainer(s.container, s.InformerFactory, s.KubernetesClient.Kubernetes(), + s.KubernetesClient.KubeSphere())) } func (s *APIServer) Run(stopCh <-chan struct{}) (err error) { @@ -310,6 +315,8 @@ func (s *APIServer) buildHandlerChain(stopCh <-chan struct{}) { tenantv1alpha2.Resource(clusterv1alpha1.ResourcesPluralCluster), clusterv1alpha1.Resource(clusterv1alpha1.ResourcesPluralCluster), resourcev1alpha3.Resource(clusterv1alpha1.ResourcesPluralCluster), + notificationv2beta1.Resource(v2beta1.ResourcesPluralConfig), + notificationv2beta1.Resource(v2beta1.ResourcesPluralReceiver), }, } @@ -443,6 +450,8 @@ func (s *APIServer) waitForResourceSync(stopCh <-chan struct{}) error { {Group: "cluster.kubesphere.io", Version: "v1alpha1", Resource: "clusters"}, {Group: "devops.kubesphere.io", Version: "v1alpha3", Resource: "devopsprojects"}, {Group: "network.kubesphere.io", Version: "v1alpha1", Resource: "ippools"}, + {Group: "notification.kubesphere.io", Version: "v2beta1", Resource: v2beta1.ResourcesPluralConfig}, + {Group: "notification.kubesphere.io", Version: "v2beta1", Resource: v2beta1.ResourcesPluralReceiver}, } devopsGVRs := []schema.GroupVersionResource{ diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index 48f01b5c6fef19b33e3626ecd90213284ada82ba..f70e1ce8d0b7f91bf140673742c9339435af79ba 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -31,6 +31,7 @@ import ( devopsv1alpha3 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha3" iamv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/iam/v1alpha2" networkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1" + notificationv2beta1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/notification/v2beta1" quotav1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/quota/v1alpha2" servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2" storagev1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/storage/v1alpha1" @@ -48,6 +49,7 @@ type Interface interface { DevopsV1alpha3() devopsv1alpha3.DevopsV1alpha3Interface IamV1alpha2() iamv1alpha2.IamV1alpha2Interface NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface + NotificationV2beta1() notificationv2beta1.NotificationV2beta1Interface QuotaV1alpha2() quotav1alpha2.QuotaV1alpha2Interface ServicemeshV1alpha2() servicemeshv1alpha2.ServicemeshV1alpha2Interface StorageV1alpha1() storagev1alpha1.StorageV1alpha1Interface @@ -67,6 +69,7 @@ type Clientset struct { devopsV1alpha3 *devopsv1alpha3.DevopsV1alpha3Client iamV1alpha2 *iamv1alpha2.IamV1alpha2Client networkV1alpha1 *networkv1alpha1.NetworkV1alpha1Client + notificationV2beta1 *notificationv2beta1.NotificationV2beta1Client quotaV1alpha2 *quotav1alpha2.QuotaV1alpha2Client servicemeshV1alpha2 *servicemeshv1alpha2.ServicemeshV1alpha2Client storageV1alpha1 *storagev1alpha1.StorageV1alpha1Client @@ -110,6 +113,11 @@ func (c *Clientset) NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface { return c.networkV1alpha1 } +// NotificationV2beta1 retrieves the NotificationV2beta1Client +func (c *Clientset) NotificationV2beta1() notificationv2beta1.NotificationV2beta1Interface { + return c.notificationV2beta1 +} + // QuotaV1alpha2 retrieves the QuotaV1alpha2Client func (c *Clientset) QuotaV1alpha2() quotav1alpha2.QuotaV1alpha2Interface { return c.quotaV1alpha2 @@ -189,6 +197,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { if err != nil { return nil, err } + cs.notificationV2beta1, err = notificationv2beta1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } cs.quotaV1alpha2, err = quotav1alpha2.NewForConfig(&configShallowCopy) if err != nil { return nil, err @@ -232,6 +244,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset { cs.devopsV1alpha3 = devopsv1alpha3.NewForConfigOrDie(c) cs.iamV1alpha2 = iamv1alpha2.NewForConfigOrDie(c) cs.networkV1alpha1 = networkv1alpha1.NewForConfigOrDie(c) + cs.notificationV2beta1 = notificationv2beta1.NewForConfigOrDie(c) cs.quotaV1alpha2 = quotav1alpha2.NewForConfigOrDie(c) cs.servicemeshV1alpha2 = servicemeshv1alpha2.NewForConfigOrDie(c) cs.storageV1alpha1 = storagev1alpha1.NewForConfigOrDie(c) @@ -253,6 +266,7 @@ func New(c rest.Interface) *Clientset { cs.devopsV1alpha3 = devopsv1alpha3.New(c) cs.iamV1alpha2 = iamv1alpha2.New(c) cs.networkV1alpha1 = networkv1alpha1.New(c) + cs.notificationV2beta1 = notificationv2beta1.New(c) cs.quotaV1alpha2 = quotav1alpha2.New(c) cs.servicemeshV1alpha2 = servicemeshv1alpha2.New(c) cs.storageV1alpha1 = storagev1alpha1.New(c) diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index 1bb7bd111aa2ad0bf4a51e726381534e49206189..09f5eb0bce181e3d3259d7b1d738d7f51c4a9444 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -39,6 +39,8 @@ import ( fakeiamv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake" networkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1" fakenetworkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1/fake" + notificationv2beta1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/notification/v2beta1" + fakenotificationv2beta1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/notification/v2beta1/fake" quotav1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/quota/v1alpha2" fakequotav1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/quota/v1alpha2/fake" servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2" @@ -135,6 +137,11 @@ func (c *Clientset) NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface { return &fakenetworkv1alpha1.FakeNetworkV1alpha1{Fake: &c.Fake} } +// NotificationV2beta1 retrieves the NotificationV2beta1Client +func (c *Clientset) NotificationV2beta1() notificationv2beta1.NotificationV2beta1Interface { + return &fakenotificationv2beta1.FakeNotificationV2beta1{Fake: &c.Fake} +} + // QuotaV1alpha2 retrieves the QuotaV1alpha2Client func (c *Clientset) QuotaV1alpha2() quotav1alpha2.QuotaV1alpha2Interface { return &fakequotav1alpha2.FakeQuotaV1alpha2{Fake: &c.Fake} diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index 3a529a6c8a2edaceed00d7c0285f00b06852c0b6..a4d2b972c92a2f677a50e66d7c1c7d039a3946a1 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -31,6 +31,7 @@ import ( devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" + notificationv2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" quotav1alpha2 "kubesphere.io/kubesphere/pkg/apis/quota/v1alpha2" servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2" storagev1alpha1 "kubesphere.io/kubesphere/pkg/apis/storage/v1alpha1" @@ -50,6 +51,7 @@ var localSchemeBuilder = runtime.SchemeBuilder{ devopsv1alpha3.AddToScheme, iamv1alpha2.AddToScheme, networkv1alpha1.AddToScheme, + notificationv2beta1.AddToScheme, quotav1alpha2.AddToScheme, servicemeshv1alpha2.AddToScheme, storagev1alpha1.AddToScheme, diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go index b366e23331ea9f6a1b6d32620bc60985aa3e8b75..9813366c7cc361568f449257225b95c5ce80be52 100644 --- a/pkg/client/clientset/versioned/scheme/register.go +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -31,6 +31,7 @@ import ( devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" + notificationv2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" quotav1alpha2 "kubesphere.io/kubesphere/pkg/apis/quota/v1alpha2" servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2" storagev1alpha1 "kubesphere.io/kubesphere/pkg/apis/storage/v1alpha1" @@ -50,6 +51,7 @@ var localSchemeBuilder = runtime.SchemeBuilder{ devopsv1alpha3.AddToScheme, iamv1alpha2.AddToScheme, networkv1alpha1.AddToScheme, + notificationv2beta1.AddToScheme, quotav1alpha2.AddToScheme, servicemeshv1alpha2.AddToScheme, storagev1alpha1.AddToScheme, diff --git a/pkg/client/clientset/versioned/typed/notification/v2beta1/config.go b/pkg/client/clientset/versioned/typed/notification/v2beta1/config.go new file mode 100644 index 0000000000000000000000000000000000000000..b85bcaa206b95e75fcf38ca00cd0bf4f5339c6c8 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/notification/v2beta1/config.go @@ -0,0 +1,184 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v2beta1 + +import ( + "context" + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// ConfigsGetter has a method to return a ConfigInterface. +// A group's client should implement this interface. +type ConfigsGetter interface { + Configs() ConfigInterface +} + +// ConfigInterface has methods to work with Config resources. +type ConfigInterface interface { + Create(ctx context.Context, config *v2beta1.Config, opts v1.CreateOptions) (*v2beta1.Config, error) + Update(ctx context.Context, config *v2beta1.Config, opts v1.UpdateOptions) (*v2beta1.Config, error) + UpdateStatus(ctx context.Context, config *v2beta1.Config, opts v1.UpdateOptions) (*v2beta1.Config, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v2beta1.Config, error) + List(ctx context.Context, opts v1.ListOptions) (*v2beta1.ConfigList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2beta1.Config, err error) + ConfigExpansion +} + +// configs implements ConfigInterface +type configs struct { + client rest.Interface +} + +// newConfigs returns a Configs +func newConfigs(c *NotificationV2beta1Client) *configs { + return &configs{ + client: c.RESTClient(), + } +} + +// Get takes name of the config, and returns the corresponding config object, and an error if there is any. +func (c *configs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2beta1.Config, err error) { + result = &v2beta1.Config{} + err = c.client.Get(). + Resource("configs"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Configs that match those selectors. +func (c *configs) List(ctx context.Context, opts v1.ListOptions) (result *v2beta1.ConfigList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v2beta1.ConfigList{} + err = c.client.Get(). + Resource("configs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested configs. +func (c *configs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("configs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a config and creates it. Returns the server's representation of the config, and an error, if there is any. +func (c *configs) Create(ctx context.Context, config *v2beta1.Config, opts v1.CreateOptions) (result *v2beta1.Config, err error) { + result = &v2beta1.Config{} + err = c.client.Post(). + Resource("configs"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(config). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a config and updates it. Returns the server's representation of the config, and an error, if there is any. +func (c *configs) Update(ctx context.Context, config *v2beta1.Config, opts v1.UpdateOptions) (result *v2beta1.Config, err error) { + result = &v2beta1.Config{} + err = c.client.Put(). + Resource("configs"). + Name(config.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(config). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *configs) UpdateStatus(ctx context.Context, config *v2beta1.Config, opts v1.UpdateOptions) (result *v2beta1.Config, err error) { + result = &v2beta1.Config{} + err = c.client.Put(). + Resource("configs"). + Name(config.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(config). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the config and deletes it. Returns an error if one occurs. +func (c *configs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Resource("configs"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *configs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("configs"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched config. +func (c *configs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2beta1.Config, err error) { + result = &v2beta1.Config{} + err = c.client.Patch(pt). + Resource("configs"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/notification/v2beta1/doc.go b/pkg/client/clientset/versioned/typed/notification/v2beta1/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..e7298214245a34f737fc9b7d9ccb8eff0d01af9e --- /dev/null +++ b/pkg/client/clientset/versioned/typed/notification/v2beta1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v2beta1 diff --git a/pkg/client/clientset/versioned/typed/notification/v2beta1/fake/doc.go b/pkg/client/clientset/versioned/typed/notification/v2beta1/fake/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..7e36dbca87adb21bcb79573d414617f117fb550e --- /dev/null +++ b/pkg/client/clientset/versioned/typed/notification/v2beta1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/client/clientset/versioned/typed/notification/v2beta1/fake/fake_config.go b/pkg/client/clientset/versioned/typed/notification/v2beta1/fake/fake_config.go new file mode 100644 index 0000000000000000000000000000000000000000..57b7478464b7631af234b7c758152fefd65dc0cd --- /dev/null +++ b/pkg/client/clientset/versioned/typed/notification/v2beta1/fake/fake_config.go @@ -0,0 +1,133 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" +) + +// FakeConfigs implements ConfigInterface +type FakeConfigs struct { + Fake *FakeNotificationV2beta1 +} + +var configsResource = schema.GroupVersionResource{Group: "notification.kubesphere.io", Version: "v2beta1", Resource: "configs"} + +var configsKind = schema.GroupVersionKind{Group: "notification.kubesphere.io", Version: "v2beta1", Kind: "Config"} + +// Get takes name of the config, and returns the corresponding config object, and an error if there is any. +func (c *FakeConfigs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2beta1.Config, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(configsResource, name), &v2beta1.Config{}) + if obj == nil { + return nil, err + } + return obj.(*v2beta1.Config), err +} + +// List takes label and field selectors, and returns the list of Configs that match those selectors. +func (c *FakeConfigs) List(ctx context.Context, opts v1.ListOptions) (result *v2beta1.ConfigList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(configsResource, configsKind, opts), &v2beta1.ConfigList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v2beta1.ConfigList{ListMeta: obj.(*v2beta1.ConfigList).ListMeta} + for _, item := range obj.(*v2beta1.ConfigList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested configs. +func (c *FakeConfigs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(configsResource, opts)) +} + +// Create takes the representation of a config and creates it. Returns the server's representation of the config, and an error, if there is any. +func (c *FakeConfigs) Create(ctx context.Context, config *v2beta1.Config, opts v1.CreateOptions) (result *v2beta1.Config, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(configsResource, config), &v2beta1.Config{}) + if obj == nil { + return nil, err + } + return obj.(*v2beta1.Config), err +} + +// Update takes the representation of a config and updates it. Returns the server's representation of the config, and an error, if there is any. +func (c *FakeConfigs) Update(ctx context.Context, config *v2beta1.Config, opts v1.UpdateOptions) (result *v2beta1.Config, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(configsResource, config), &v2beta1.Config{}) + if obj == nil { + return nil, err + } + return obj.(*v2beta1.Config), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeConfigs) UpdateStatus(ctx context.Context, config *v2beta1.Config, opts v1.UpdateOptions) (*v2beta1.Config, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(configsResource, "status", config), &v2beta1.Config{}) + if obj == nil { + return nil, err + } + return obj.(*v2beta1.Config), err +} + +// Delete takes name of the config and deletes it. Returns an error if one occurs. +func (c *FakeConfigs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(configsResource, name), &v2beta1.Config{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeConfigs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(configsResource, listOpts) + + _, err := c.Fake.Invokes(action, &v2beta1.ConfigList{}) + return err +} + +// Patch applies the patch and returns the patched config. +func (c *FakeConfigs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2beta1.Config, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(configsResource, name, pt, data, subresources...), &v2beta1.Config{}) + if obj == nil { + return nil, err + } + return obj.(*v2beta1.Config), err +} diff --git a/pkg/client/clientset/versioned/typed/notification/v2beta1/fake/fake_notification_client.go b/pkg/client/clientset/versioned/typed/notification/v2beta1/fake/fake_notification_client.go new file mode 100644 index 0000000000000000000000000000000000000000..943c88630b75ffa5c7994c93a67be8e1449a19d2 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/notification/v2beta1/fake/fake_notification_client.go @@ -0,0 +1,44 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" + v2beta1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/notification/v2beta1" +) + +type FakeNotificationV2beta1 struct { + *testing.Fake +} + +func (c *FakeNotificationV2beta1) Configs() v2beta1.ConfigInterface { + return &FakeConfigs{c} +} + +func (c *FakeNotificationV2beta1) Receivers() v2beta1.ReceiverInterface { + return &FakeReceivers{c} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeNotificationV2beta1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/client/clientset/versioned/typed/notification/v2beta1/fake/fake_receiver.go b/pkg/client/clientset/versioned/typed/notification/v2beta1/fake/fake_receiver.go new file mode 100644 index 0000000000000000000000000000000000000000..4f887de8d868e3d17740f3696f1d9f6a989b8300 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/notification/v2beta1/fake/fake_receiver.go @@ -0,0 +1,133 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" +) + +// FakeReceivers implements ReceiverInterface +type FakeReceivers struct { + Fake *FakeNotificationV2beta1 +} + +var receiversResource = schema.GroupVersionResource{Group: "notification.kubesphere.io", Version: "v2beta1", Resource: "receivers"} + +var receiversKind = schema.GroupVersionKind{Group: "notification.kubesphere.io", Version: "v2beta1", Kind: "Receiver"} + +// Get takes name of the receiver, and returns the corresponding receiver object, and an error if there is any. +func (c *FakeReceivers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2beta1.Receiver, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(receiversResource, name), &v2beta1.Receiver{}) + if obj == nil { + return nil, err + } + return obj.(*v2beta1.Receiver), err +} + +// List takes label and field selectors, and returns the list of Receivers that match those selectors. +func (c *FakeReceivers) List(ctx context.Context, opts v1.ListOptions) (result *v2beta1.ReceiverList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(receiversResource, receiversKind, opts), &v2beta1.ReceiverList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v2beta1.ReceiverList{ListMeta: obj.(*v2beta1.ReceiverList).ListMeta} + for _, item := range obj.(*v2beta1.ReceiverList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested receivers. +func (c *FakeReceivers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(receiversResource, opts)) +} + +// Create takes the representation of a receiver and creates it. Returns the server's representation of the receiver, and an error, if there is any. +func (c *FakeReceivers) Create(ctx context.Context, receiver *v2beta1.Receiver, opts v1.CreateOptions) (result *v2beta1.Receiver, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(receiversResource, receiver), &v2beta1.Receiver{}) + if obj == nil { + return nil, err + } + return obj.(*v2beta1.Receiver), err +} + +// Update takes the representation of a receiver and updates it. Returns the server's representation of the receiver, and an error, if there is any. +func (c *FakeReceivers) Update(ctx context.Context, receiver *v2beta1.Receiver, opts v1.UpdateOptions) (result *v2beta1.Receiver, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(receiversResource, receiver), &v2beta1.Receiver{}) + if obj == nil { + return nil, err + } + return obj.(*v2beta1.Receiver), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeReceivers) UpdateStatus(ctx context.Context, receiver *v2beta1.Receiver, opts v1.UpdateOptions) (*v2beta1.Receiver, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(receiversResource, "status", receiver), &v2beta1.Receiver{}) + if obj == nil { + return nil, err + } + return obj.(*v2beta1.Receiver), err +} + +// Delete takes name of the receiver and deletes it. Returns an error if one occurs. +func (c *FakeReceivers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(receiversResource, name), &v2beta1.Receiver{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeReceivers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(receiversResource, listOpts) + + _, err := c.Fake.Invokes(action, &v2beta1.ReceiverList{}) + return err +} + +// Patch applies the patch and returns the patched receiver. +func (c *FakeReceivers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2beta1.Receiver, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(receiversResource, name, pt, data, subresources...), &v2beta1.Receiver{}) + if obj == nil { + return nil, err + } + return obj.(*v2beta1.Receiver), err +} diff --git a/pkg/client/clientset/versioned/typed/notification/v2beta1/generated_expansion.go b/pkg/client/clientset/versioned/typed/notification/v2beta1/generated_expansion.go new file mode 100644 index 0000000000000000000000000000000000000000..ecc049523bc6bb0b48c1e441c21b3c84f3f9f1dc --- /dev/null +++ b/pkg/client/clientset/versioned/typed/notification/v2beta1/generated_expansion.go @@ -0,0 +1,23 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v2beta1 + +type ConfigExpansion interface{} + +type ReceiverExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/notification/v2beta1/notification_client.go b/pkg/client/clientset/versioned/typed/notification/v2beta1/notification_client.go new file mode 100644 index 0000000000000000000000000000000000000000..a494ff7581f834edefb0a95574b5d964ee9663b9 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/notification/v2beta1/notification_client.go @@ -0,0 +1,94 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v2beta1 + +import ( + rest "k8s.io/client-go/rest" + v2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +type NotificationV2beta1Interface interface { + RESTClient() rest.Interface + ConfigsGetter + ReceiversGetter +} + +// NotificationV2beta1Client is used to interact with features provided by the notification.kubesphere.io group. +type NotificationV2beta1Client struct { + restClient rest.Interface +} + +func (c *NotificationV2beta1Client) Configs() ConfigInterface { + return newConfigs(c) +} + +func (c *NotificationV2beta1Client) Receivers() ReceiverInterface { + return newReceivers(c) +} + +// NewForConfig creates a new NotificationV2beta1Client for the given config. +func NewForConfig(c *rest.Config) (*NotificationV2beta1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &NotificationV2beta1Client{client}, nil +} + +// NewForConfigOrDie creates a new NotificationV2beta1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *NotificationV2beta1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new NotificationV2beta1Client for the given RESTClient. +func New(c rest.Interface) *NotificationV2beta1Client { + return &NotificationV2beta1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v2beta1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *NotificationV2beta1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/client/clientset/versioned/typed/notification/v2beta1/receiver.go b/pkg/client/clientset/versioned/typed/notification/v2beta1/receiver.go new file mode 100644 index 0000000000000000000000000000000000000000..24adee0006c0c1544d1d773febacf428a7bf67f8 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/notification/v2beta1/receiver.go @@ -0,0 +1,184 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v2beta1 + +import ( + "context" + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// ReceiversGetter has a method to return a ReceiverInterface. +// A group's client should implement this interface. +type ReceiversGetter interface { + Receivers() ReceiverInterface +} + +// ReceiverInterface has methods to work with Receiver resources. +type ReceiverInterface interface { + Create(ctx context.Context, receiver *v2beta1.Receiver, opts v1.CreateOptions) (*v2beta1.Receiver, error) + Update(ctx context.Context, receiver *v2beta1.Receiver, opts v1.UpdateOptions) (*v2beta1.Receiver, error) + UpdateStatus(ctx context.Context, receiver *v2beta1.Receiver, opts v1.UpdateOptions) (*v2beta1.Receiver, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v2beta1.Receiver, error) + List(ctx context.Context, opts v1.ListOptions) (*v2beta1.ReceiverList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2beta1.Receiver, err error) + ReceiverExpansion +} + +// receivers implements ReceiverInterface +type receivers struct { + client rest.Interface +} + +// newReceivers returns a Receivers +func newReceivers(c *NotificationV2beta1Client) *receivers { + return &receivers{ + client: c.RESTClient(), + } +} + +// Get takes name of the receiver, and returns the corresponding receiver object, and an error if there is any. +func (c *receivers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2beta1.Receiver, err error) { + result = &v2beta1.Receiver{} + err = c.client.Get(). + Resource("receivers"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Receivers that match those selectors. +func (c *receivers) List(ctx context.Context, opts v1.ListOptions) (result *v2beta1.ReceiverList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v2beta1.ReceiverList{} + err = c.client.Get(). + Resource("receivers"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested receivers. +func (c *receivers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("receivers"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a receiver and creates it. Returns the server's representation of the receiver, and an error, if there is any. +func (c *receivers) Create(ctx context.Context, receiver *v2beta1.Receiver, opts v1.CreateOptions) (result *v2beta1.Receiver, err error) { + result = &v2beta1.Receiver{} + err = c.client.Post(). + Resource("receivers"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(receiver). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a receiver and updates it. Returns the server's representation of the receiver, and an error, if there is any. +func (c *receivers) Update(ctx context.Context, receiver *v2beta1.Receiver, opts v1.UpdateOptions) (result *v2beta1.Receiver, err error) { + result = &v2beta1.Receiver{} + err = c.client.Put(). + Resource("receivers"). + Name(receiver.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(receiver). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *receivers) UpdateStatus(ctx context.Context, receiver *v2beta1.Receiver, opts v1.UpdateOptions) (result *v2beta1.Receiver, err error) { + result = &v2beta1.Receiver{} + err = c.client.Put(). + Resource("receivers"). + Name(receiver.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(receiver). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the receiver and deletes it. Returns an error if one occurs. +func (c *receivers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Resource("receivers"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *receivers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("receivers"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched receiver. +func (c *receivers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2beta1.Receiver, err error) { + result = &v2beta1.Receiver{} + err = c.client.Patch(pt). + Resource("receivers"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go index 49260176bf91fb239e7d986d4ba91ac1298f7348..efde8e5b3e0df96cada69b359027adeeee4d3f58 100644 --- a/pkg/client/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -35,6 +35,7 @@ import ( iam "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam" internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" network "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network" + notification "kubesphere.io/kubesphere/pkg/client/informers/externalversions/notification" quota "kubesphere.io/kubesphere/pkg/client/informers/externalversions/quota" servicemesh "kubesphere.io/kubesphere/pkg/client/informers/externalversions/servicemesh" storage "kubesphere.io/kubesphere/pkg/client/informers/externalversions/storage" @@ -188,6 +189,7 @@ type SharedInformerFactory interface { Devops() devops.Interface Iam() iam.Interface Network() network.Interface + Notification() notification.Interface Quota() quota.Interface Servicemesh() servicemesh.Interface Storage() storage.Interface @@ -219,6 +221,10 @@ func (f *sharedInformerFactory) Network() network.Interface { return network.New(f, f.namespace, f.tweakListOptions) } +func (f *sharedInformerFactory) Notification() notification.Interface { + return notification.New(f, f.namespace, f.tweakListOptions) +} + func (f *sharedInformerFactory) Quota() quota.Interface { return quota.New(f, f.namespace, f.tweakListOptions) } diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index f0c144604a4496d7c72d3498fcc42e7d2df6c1c8..8ab0de638463ef8f605bec639a77ef3ac0b939ce 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -30,6 +30,7 @@ import ( v1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" + v2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" quotav1alpha2 "kubesphere.io/kubesphere/pkg/apis/quota/v1alpha2" servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2" storagev1alpha1 "kubesphere.io/kubesphere/pkg/apis/storage/v1alpha1" @@ -132,6 +133,12 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource case networkv1alpha1.SchemeGroupVersion.WithResource("namespacenetworkpolicies"): return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().NamespaceNetworkPolicies().Informer()}, nil + // Group=notification.kubesphere.io, Version=v2beta1 + case v2beta1.SchemeGroupVersion.WithResource("configs"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Notification().V2beta1().Configs().Informer()}, nil + case v2beta1.SchemeGroupVersion.WithResource("receivers"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Notification().V2beta1().Receivers().Informer()}, nil + // Group=quota.kubesphere.io, Version=v1alpha2 case quotav1alpha2.SchemeGroupVersion.WithResource("resourcequotas"): return &genericInformer{resource: resource.GroupResource(), informer: f.Quota().V1alpha2().ResourceQuotas().Informer()}, nil diff --git a/pkg/client/informers/externalversions/notification/interface.go b/pkg/client/informers/externalversions/notification/interface.go new file mode 100644 index 0000000000000000000000000000000000000000..58de480dc2fdac0de4f72d4b8144841391157b95 --- /dev/null +++ b/pkg/client/informers/externalversions/notification/interface.go @@ -0,0 +1,46 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package notification + +import ( + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v2beta1 "kubesphere.io/kubesphere/pkg/client/informers/externalversions/notification/v2beta1" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V2beta1 provides access to shared informers for resources in V2beta1. + V2beta1() v2beta1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V2beta1 returns a new v2beta1.Interface. +func (g *group) V2beta1() v2beta1.Interface { + return v2beta1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/client/informers/externalversions/notification/v2beta1/config.go b/pkg/client/informers/externalversions/notification/v2beta1/config.go new file mode 100644 index 0000000000000000000000000000000000000000..17f576409ac676fb06cad3d4353e44efd7b3e292 --- /dev/null +++ b/pkg/client/informers/externalversions/notification/v2beta1/config.go @@ -0,0 +1,89 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v2beta1 + +import ( + "context" + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + notificationv2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v2beta1 "kubesphere.io/kubesphere/pkg/client/listers/notification/v2beta1" +) + +// ConfigInformer provides access to a shared informer and lister for +// Configs. +type ConfigInformer interface { + Informer() cache.SharedIndexInformer + Lister() v2beta1.ConfigLister +} + +type configInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewConfigInformer constructs a new informer for Config type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewConfigInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredConfigInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredConfigInformer constructs a new informer for Config type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredConfigInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.NotificationV2beta1().Configs().List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.NotificationV2beta1().Configs().Watch(context.TODO(), options) + }, + }, + ¬ificationv2beta1.Config{}, + resyncPeriod, + indexers, + ) +} + +func (f *configInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredConfigInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *configInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(¬ificationv2beta1.Config{}, f.defaultInformer) +} + +func (f *configInformer) Lister() v2beta1.ConfigLister { + return v2beta1.NewConfigLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/notification/v2beta1/interface.go b/pkg/client/informers/externalversions/notification/v2beta1/interface.go new file mode 100644 index 0000000000000000000000000000000000000000..a429dca4210b6a092c796a191e3c2e10ab845e00 --- /dev/null +++ b/pkg/client/informers/externalversions/notification/v2beta1/interface.go @@ -0,0 +1,52 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v2beta1 + +import ( + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // Configs returns a ConfigInformer. + Configs() ConfigInformer + // Receivers returns a ReceiverInformer. + Receivers() ReceiverInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// Configs returns a ConfigInformer. +func (v *version) Configs() ConfigInformer { + return &configInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// Receivers returns a ReceiverInformer. +func (v *version) Receivers() ReceiverInformer { + return &receiverInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/notification/v2beta1/receiver.go b/pkg/client/informers/externalversions/notification/v2beta1/receiver.go new file mode 100644 index 0000000000000000000000000000000000000000..c984b7ec10ee17509339e5d284076e54c4539452 --- /dev/null +++ b/pkg/client/informers/externalversions/notification/v2beta1/receiver.go @@ -0,0 +1,89 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v2beta1 + +import ( + "context" + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + notificationv2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v2beta1 "kubesphere.io/kubesphere/pkg/client/listers/notification/v2beta1" +) + +// ReceiverInformer provides access to a shared informer and lister for +// Receivers. +type ReceiverInformer interface { + Informer() cache.SharedIndexInformer + Lister() v2beta1.ReceiverLister +} + +type receiverInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewReceiverInformer constructs a new informer for Receiver type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewReceiverInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredReceiverInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredReceiverInformer constructs a new informer for Receiver type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredReceiverInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.NotificationV2beta1().Receivers().List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.NotificationV2beta1().Receivers().Watch(context.TODO(), options) + }, + }, + ¬ificationv2beta1.Receiver{}, + resyncPeriod, + indexers, + ) +} + +func (f *receiverInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredReceiverInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *receiverInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(¬ificationv2beta1.Receiver{}, f.defaultInformer) +} + +func (f *receiverInformer) Lister() v2beta1.ReceiverLister { + return v2beta1.NewReceiverLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/listers/notification/v2beta1/config.go b/pkg/client/listers/notification/v2beta1/config.go new file mode 100644 index 0000000000000000000000000000000000000000..00755435f3a57f984e720de7f24b7feeca2a11fe --- /dev/null +++ b/pkg/client/listers/notification/v2beta1/config.go @@ -0,0 +1,65 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v2beta1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" +) + +// ConfigLister helps list Configs. +type ConfigLister interface { + // List lists all Configs in the indexer. + List(selector labels.Selector) (ret []*v2beta1.Config, err error) + // Get retrieves the Config from the index for a given name. + Get(name string) (*v2beta1.Config, error) + ConfigListerExpansion +} + +// configLister implements the ConfigLister interface. +type configLister struct { + indexer cache.Indexer +} + +// NewConfigLister returns a new ConfigLister. +func NewConfigLister(indexer cache.Indexer) ConfigLister { + return &configLister{indexer: indexer} +} + +// List lists all Configs in the indexer. +func (s *configLister) List(selector labels.Selector) (ret []*v2beta1.Config, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v2beta1.Config)) + }) + return ret, err +} + +// Get retrieves the Config from the index for a given name. +func (s *configLister) Get(name string) (*v2beta1.Config, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v2beta1.Resource("config"), name) + } + return obj.(*v2beta1.Config), nil +} diff --git a/pkg/client/listers/notification/v2beta1/expansion_generated.go b/pkg/client/listers/notification/v2beta1/expansion_generated.go new file mode 100644 index 0000000000000000000000000000000000000000..76a2eee04439a55069fd90c268c10f608ca61c60 --- /dev/null +++ b/pkg/client/listers/notification/v2beta1/expansion_generated.go @@ -0,0 +1,27 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v2beta1 + +// ConfigListerExpansion allows custom methods to be added to +// ConfigLister. +type ConfigListerExpansion interface{} + +// ReceiverListerExpansion allows custom methods to be added to +// ReceiverLister. +type ReceiverListerExpansion interface{} diff --git a/pkg/client/listers/notification/v2beta1/receiver.go b/pkg/client/listers/notification/v2beta1/receiver.go new file mode 100644 index 0000000000000000000000000000000000000000..14d2498eacdb85180b24d8c49ca85e2dd3cfaa4f --- /dev/null +++ b/pkg/client/listers/notification/v2beta1/receiver.go @@ -0,0 +1,65 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v2beta1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" +) + +// ReceiverLister helps list Receivers. +type ReceiverLister interface { + // List lists all Receivers in the indexer. + List(selector labels.Selector) (ret []*v2beta1.Receiver, err error) + // Get retrieves the Receiver from the index for a given name. + Get(name string) (*v2beta1.Receiver, error) + ReceiverListerExpansion +} + +// receiverLister implements the ReceiverLister interface. +type receiverLister struct { + indexer cache.Indexer +} + +// NewReceiverLister returns a new ReceiverLister. +func NewReceiverLister(indexer cache.Indexer) ReceiverLister { + return &receiverLister{indexer: indexer} +} + +// List lists all Receivers in the indexer. +func (s *receiverLister) List(selector labels.Selector) (ret []*v2beta1.Receiver, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v2beta1.Receiver)) + }) + return ret, err +} + +// Get retrieves the Receiver from the index for a given name. +func (s *receiverLister) Get(name string) (*v2beta1.Receiver, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v2beta1.Resource("receiver"), name) + } + return obj.(*v2beta1.Receiver), nil +} diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index ea0de7d594a32129328eb5517a441d192025f574..456782591414c3e7074bba9c9a66f78377565e1d 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -125,6 +125,9 @@ const ( ApplicationName = "app.kubernetes.io/name" ApplicationVersion = "app.kubernetes.io/version" AlertingTag = "Alerting" + + NotificationTag = "Notification" + NotificationSecretNamespace = "kubesphere-monitoring-federated" ) var ( diff --git a/pkg/controller/notification/notification_controller.go b/pkg/controller/notification/notification_controller.go new file mode 100644 index 0000000000000000000000000000000000000000..3877fa9996501f99b9fc6c4e3719b4c677798e16 --- /dev/null +++ b/pkg/controller/notification/notification_controller.go @@ -0,0 +1,513 @@ +/* +Copyright 2019 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package notification + +import ( + "context" + "fmt" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" + toolscache "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" + "kubesphere.io/kubesphere/pkg/apis/types/v1beta1" + "kubesphere.io/kubesphere/pkg/constants" + "reflect" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "time" +) + +const ( + // SuccessSynced is used as part of the Event 'reason' when a Foo is synced + successSynced = "Synced" + controllerName = "notification-controller" +) + +type Controller struct { + client.Client + ksCache cache.Cache + reconciledObjs []runtime.Object + informerSynced []toolscache.InformerSynced + // workqueue is a rate limited work queue. This is used to queue work to be + // processed instead of performing it as soon as a change happens. This + // means we can ensure we only process a fixed amount of resources at a + // time, and makes it easy to ensure we are never processing the same item + // simultaneously in two different workers. + workqueue workqueue.RateLimitingInterface + // recorder is an event recorder for recording Event resources to the + // Kubernetes API. + recorder record.EventRecorder +} + +func NewController(k8sClient kubernetes.Interface, ksClient client.Client, ksCache cache.Cache) (*Controller, error) { + // Create event broadcaster + // Add sample-controller types to the default Kubernetes Scheme so Events can be + // logged for sample-controller types. + + klog.V(4).Info("Creating event broadcaster") + eventBroadcaster := record.NewBroadcaster() + eventBroadcaster.StartLogging(klog.Infof) + eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: k8sClient.CoreV1().Events("")}) + recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName}) + ctl := &Controller{ + Client: ksClient, + ksCache: ksCache, + workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Notification"), + recorder: recorder, + } + klog.Info("Setting up event handlers") + + if err := ctl.setEventHandlers(); err != nil { + return nil, err + } + + return ctl, nil +} + +func (c *Controller) setEventHandlers() error { + + if c.reconciledObjs != nil && len(c.reconciledObjs) > 0 { + c.reconciledObjs = c.reconciledObjs[:0] + } + c.reconciledObjs = append(c.reconciledObjs, &v2beta1.Config{}) + c.reconciledObjs = append(c.reconciledObjs, &v2beta1.Receiver{}) + c.reconciledObjs = append(c.reconciledObjs, &corev1.Secret{}) + + if c.informerSynced != nil && len(c.informerSynced) > 0 { + c.informerSynced = c.informerSynced[:0] + } + + for _, obj := range c.reconciledObjs { + if informer, err := c.ksCache.GetInformer(context.Background(), obj); err != nil { + klog.Errorf("get %s informer error, %v", obj.GetObjectKind().GroupVersionKind().String(), err) + return err + } else { + informer.AddEventHandler(toolscache.ResourceEventHandlerFuncs{ + AddFunc: c.enqueue, + UpdateFunc: func(old, new interface{}) { + c.enqueue(new) + }, + DeleteFunc: c.enqueue, + }) + c.informerSynced = append(c.informerSynced, informer.HasSynced) + } + } + + return nil +} + +func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error { + defer utilruntime.HandleCrash() + defer c.workqueue.ShutDown() + + // Start the informer factories to begin populating the informer caches + klog.Info("Starting Notification controller") + + // Wait for the caches to be synced before starting workers + klog.Info("Waiting for informer caches to sync") + + if ok := toolscache.WaitForCacheSync(stopCh, c.informerSynced...); !ok { + return fmt.Errorf("failed to wait for caches to sync") + } + + klog.Info("Starting workers") + // Launch two workers to process Foo resources + for i := 0; i < threadiness; i++ { + go wait.Until(c.runWorker, time.Second, stopCh) + } + + klog.Info("Started workers") + <-stopCh + klog.Info("Shutting down workers") + return nil +} + +func (c *Controller) enqueue(obj interface{}) { + c.workqueue.Add(obj) +} + +func (c *Controller) runWorker() { + for c.processNextWorkItem() { + } +} + +func (c *Controller) processNextWorkItem() bool { + obj, shutdown := c.workqueue.Get() + + if shutdown { + return false + } + + // We wrap this block in a func so we can defer c.workqueue.Done. + err := func(obj interface{}) error { + // We call Done here so the workqueue knows we have finished + // processing this item. We also must remember to call Forget if we + // do not want this work item being re-queued. For example, we do + // not call Forget if a transient error occurs, instead the item is + // put back on the workqueue and attempted again after a back-off + // period. + defer c.workqueue.Done(obj) + + // Run the reconcile, passing it the namespace/name string of the + // Foo resource to be synced. + if err := c.reconcile(obj); err != nil { + // Put the item back on the workqueue to handle any transient errors. + c.workqueue.AddRateLimited(obj) + } + // Finally, if no error occurs we Forget this item so it does not + // get queued again until another change happens. + c.workqueue.Forget(obj) + return nil + }(obj) + + if err != nil { + utilruntime.HandleError(err) + return true + } + + return true +} + +// syncHandler compares the actual state with the desired, and attempts to +// converge the two. It then updates the Status block of the Foo resource +// with the current status of the resource. +func (c *Controller) reconcile(obj interface{}) error { + + runtimeObj, ok := obj.(runtime.Object) + if !ok { + utilruntime.HandleError(fmt.Errorf("object does not implement the Object interfaces")) + return nil + } + runtimeObj = runtimeObj.DeepCopyObject() + + accessor, err := meta.Accessor(runtimeObj) + if err != nil { + utilruntime.HandleError(fmt.Errorf("object does not implement the Object interfaces")) + return nil + } + + // Only reconcile the secret which created by notification manager. + if secret, ok := obj.(*corev1.Secret); ok { + if secret.Namespace != constants.NotificationSecretNamespace { + klog.V(8).Infof("No need to reconcile secret %s/%s", accessor.GetNamespace(), accessor.GetName()) + return nil + } + + if err := c.ensureNotificationNamespaceExist(); err != nil { + return err + } + } + + name := accessor.GetName() + kind := runtimeObj.GetObjectKind().GroupVersionKind().String() + err = c.Get(context.Background(), client.ObjectKey{Name: accessor.GetName(), Namespace: accessor.GetNamespace()}, runtimeObj) + if err != nil { + // The user may no longer exist, in which case we stop + // processing. + if errors.IsNotFound(err) { + utilruntime.HandleError(fmt.Errorf("obj '%s, %s' in work queue no longer exists", kind, name)) + c.recorder.Event(runtimeObj, corev1.EventTypeNormal, successSynced, fmt.Sprintf("%s synced successfully", kind)) + klog.Infof("Successfully synced %s:%s", kind, name) + return nil + } + klog.Error(err) + return err + } + + if err = c.multiClusterSync(context.Background(), runtimeObj); err != nil { + return err + } + + c.recorder.Event(runtimeObj, corev1.EventTypeNormal, successSynced, fmt.Sprintf("%s synced successfully", kind)) + klog.Infof("Successfully synced %s:%s", kind, name) + return nil +} + +func (c *Controller) Start(stopCh <-chan struct{}) error { + return c.Run(4, stopCh) +} + +func (c *Controller) multiClusterSync(ctx context.Context, obj runtime.Object) error { + + if err := c.ensureNotControlledByKubefed(ctx, obj); err != nil { + klog.Error(err) + return err + } + + switch obj.(type) { + case *v2beta1.Config: + return c.syncFederatedConfig(obj.(*v2beta1.Config)) + case *v2beta1.Receiver: + return c.syncFederatedReceiver(obj.(*v2beta1.Receiver)) + case *corev1.Secret: + return c.syncFederatedSecret(obj.(*corev1.Secret)) + default: + klog.Errorf("unknown type for notification, %v", obj) + return nil + } +} + +func (c *Controller) syncFederatedConfig(obj *v2beta1.Config) error { + + fedObj := &v1beta1.FederatedNotificationConfig{} + err := c.Get(context.Background(), client.ObjectKey{Name: obj.Name}, fedObj) + if err != nil { + if errors.IsNotFound(err) { + fedObj = &v1beta1.FederatedNotificationConfig{ + TypeMeta: metav1.TypeMeta{ + Kind: v1beta1.FederatedNotificationConfigKind, + APIVersion: v1beta1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: obj.Name, + }, + Spec: v1beta1.FederatedNotificationConfigSpec{ + Template: v1beta1.NotificationConfigTemplate{ + ObjectMeta: metav1.ObjectMeta{ + Labels: obj.Labels, + }, + Spec: obj.Spec, + }, + Placement: v1beta1.GenericPlacementFields{ + ClusterSelector: &metav1.LabelSelector{}, + }, + }, + } + + err := controllerutil.SetControllerReference(obj, fedObj, scheme.Scheme) + if err != nil { + return err + } + if err := c.Create(context.Background(), fedObj); err != nil { + klog.Errorf("create '%s:%s' failed, %s", fedObj.GetObjectKind().GroupVersionKind().String(), obj.Name, err) + return err + } + + return nil + } + klog.Error(err) + return err + } + + if !reflect.DeepEqual(fedObj.Spec.Template.Labels, obj.Labels) || !reflect.DeepEqual(fedObj.Spec.Template.Spec, obj.Spec) { + + fedObj.Spec.Template.Spec = obj.Spec + fedObj.Spec.Template.Labels = obj.Labels + + if err := c.Update(context.Background(), fedObj); err != nil { + klog.Errorf("update '%s:%s' failed, %s", fedObj.GetObjectKind().GroupVersionKind().String(), obj.Name, err) + return err + } + } + + return nil +} + +func (c *Controller) syncFederatedReceiver(obj *v2beta1.Receiver) error { + + fedObj := &v1beta1.FederatedNotificationReceiver{} + err := c.Get(context.Background(), client.ObjectKey{Name: obj.Name}, fedObj) + if err != nil { + if errors.IsNotFound(err) { + fedObj = &v1beta1.FederatedNotificationReceiver{ + TypeMeta: metav1.TypeMeta{ + Kind: v1beta1.FederatedNotificationReceiverKind, + APIVersion: v1beta1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: obj.Name, + }, + Spec: v1beta1.FederatedNotificationReceiverSpec{ + Template: v1beta1.NotificationReceiverTemplate{ + ObjectMeta: metav1.ObjectMeta{ + Labels: obj.Labels, + }, + Spec: obj.Spec, + }, + Placement: v1beta1.GenericPlacementFields{ + ClusterSelector: &metav1.LabelSelector{}, + }, + }, + } + + err := controllerutil.SetControllerReference(obj, fedObj, scheme.Scheme) + if err != nil { + return err + } + if err := c.Create(context.Background(), fedObj); err != nil { + klog.Errorf("create '%s:%s' failed, %s", fedObj.GetObjectKind().GroupVersionKind().String(), obj.Name, err) + return err + } + + return nil + } + klog.Error(err) + return err + } + + if !reflect.DeepEqual(fedObj.Spec.Template.Labels, obj.Labels) || !reflect.DeepEqual(fedObj.Spec.Template.Spec, obj.Spec) { + + fedObj.Spec.Template.Spec = obj.Spec + fedObj.Spec.Template.Labels = obj.Labels + + if err := c.Update(context.Background(), fedObj); err != nil { + klog.Errorf("update '%s:%s' failed, %s", fedObj.GetObjectKind().GroupVersionKind().String(), obj.Name, err) + return err + } + } + + return nil +} + +func (c *Controller) syncFederatedSecret(obj *corev1.Secret) error { + + fedObj := &v1beta1.FederatedSecret{} + err := c.Get(context.Background(), client.ObjectKey{Name: obj.Name, Namespace: obj.Namespace}, fedObj) + if err != nil { + if errors.IsNotFound(err) { + fedObj = &v1beta1.FederatedSecret{ + TypeMeta: metav1.TypeMeta{ + Kind: v1beta1.FederatedSecretKind, + APIVersion: v1beta1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: obj.Name, + Namespace: obj.Namespace, + }, + Spec: v1beta1.FederatedSecretSpec{ + Template: v1beta1.SecretTemplate{ + Data: obj.Data, + StringData: obj.StringData, + Type: obj.Type, + }, + Placement: v1beta1.GenericPlacementFields{ + ClusterSelector: &metav1.LabelSelector{}, + }, + }, + } + + err := controllerutil.SetControllerReference(obj, fedObj, scheme.Scheme) + if err != nil { + return err + } + if err := c.Create(context.Background(), fedObj); err != nil { + klog.Errorf("create '%s:%s' failed, %s", fedObj.GetObjectKind().GroupVersionKind().String(), obj.Name, err) + return err + } + + return nil + } + klog.Error(err) + return err + } + + if !reflect.DeepEqual(fedObj.Spec.Template.Data, obj.Data) || + !reflect.DeepEqual(fedObj.Spec.Template.StringData, obj.StringData) || + !reflect.DeepEqual(fedObj.Spec.Template.Type, obj.Type) { + + fedObj.Spec.Template.Data = obj.Data + fedObj.Spec.Template.StringData = obj.StringData + fedObj.Spec.Template.Type = obj.Type + + if err := c.Update(context.Background(), fedObj); err != nil { + klog.Errorf("update '%s:%s' failed, %s", fedObj.GetObjectKind().GroupVersionKind().String(), obj.Name, err) + return err + } + } + + return nil +} + +func (c *Controller) ensureNotificationNamespaceExist() error { + + ns := corev1.Namespace{} + if err := c.Get(context.Background(), client.ObjectKey{Name: constants.NotificationSecretNamespace}, &ns); err != nil { + return err + } + + fedNs := v1beta1.FederatedNamespace{} + if err := c.Get(context.Background(), client.ObjectKey{Name: constants.NotificationSecretNamespace, Namespace: constants.NotificationSecretNamespace}, &fedNs); err != nil { + if errors.IsAlreadyExists(err) { + return nil + } + + if errors.IsNotFound(err) { + fedNs = v1beta1.FederatedNamespace{ + TypeMeta: metav1.TypeMeta{ + Kind: v1beta1.FederatedNamespaceKind, + APIVersion: v1beta1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: constants.NotificationSecretNamespace, + Namespace: constants.NotificationSecretNamespace, + }, + Spec: v1beta1.FederatedNamespaceSpec{ + Placement: v1beta1.GenericPlacementFields{ + ClusterSelector: &metav1.LabelSelector{}, + }, + }, + } + + if err := controllerutil.SetControllerReference(&ns, &fedNs, scheme.Scheme); err != nil { + return err + } + + return c.Create(context.Background(), &fedNs) + } + + return err + } + + return nil + +} + +func (c *Controller) ensureNotControlledByKubefed(ctx context.Context, obj runtime.Object) error { + + accessor, err := meta.Accessor(obj) + if err != nil { + klog.Error(err) + return nil + } + + labels := accessor.GetLabels() + if labels == nil { + labels = make(map[string]string, 0) + } + + if labels[constants.KubefedManagedLabel] != "false" { + labels[constants.KubefedManagedLabel] = "false" + accessor.SetLabels(labels) + err := c.Update(ctx, accessor.(runtime.Object)) + if err != nil { + klog.Error(err) + } + } + return nil +} diff --git a/pkg/controller/notification/notification_controller_suite_test.go b/pkg/controller/notification/notification_controller_suite_test.go new file mode 100644 index 0000000000000000000000000000000000000000..cee2730cd7f16b5318f86a69bbfc88bf9df162a0 --- /dev/null +++ b/pkg/controller/notification/notification_controller_suite_test.go @@ -0,0 +1,83 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package notification + +import ( + "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/kubernetes/scheme" + "kubesphere.io/kubesphere/pkg/apis" + "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/envtest/printer" +) + +func TestSource(t *testing.T) { + RegisterFailHandler(Fail) + suiteName := "Cache Suite" + RunSpecsWithDefaultAndCustomReporters(t, suiteName, []Reporter{printer.NewlineReporter{}, printer.NewProwReporter(suiteName)}) +} + +var testenv *envtest.Environment +var cfg *rest.Config +var k8sManager ctrl.Manager + +var _ = BeforeSuite(func(done Done) { + + testenv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")}, + } + + var err error + cfg, err = testenv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + err = v2beta1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + err = apis.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + MetricsBindAddress: "0", + }) + Expect(err).ToNot(HaveOccurred()) + + r, err := NewController(fake.NewSimpleClientset(), k8sManager.GetClient(), k8sManager.GetCache()) + Expect(err).ToNot(HaveOccurred()) + err = k8sManager.Add(r) + Expect(err).ToNot(HaveOccurred()) + + go func() { + err = k8sManager.Start(ctrl.SetupSignalHandler()) + Expect(err).ToNot(HaveOccurred()) + }() + + close(done) +}, 60) + +var _ = AfterSuite(func() { + Expect(testenv.Stop()).To(Succeed()) +}) diff --git a/pkg/controller/notification/notification_controller_test.go b/pkg/controller/notification/notification_controller_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d3d3171140a740f43a4a2f5d7041f7937d12a93d --- /dev/null +++ b/pkg/controller/notification/notification_controller_test.go @@ -0,0 +1,201 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package notification + +import ( + "context" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" + "kubesphere.io/kubesphere/pkg/apis/types/v1beta1" + "kubesphere.io/kubesphere/pkg/constants" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + "time" +) + +var ( + _ = Describe("Secret", func() { + + const timeout = time.Second * 30 + const interval = time.Second * 1 + + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.NotificationSecretNamespace, + }, + } + + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: constants.NotificationSecretNamespace, + }, + } + + var ( + cl client.Client + ksCache cache.Cache + informerCacheCtx context.Context + informerCacheCancel context.CancelFunc + ) + + BeforeEach(func() { + var err error + cl, err = client.New(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + + ksCache, err = cache.New(cfg, cache.Options{}) + Expect(err).NotTo(HaveOccurred()) + + informerCacheCtx, informerCacheCancel = context.WithCancel(context.Background()) + go func(ctx context.Context) { + defer GinkgoRecover() + Expect(ksCache.Start(ctx.Done())).To(Succeed()) + }(informerCacheCtx) + Expect(ksCache.WaitForCacheSync(informerCacheCtx.Done())).To(BeTrue()) + + Eventually(func() bool { + err = cl.Create(informerCacheCtx, namespace) + if err == nil || errors.IsAlreadyExists(err) { + return true + } + return false + }, timeout, interval).Should(BeTrue()) + }) + + AfterEach(func() { + By("cleaning up") + informerCacheCancel() + }) + + // Add Tests for OpenAPI validation (or additonal CRD features) specified in + // your API definition. + // Avoid adding tests for vanilla CRUD operations because they would + // test Kubernetes API server, which isn't the goal here. + Context("Notification Controller", func() { + It("Should create successfully", func() { + + // Create a secret + Expect(cl.Create(context.Background(), secret)).Should(Succeed()) + time.Sleep(time.Second) + + fedSecret := &v1beta1.FederatedSecret{} + By("Expecting to create federated secret successfully") + Eventually(func() bool { + err := ksCache.Get(context.Background(), client.ObjectKey{Name: secret.Name, Namespace: constants.NotificationSecretNamespace}, fedSecret) + Expect(err).Should(Succeed()) + return !fedSecret.CreationTimestamp.IsZero() + }, timeout, interval).Should(BeTrue()) + + err := ksCache.Get(context.Background(), client.ObjectKey{Name: secret.Name, Namespace: constants.NotificationSecretNamespace}, secret) + Expect(err).Should(Succeed()) + secret.StringData = map[string]string{"foo": "bar"} + Expect(cl.Update(context.Background(), secret)).Should(Succeed()) + time.Sleep(time.Second) + + By("Expecting to update federated secret successfully") + Eventually(func() bool { + err := ksCache.Get(context.Background(), client.ObjectKey{Name: secret.Name, Namespace: constants.NotificationSecretNamespace}, fedSecret) + Expect(err).Should(Succeed()) + return string(fedSecret.Spec.Template.Data["foo"]) == "bar" + }, timeout, interval).Should(BeTrue()) + }) + }) + }) + + _ = Describe("Notification", func() { + + const timeout = time.Second * 30 + const interval = time.Second * 1 + + obj := &v2beta1.Config{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: constants.NotificationSecretNamespace, + Labels: map[string]string{ + "type": "default", + }, + }, + } + + var ( + cl client.Client + ksCache cache.Cache + informerCacheCtx context.Context + informerCacheCancel context.CancelFunc + ) + + BeforeEach(func() { + var err error + cl, err = client.New(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + + ksCache, err = cache.New(cfg, cache.Options{}) + Expect(err).NotTo(HaveOccurred()) + + informerCacheCtx, informerCacheCancel = context.WithCancel(context.Background()) + go func(ctx context.Context) { + defer GinkgoRecover() + Expect(ksCache.Start(ctx.Done())).To(Succeed()) + }(informerCacheCtx) + Expect(ksCache.WaitForCacheSync(informerCacheCtx.Done())).To(BeTrue()) + }) + + AfterEach(func() { + By("cleaning up") + informerCacheCancel() + }) + + // Add Tests for OpenAPI validation (or additonal CRD features) specified in + // your API definition. + // Avoid adding tests for vanilla CRUD operations because they would + // test Kubernetes API server, which isn't the goal here. + Context("Notification Controller", func() { + It("Should create successfully", func() { + + // Create a bject + Expect(cl.Create(context.Background(), obj)).Should(Succeed()) + time.Sleep(time.Second) + + fedObj := &v1beta1.FederatedNotificationConfig{} + By("Expecting to create federated object successfully") + Eventually(func() bool { + err := ksCache.Get(context.Background(), client.ObjectKey{Name: obj.Name}, fedObj) + Expect(err).Should(Succeed()) + return !fedObj.CreationTimestamp.IsZero() + }, timeout, interval).Should(BeTrue()) + + err := ksCache.Get(context.Background(), client.ObjectKey{Name: obj.Name}, obj) + Expect(err).Should(Succeed()) + obj.Labels = map[string]string{"foo": "bar"} + Expect(cl.Update(context.Background(), obj)).Should(Succeed()) + time.Sleep(time.Second) + + By("Expecting to update federated object successfully") + Eventually(func() bool { + err := ksCache.Get(context.Background(), client.ObjectKey{Name: obj.Name}, fedObj) + Expect(err).Should(Succeed()) + return fedObj.Spec.Template.Labels["foo"] == "bar" + }, timeout, interval).Should(BeTrue()) + }) + }) + }) +) diff --git a/pkg/kapis/notification/v2beta1/handler.go b/pkg/kapis/notification/v2beta1/handler.go new file mode 100644 index 0000000000000000000000000000000000000000..39ed02e5a4a1b1887ac00f2707a00b745999958e --- /dev/null +++ b/pkg/kapis/notification/v2beta1/handler.go @@ -0,0 +1,149 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2beta1 + +import ( + "github.com/emicklei/go-restful" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/client-go/kubernetes" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models/notification" + servererr "kubesphere.io/kubesphere/pkg/server/errors" +) + +type handler struct { + operator notification.Operator +} + +func newNotificationHandler( + informers informers.InformerFactory, + k8sClient kubernetes.Interface, + ksClient kubesphere.Interface) *handler { + + return &handler{ + operator: notification.NewOperator(informers, k8sClient, ksClient), + } +} + +func (h *handler) ListResource(req *restful.Request, resp *restful.Response) { + + user := req.PathParameter("user") + resource := req.PathParameter("resources") + subresource := req.QueryParameter("type") + q := query.ParseQueryParameter(req) + + if !h.operator.IsKnownResource(resource, subresource) { + api.HandleBadRequest(resp, req, servererr.New("unknown resource type %s/%s", resource, subresource)) + return + } + + objs, err := h.operator.List(user, resource, subresource, q) + handleResponse(req, resp, objs, err) +} + +func (h *handler) GetResource(req *restful.Request, resp *restful.Response) { + + user := req.PathParameter("user") + resource := req.PathParameter("resources") + name := req.PathParameter("name") + subresource := req.QueryParameter("type") + + if !h.operator.IsKnownResource(resource, subresource) { + api.HandleBadRequest(resp, req, servererr.New("unknown resource type %s/%s", resource, subresource)) + return + } + + obj, err := h.operator.Get(user, resource, name, subresource) + handleResponse(req, resp, obj, err) +} + +func (h *handler) CreateResource(req *restful.Request, resp *restful.Response) { + + user := req.PathParameter("user") + resource := req.PathParameter("resources") + + if !h.operator.IsKnownResource(resource, "") { + api.HandleBadRequest(resp, req, servererr.New("unknown resource type %s", resource)) + return + } + + obj := h.operator.GetObject(resource) + if err := req.ReadEntity(obj); err != nil { + api.HandleBadRequest(resp, req, err) + return + } + + created, err := h.operator.Create(user, resource, obj) + handleResponse(req, resp, created, err) +} + +func (h *handler) UpdateResource(req *restful.Request, resp *restful.Response) { + + user := req.PathParameter("user") + resource := req.PathParameter("resources") + name := req.PathParameter("name") + + if !h.operator.IsKnownResource(resource, "") { + api.HandleBadRequest(resp, req, servererr.New("unknown resource type %s", resource)) + return + } + + obj := h.operator.GetObject(resource) + if err := req.ReadEntity(obj); err != nil { + api.HandleBadRequest(resp, req, err) + return + } + + updated, err := h.operator.Update(user, resource, name, obj) + handleResponse(req, resp, updated, err) +} + +func (h *handler) DeleteResource(req *restful.Request, resp *restful.Response) { + + user := req.PathParameter("user") + resource := req.PathParameter("resources") + name := req.PathParameter("name") + + if !h.operator.IsKnownResource(resource, "") { + api.HandleBadRequest(resp, req, servererr.New("unknown resource type %s", resource)) + return + } + + handleResponse(req, resp, servererr.None, h.operator.Delete(user, resource, name)) +} + +func handleResponse(req *restful.Request, resp *restful.Response, obj interface{}, err error) { + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(resp, req, err) + return + } else if errors.IsConflict(err) { + api.HandleConflict(resp, req, err) + return + } + api.HandleBadRequest(resp, req, err) + return + } + + _ = resp.WriteEntity(obj) +} diff --git a/pkg/kapis/notification/v2beta1/register.go b/pkg/kapis/notification/v2beta1/register.go new file mode 100644 index 0000000000000000000000000000000000000000..a9d4a65a282d29fbe5492d3f482c77d3affe780f --- /dev/null +++ b/pkg/kapis/notification/v2beta1/register.go @@ -0,0 +1,153 @@ +/* + + Copyright 2020 The KubeSphere Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +package v2beta1 + +import ( + "github.com/emicklei/go-restful" + openapi "github.com/emicklei/go-restful-openapi" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/apiserver/runtime" + kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/server/errors" + "net/http" +) + +const ( + GroupName = "notification.kubesphere.io" + KeyOpenAPITags = openapi.KeyOpenAPITags +) + +var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v2beta1"} + +func AddToContainer( + container *restful.Container, + informers informers.InformerFactory, + k8sClient kubernetes.Interface, + ksClient kubesphere.Interface) error { + + ws := runtime.NewWebService(GroupVersion) + h := newNotificationHandler(informers, k8sClient, ksClient) + + // apis for global notification config, receiver, and secret + ws.Route(ws.GET("/{resources}"). + To(h.ListResource). + Doc("list the notification configs or receivers"). + Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). + Param(ws.PathParameter("resources", "known values include configs, receivers, secrets")). + Param(ws.QueryParameter(query.ParameterName, "name used for filtering").Required(false)). + Param(ws.QueryParameter(query.ParameterLabelSelector, "label selector used for filtering").Required(false)). + Param(ws.QueryParameter("type", "config or receiver type, known values include dingtalk, email, slack, webhook, wechat").Required(false)). + Param(ws.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d").DefaultValue("page=1")). + Param(ws.QueryParameter(query.ParameterLimit, "limit").Required(false)). + Param(ws.QueryParameter(query.ParameterAscending, "sort parameters, e.g. ascending=false").Required(false).DefaultValue("ascending=false")). + Param(ws.QueryParameter(query.ParameterOrderBy, "sort parameters, e.g. orderBy=createTime")). + Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{}})) + + ws.Route(ws.GET("/{resources}/{name}"). + To(h.GetResource). + Doc("get the specified notification config or receiver"). + Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). + Param(ws.PathParameter("resources", "known values include configs, receivers, secrets")). + Param(ws.PathParameter(query.ParameterName, "the name of the resource")). + Param(ws.QueryParameter("type", "config or receiver type, known values include dingtalk, email, slack, webhook, wechat").Required(false)). + Returns(http.StatusOK, api.StatusOK, nil)) + + ws.Route(ws.POST("/{resources}"). + To(h.CreateResource). + Doc("create a notification config or receiver"). + Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). + Param(ws.PathParameter("resources", "known values include configs, receivers, secrets")). + Returns(http.StatusOK, api.StatusOK, nil)) + + ws.Route(ws.PUT("/{resources}/{name}"). + To(h.UpdateResource). + Doc("update the specified notification config or receiver"). + Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). + Param(ws.PathParameter("resources", "known values include configs, receivers, secrets")). + Param(ws.PathParameter(query.ParameterName, "the name of the resource")). + Returns(http.StatusOK, api.StatusOK, nil)) + + ws.Route(ws.DELETE("/{resources}/{name}"). + To(h.DeleteResource). + Doc("delete the specified notification config or receiver"). + Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). + Param(ws.PathParameter("resources", "known values include configs, receivers, secrets")). + Param(ws.PathParameter(query.ParameterName, "the name of the resource")). + Returns(http.StatusOK, api.StatusOK, errors.None)) + + // apis for tenant notification config and receiver + ws.Route(ws.GET("/users/{user}/{resources}"). + To(h.ListResource). + Doc("list the notification configs or receivers"). + Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). + Param(ws.PathParameter("user", "user name")). + Param(ws.PathParameter("resources", "known values include configs, receivers, secrets")). + Param(ws.QueryParameter(query.ParameterName, "name used for filtering").Required(false)). + Param(ws.QueryParameter(query.ParameterLabelSelector, "label selector used for filtering").Required(false)). + Param(ws.QueryParameter("type", "config or receiver type, known values include dingtalk, email, slack, webhook, wechat").Required(false)). + Param(ws.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d").DefaultValue("page=1")). + Param(ws.QueryParameter(query.ParameterLimit, "limit").Required(false)). + Param(ws.QueryParameter(query.ParameterAscending, "sort parameters, e.g. ascending=false").Required(false).DefaultValue("ascending=false")). + Param(ws.QueryParameter(query.ParameterOrderBy, "sort parameters, e.g. orderBy=createTime")). + Returns(http.StatusOK, api.StatusOK, api.ListResult{Items: []interface{}{}})) + + ws.Route(ws.GET("/users/{user}/{resources}/{name}"). + To(h.GetResource). + Doc("get the specified notification config or receiver"). + Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). + Param(ws.PathParameter("user", "user name")). + Param(ws.PathParameter("resources", "known values include configs, receivers, secrets")). + Param(ws.PathParameter(query.ParameterName, "the name of the resource")). + Param(ws.QueryParameter("type", "config or receiver type, known values include dingtalk, email, slack, webhook, wechat").Required(false)). + Returns(http.StatusOK, api.StatusOK, nil)) + + ws.Route(ws.POST("/users/{user}/{resources}"). + To(h.CreateResource). + Doc("create the specified notification config or receiver"). + Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). + Param(ws.PathParameter("user", "user name")). + Param(ws.PathParameter("resources", "known values include configs, receivers, secrets")). + Returns(http.StatusOK, api.StatusOK, nil)) + + ws.Route(ws.PUT("/users/{user}/{resources}/{name}"). + To(h.UpdateResource). + Doc("update the specified notification config or receiver"). + Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). + Param(ws.PathParameter("user", "user name")). + Param(ws.PathParameter("resources", "known values include configs, receivers, secrets")). + Param(ws.PathParameter(query.ParameterName, "the name of the resource")). + Returns(http.StatusOK, api.StatusOK, nil)) + + ws.Route(ws.DELETE("/users/{user}/{resources}/{name}"). + To(h.DeleteResource). + Doc("delete the specified notification config or receiver"). + Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). + Param(ws.PathParameter("user", "user name")). + Param(ws.PathParameter("resources", "known values include configs, receivers, secrets")). + Param(ws.PathParameter(query.ParameterName, "the name of the resource")). + Returns(http.StatusOK, api.StatusOK, errors.None)) + + container.Add(ws) + return nil +} diff --git a/pkg/models/notification/notification.go b/pkg/models/notification/notification.go new file mode 100644 index 0000000000000000000000000000000000000000..69bdc32287100c5a70565669ad0d37529e0ef3b9 --- /dev/null +++ b/pkg/models/notification/notification.go @@ -0,0 +1,358 @@ +package notification + +import ( + "context" + "fmt" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" + "reflect" +) + +type Operator interface { + List(user, resource, subresource string, query *query.Query) (*api.ListResult, error) + Get(user, resource, name, subresource string) (runtime.Object, error) + Create(user, resource string, obj runtime.Object) (runtime.Object, error) + Delete(user, resource, name string) error + Update(user, resource, name string, obj runtime.Object) (runtime.Object, error) + + GetObject(resource string) runtime.Object + IsKnownResource(resource, subresource string) bool +} + +type operator struct { + k8sClient kubernetes.Interface + ksClient kubesphere.Interface + informers informers.InformerFactory + resourceGetter *resource.ResourceGetter +} + +func NewOperator( + informers informers.InformerFactory, + k8sClient kubernetes.Interface, + ksClient kubesphere.Interface) Operator { + + return &operator{ + informers: informers, + k8sClient: k8sClient, + ksClient: ksClient, + resourceGetter: resource.NewResourceGetter(informers, nil), + } +} + +// List objects. Only global objects will be returned if the user is nil. +// If the user is not nil, only tenant objects whose tenant label matches the user will be returned. +func (o *operator) List(user, resource, subresource string, q *query.Query) (*api.ListResult, error) { + + if len(q.LabelSelector) > 0 { + q.LabelSelector = q.LabelSelector + "," + } + + filter := "" + // If user is nil, it will list all global object. + if user == "" { + if isConfig(o.GetObject(resource)) { + filter = "type=default" + } else { + filter = "type=global" + } + } else { + // If the user is not nil, only return the object belong to this user. + filter = "type=tenant,user=" + user + } + + q.LabelSelector = q.LabelSelector + filter + + res, err := o.resourceGetter.List(resource, constants.NotificationSecretNamespace, q) + if err != nil { + return nil, err + } + + if subresource == "" || resource == "secrets" { + return res, nil + } + + results := &api.ListResult{} + for _, item := range res.Items { + obj := clean(item, resource, subresource) + if obj != nil { + results.Items = append(results.Items, obj) + } + } + results.TotalItems = len(results.Items) + + return results, nil +} + +// Get the specified object, if you want to get a global object, the user must be nil. +// If you want to get a tenant object, the user must equal to the tenant specified in labels of the object. +func (o *operator) Get(user, resource, name, subresource string) (runtime.Object, error) { + obj, err := o.resourceGetter.Get(resource, constants.NotificationSecretNamespace, name) + if err != nil { + return nil, err + } + + if err := authorizer(user, obj); err != nil { + return nil, err + } + + if subresource == "" || resource == "secrets" { + return obj, nil + } + + res := clean(obj, resource, subresource) + if res == nil { + return nil, errors.NewNotFound(v2beta1.Resource(obj.GetObjectKind().GroupVersionKind().GroupKind().Kind), name) + } + + return res, nil +} + +// Create an object. A global object will be created if the user is nil. +// A tenant object will be created if the user is not nil. +func (o *operator) Create(user, resource string, obj runtime.Object) (runtime.Object, error) { + + if err := appendLabel(user, obj); err != nil { + return nil, err + } + + switch resource { + case v2beta1.ResourcesPluralConfig: + return o.ksClient.NotificationV2beta1().Configs().Create(context.Background(), obj.(*v2beta1.Config), v1.CreateOptions{}) + case v2beta1.ResourcesPluralReceiver: + return o.ksClient.NotificationV2beta1().Receivers().Create(context.Background(), obj.(*v2beta1.Receiver), v1.CreateOptions{}) + case "secrets": + return o.k8sClient.CoreV1().Secrets(constants.NotificationSecretNamespace).Create(context.Background(), obj.(*corev1.Secret), v1.CreateOptions{}) + default: + return nil, errors.NewInternalError(nil) + } +} + +// Delete an object. A global object will be deleted if the user is nil. +// If the user is not nil, a tenant object whose tenant label matches the user will be deleted. +func (o *operator) Delete(user, resource, name string) error { + + if obj, err := o.Get(user, resource, name, ""); err != nil { + return err + } else { + if err := authorizer(user, obj); err != nil { + return err + } + } + + switch resource { + case v2beta1.ResourcesPluralConfig: + return o.ksClient.NotificationV2beta1().Configs().Delete(context.Background(), name, v1.DeleteOptions{}) + case v2beta1.ResourcesPluralReceiver: + return o.ksClient.NotificationV2beta1().Receivers().Delete(context.Background(), name, v1.DeleteOptions{}) + case "secrets": + return o.k8sClient.CoreV1().Secrets(constants.NotificationSecretNamespace).Delete(context.Background(), name, v1.DeleteOptions{}) + default: + return errors.NewInternalError(nil) + } +} + +// Update an object, only a global object will be updated if the user is nil. +// If the user is not nil, a tenant object whose tenant label matches the user will be updated. +func (o *operator) Update(user, resource, name string, obj runtime.Object) (runtime.Object, error) { + + if err := appendLabel(user, obj); err != nil { + return nil, err + } + + if old, err := o.Get(user, resource, name, ""); err != nil { + return nil, err + } else { + if err := authorizer(user, old); err != nil { + return nil, err + } + } + + switch resource { + case v2beta1.ResourcesPluralConfig: + return o.ksClient.NotificationV2beta1().Configs().Update(context.Background(), obj.(*v2beta1.Config), v1.UpdateOptions{}) + case v2beta1.ResourcesPluralReceiver: + return o.ksClient.NotificationV2beta1().Receivers().Update(context.Background(), obj.(*v2beta1.Receiver), v1.UpdateOptions{}) + case "secrets": + return o.k8sClient.CoreV1().Secrets(constants.NotificationSecretNamespace).Update(context.Background(), obj.(*corev1.Secret), v1.UpdateOptions{}) + default: + return nil, errors.NewInternalError(nil) + } +} + +func (o *operator) GetObject(resource string) runtime.Object { + + switch resource { + case v2beta1.ResourcesPluralConfig: + return &v2beta1.Config{} + case v2beta1.ResourcesPluralReceiver: + return &v2beta1.Receiver{} + case "secrets": + return &corev1.Secret{} + default: + return nil + } +} + +func (o *operator) IsKnownResource(resource, subresource string) bool { + + if obj := o.GetObject(resource); obj == nil { + return false + } + + // "" means get all types of the config or receiver. + if subresource != "dingtalk" && + subresource != "email" && + subresource != "slack" && + subresource != "webhook" && + subresource != "wechat" && + subresource != "" { + return false + } + + return true +} + +// Does the user has permission to access this object. +func authorizer(user string, obj runtime.Object) error { + // If the user is not nil, it must equal to the tenant specified in labels of the object. + if user != "" && !isOwner(user, obj) { + return errors.NewForbidden(v2beta1.Resource(obj.GetObjectKind().GroupVersionKind().GroupKind().Kind), "", + fmt.Errorf("user '%s' is not the owner of object", user)) + } + + // If the user is nil, the object must be a global object. + if user == "" && !isGlobal(obj) { + return errors.NewForbidden(v2beta1.Resource(obj.GetObjectKind().GroupVersionKind().GroupKind().Kind), "", + fmt.Errorf("object is not a global object")) + } + + return nil +} + +// Is the user equal to the tenant specified in the object labels. +func isOwner(user string, obj interface{}) bool { + + accessor, err := meta.Accessor(obj) + if err != nil { + klog.Errorln(err) + return false + } + + return accessor.GetLabels()["user"] == user +} + +func isConfig(obj runtime.Object) bool { + switch obj.(type) { + case *v2beta1.Config: + return true + default: + return false + } +} + +// Is the object is a global object. +func isGlobal(obj runtime.Object) bool { + accessor, err := meta.Accessor(obj) + if err != nil { + klog.Errorln(err) + return false + } + + if isConfig(obj) { + return accessor.GetLabels()["type"] == "default" + } else { + return accessor.GetLabels()["type"] == "global" + } +} + +func appendLabel(user string, obj runtime.Object) error { + + accessor, err := meta.Accessor(obj) + if err != nil { + klog.Errorln(err) + return err + } + + labels := accessor.GetLabels() + if labels == nil { + labels = make(map[string]string) + } + + if user == "" { + if isConfig(obj) { + labels["type"] = "default" + } else { + labels["type"] = "global" + } + } else { + labels["type"] = "tenant" + labels["user"] = user + } + + accessor.SetLabels(labels) + return nil +} + +func clean(obj interface{}, resource, subresource string) runtime.Object { + if resource == v2beta1.ResourcesPluralConfig { + config := obj.(*v2beta1.Config) + newConfig := config.DeepCopy() + newConfig.Spec = v2beta1.ConfigSpec{} + switch subresource { + case "dingtalk": + newConfig.Spec.DingTalk = config.Spec.DingTalk + case "email": + newConfig.Spec.Email = config.Spec.Email + case "slack": + newConfig.Spec.Slack = config.Spec.Slack + case "webhook": + newConfig.Spec.Webhook = config.Spec.Webhook + case "wechat": + newConfig.Spec.Wechat = config.Spec.Wechat + default: + return nil + } + + if reflect.ValueOf(newConfig.Spec).IsZero() { + return nil + } + + return newConfig + } else { + receiver := obj.(*v2beta1.Receiver) + newReceiver := receiver.DeepCopy() + newReceiver.Spec = v2beta1.ReceiverSpec{} + switch subresource { + case "dingtalk": + newReceiver.Spec.DingTalk = receiver.Spec.DingTalk + case "email": + newReceiver.Spec.Email = receiver.Spec.Email + case "slack": + newReceiver.Spec.Slack = receiver.Spec.Slack + case "webhook": + newReceiver.Spec.Webhook = receiver.Spec.Webhook + case "wechat": + newReceiver.Spec.Wechat = receiver.Spec.Wechat + default: + return nil + } + + if reflect.ValueOf(newReceiver.Spec).IsZero() { + return nil + } + + return newReceiver + } +} diff --git a/pkg/models/notification/notification_test.go b/pkg/models/notification/notification_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a4152b3af9dc0a85040bc6d7bdb5c167fa205994 --- /dev/null +++ b/pkg/models/notification/notification_test.go @@ -0,0 +1,217 @@ +/* +Copyright 2020 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package notification + +import ( + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + fakek8s "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/informers" + "reflect" + "testing" +) + +func TestOperator_List(t *testing.T) { + o := prepare() + tests := []struct { + result *api.ListResult + expectError error + }{ + { + result: &api.ListResult{ + Items: []interface{}{secret1, secret2, secret3}, + TotalItems: 3, + }, + }, + } + + for i, test := range tests { + result, err := o.List("", "secrets", "", &query.Query{ + SortBy: query.FieldName, + Ascending: true, + }) + + if err != nil { + if !reflect.DeepEqual(err, test.expectError) { + t.Errorf("got %#v, expected %#v", err, test.expectError) + } + continue + } + + if diff := cmp.Diff(result, test.result); diff != "" { + t.Errorf("case %d, %s", i, diff) + } + } +} + +func TestOperator_Get(t *testing.T) { + o := prepare() + tests := []struct { + result *corev1.Secret + name string + expectError error + }{ + { + result: secret1, + name: secret1.Name, + expectError: nil, + }, + { + name: "foo4", + expectError: errors.NewNotFound(corev1.Resource("secret"), "foo4"), + }, + } + + for _, test := range tests { + result, err := o.Get("", "secrets", test.name, "") + + if err != nil { + if !reflect.DeepEqual(err, test.expectError) { + t.Errorf("got %#v, expected %#v", err, test.expectError) + } + continue + } + + if diff := cmp.Diff(result, test.result); diff != "" { + t.Error(diff) + } + } +} + +func TestOperator_Create(t *testing.T) { + o := prepare() + tests := []struct { + result *corev1.Secret + secret *corev1.Secret + expectError error + }{ + { + result: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: constants.NotificationSecretNamespace, + Labels: map[string]string{ + "type": "global", + }, + }, + }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Labels: map[string]string{ + "type": "global", + }, + }, + }, + expectError: nil, + }, + } + + for i, test := range tests { + result, err := o.Create("", "secrets", test.secret) + + if err != nil { + if !reflect.DeepEqual(err, test.expectError) { + t.Errorf("case %d, got %#v, expected %#v", i, err, test.expectError) + } + continue + } + + if diff := cmp.Diff(result, test.result); diff != "" { + t.Error(diff) + } + } +} + +func TestOperator_Delete(t *testing.T) { + o := prepare() + tests := []struct { + name string + expectError error + }{ + { + name: "foo4", + expectError: errors.NewNotFound(corev1.Resource("secret"), "foo4"), + }, + } + + for i, test := range tests { + err := o.Delete("", "secrets", test.name) + if err != nil { + if test.expectError != nil && test.expectError.Error() == err.Error() { + continue + } else { + if !reflect.DeepEqual(err, test.expectError) { + t.Errorf("case %d, got %#v, expected %#v", i, err, test.expectError) + } + } + } + } +} + +var ( + secret1 = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: constants.NotificationSecretNamespace, + Labels: map[string]string{ + "type": "global", + }, + }, + } + + secret2 = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: constants.NotificationSecretNamespace, + Labels: map[string]string{ + "type": "global", + }, + }, + } + + secret3 = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo3", + Namespace: constants.NotificationSecretNamespace, + Labels: map[string]string{ + "type": "global", + }, + }, + } + + secrets = []*corev1.Secret{secret1, secret2, secret3} +) + +func prepare() Operator { + + ksClient := fakeks.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, nil, nil, nil, nil) + + for _, secret := range secrets { + _ = fakeInformerFactory.KubernetesSharedInformerFactory().Core().V1().Secrets().Informer().GetIndexer().Add(secret) + } + + return NewOperator(fakeInformerFactory, k8sClient, ksClient) +} diff --git a/pkg/models/resources/v1alpha3/notification/notification.go b/pkg/models/resources/v1alpha3/notification/notification.go new file mode 100644 index 0000000000000000000000000000000000000000..47ad0ff30adc448ae8a2898e923c194f6494a721 --- /dev/null +++ b/pkg/models/resources/v1alpha3/notification/notification.go @@ -0,0 +1,115 @@ +/* +Copyright 2019 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package notification + +import ( + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "strings" +) + +type configGetter struct { + ksInformer ksinformers.SharedInformerFactory +} + +func NewNotificationConfigGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface { + return &configGetter{ksInformer: informer} +} + +func (g *configGetter) Get(_, name string) (runtime.Object, error) { + return g.ksInformer.Notification().V2beta1().Configs().Lister().Get(name) +} + +func (g *configGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + objs, err := g.ksInformer.Notification().V2beta1().Configs().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, obj := range objs { + result = append(result, obj) + } + return v1alpha3.DefaultList(result, query, compare, filter), nil +} + +type receiverGetter struct { + ksInformer ksinformers.SharedInformerFactory +} + +func NewNotificationReceiverGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface { + return &receiverGetter{ksInformer: informer} +} + +func (g *receiverGetter) Get(_, name string) (runtime.Object, error) { + return g.ksInformer.Notification().V2beta1().Receivers().Lister().Get(name) +} + +func (g *receiverGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + objs, err := g.ksInformer.Notification().V2beta1().Receivers().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, obj := range objs { + result = append(result, obj) + } + return v1alpha3.DefaultList(result, query, compare, filter), nil +} + +func compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftObj, err := meta.Accessor(left) + if err != nil { + return false + } + + rightObj, err := meta.Accessor(right) + if err != nil { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(meta.AsPartialObjectMetadata(leftObj).ObjectMeta, + meta.AsPartialObjectMetadata(rightObj).ObjectMeta, field) +} + +func filter(object runtime.Object, filter query.Filter) bool { + + accessor, err := meta.Accessor(object) + if err != nil { + return false + } + + switch filter.Field { + case query.FieldNames: + for _, name := range strings.Split(string(filter.Value), ",") { + if accessor.GetName() == name { + return true + } + } + return false + case query.FieldName: + return strings.Contains(accessor.GetName(), string(filter.Value)) + default: + return true + } +} diff --git a/pkg/models/resources/v1alpha3/notification/notification_test.go b/pkg/models/resources/v1alpha3/notification/notification_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6be481afa26e770cd74a09510943bf313a5d1c35 --- /dev/null +++ b/pkg/models/resources/v1alpha3/notification/notification_test.go @@ -0,0 +1,140 @@ +/* +Copyright 2019 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package notification + +import ( + "github.com/google/go-cmp/cmp" + "github.com/google/uuid" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/cache" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "kubesphere.io/kubesphere/pkg/server/errors" + "math/rand" + "sort" + "testing" +) + +const ( + Prefix = "foo" + LengthMin = 3 + LengthMax = 10 +) + +func TestListObjects(t *testing.T) { + tests := []struct { + description string + key string + }{ + { + "test name filter", + v2beta1.ResourcesPluralConfig, + }, + { + "test name filter", + v2beta1.ResourcesPluralReceiver, + }, + } + + q := &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: true, + Filters: map[query.Field]query.Value{query.FieldName: query.Value(Prefix)}, + } + + for _, test := range tests { + + getter, objects, err := prepare(test.key) + if err != nil { + t.Fatal(err) + } + + got, err := getter.List("", q) + if err != nil { + t.Fatal(err) + } + + expected := &api.ListResult{ + Items: objects, + TotalItems: len(objects), + } + + if diff := cmp.Diff(got, expected); diff != "" { + t.Errorf("[%s] %T differ (-got, +want): %s", test.description, expected, diff) + } + } +} + +func prepare(key string) (v1alpha3.Interface, []interface{}, error) { + client := fake.NewSimpleClientset() + informer := ksinformers.NewSharedInformerFactory(client, 0) + + var obj runtime.Object + var indexer cache.Indexer + var getter func(informer ksinformers.SharedInformerFactory) v1alpha3.Interface + switch key { + case v2beta1.ResourcesPluralConfig: + indexer = informer.Notification().V2beta1().Configs().Informer().GetIndexer() + getter = NewNotificationConfigGetter + obj = &v2beta1.Config{} + case v2beta1.ResourcesPluralReceiver: + indexer = informer.Notification().V2beta1().Receivers().Informer().GetIndexer() + getter = NewNotificationReceiverGetter + obj = &v2beta1.Receiver{} + default: + return nil, nil, errors.New("unowned type %s", key) + } + + num := rand.Intn(LengthMax) + if num < LengthMin { + num = LengthMin + } + + var suffix []string + for i := 0; i < num; i++ { + s := uuid.New().String() + suffix = append(suffix, s) + } + sort.Strings(suffix) + + var objects []interface{} + for i := 0; i < num; i++ { + val := obj.DeepCopyObject() + accessor, err := meta.Accessor(val) + if err != nil { + return nil, nil, err + } + + accessor.SetName(Prefix + "-" + suffix[i]) + err = indexer.Add(accessor) + if err != nil { + return nil, nil, err + } + objects = append(objects, val) + } + + return getter(informer), objects, nil +} diff --git a/pkg/models/resources/v1alpha3/resource/resource.go b/pkg/models/resources/v1alpha3/resource/resource.go index 6590ba07a2dde5840a4fce81d89c55001b45f89d..e457e4da77fed9ac642b71a892423b4ee6bc6778 100644 --- a/pkg/models/resources/v1alpha3/resource/resource.go +++ b/pkg/models/resources/v1alpha3/resource/resource.go @@ -26,6 +26,7 @@ import ( devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" + notificationv2beta1 "kubesphere.io/kubesphere/pkg/apis/notification/v2beta1" tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2" typesv1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1" @@ -61,10 +62,12 @@ import ( "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/namespace" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/networkpolicy" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/node" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/notification" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/persistentvolumeclaim" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/pod" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/role" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/rolebinding" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/secret" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/service" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/serviceaccount" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/statefulset" @@ -92,6 +95,7 @@ func NewResourceGetter(factory informers.InformerFactory, cache cache.Cache) *Re getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"}] = service.New(factory.KubernetesSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}] = namespace.New(factory.KubernetesSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}] = configmap.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}] = secret.New(factory.KubernetesSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}] = pod.New(factory.KubernetesSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "nodes"}] = node.New(factory.KubernetesSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "serviceaccounts"}] = serviceaccount.New(factory.KubernetesSharedInformerFactory()) @@ -122,6 +126,9 @@ func NewResourceGetter(factory informers.InformerFactory, cache cache.Cache) *Re getters[schema.GroupVersionResource{Group: "cluster.kubesphere.io", Version: "v1alpha1", Resource: "clusters"}] = cluster.New(factory.KubeSphereSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "apiextensions.k8s.io", Version: "v1", Resource: "customresourcedefinitions"}] = customresourcedefinition.New(factory.ApiExtensionSharedInformerFactory()) + getters[notificationv2beta1.SchemeGroupVersion.WithResource(notificationv2beta1.ResourcesPluralConfig)] = notification.NewNotificationConfigGetter(factory.KubeSphereSharedInformerFactory()) + getters[notificationv2beta1.SchemeGroupVersion.WithResource(notificationv2beta1.ResourcesPluralReceiver)] = notification.NewNotificationReceiverGetter(factory.KubeSphereSharedInformerFactory()) + // federated resources getters[typesv1beta1.SchemeGroupVersion.WithResource(typesv1beta1.ResourcePluralFederatedNamespace)] = federatednamespace.New(factory.KubeSphereSharedInformerFactory()) getters[typesv1beta1.SchemeGroupVersion.WithResource(typesv1beta1.ResourcePluralFederatedDeployment)] = federateddeployment.New(factory.KubeSphereSharedInformerFactory()) diff --git a/pkg/models/resources/v1alpha3/secret/secrets.go b/pkg/models/resources/v1alpha3/secret/secrets.go new file mode 100644 index 0000000000000000000000000000000000000000..3c2569299e9108e3d2959eaba74388767c4b3725 --- /dev/null +++ b/pkg/models/resources/v1alpha3/secret/secrets.go @@ -0,0 +1,77 @@ +/* +Copyright 2019 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package secret + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + + "k8s.io/api/core/v1" +) + +type secretSearcher struct { + informers informers.SharedInformerFactory +} + +func New(informers informers.SharedInformerFactory) v1alpha3.Interface { + return &secretSearcher{informers: informers} +} + +func (s *secretSearcher) Get(namespace, name string) (runtime.Object, error) { + return s.informers.Core().V1().Secrets().Lister().Secrets(namespace).Get(name) +} + +func (s *secretSearcher) List(namespace string, query *query.Query) (*api.ListResult, error) { + secrets, err := s.informers.Core().V1().Secrets().Lister().Secrets(namespace).List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, secret := range secrets { + result = append(result, secret) + } + + return v1alpha3.DefaultList(result, query, s.compare, s.filter), nil +} + +func (s *secretSearcher) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftSecret, ok := left.(*v1.Secret) + if !ok { + return false + } + + rightSecret, ok := right.(*v1.Secret) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftSecret.ObjectMeta, rightSecret.ObjectMeta, field) +} + +func (s *secretSearcher) filter(object runtime.Object, filter query.Filter) bool { + secret, ok := object.(*v1.Secret) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(secret.ObjectMeta, filter) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informertest/fake_cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informertest/fake_cache.go new file mode 100644 index 0000000000000000000000000000000000000000..e686208296ba9909cd77aedc37c5e428537a04cb --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informertest/fake_cache.go @@ -0,0 +1,141 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package informertest + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/scheme" + toolscache "k8s.io/client-go/tools/cache" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllertest" +) + +var _ cache.Cache = &FakeInformers{} + +// FakeInformers is a fake implementation of Informers +type FakeInformers struct { + InformersByGVK map[schema.GroupVersionKind]toolscache.SharedIndexInformer + Scheme *runtime.Scheme + Error error + Synced *bool +} + +// GetInformerForKind implements Informers +func (c *FakeInformers) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (cache.Informer, error) { + if c.Scheme == nil { + c.Scheme = scheme.Scheme + } + obj, err := c.Scheme.New(gvk) + if err != nil { + return nil, err + } + return c.informerFor(gvk, obj) +} + +// FakeInformerForKind implements Informers +func (c *FakeInformers) FakeInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (*controllertest.FakeInformer, error) { + if c.Scheme == nil { + c.Scheme = scheme.Scheme + } + obj, err := c.Scheme.New(gvk) + if err != nil { + return nil, err + } + i, err := c.informerFor(gvk, obj) + if err != nil { + return nil, err + } + return i.(*controllertest.FakeInformer), nil +} + +// GetInformer implements Informers +func (c *FakeInformers) GetInformer(ctx context.Context, obj runtime.Object) (cache.Informer, error) { + if c.Scheme == nil { + c.Scheme = scheme.Scheme + } + gvks, _, err := c.Scheme.ObjectKinds(obj) + if err != nil { + return nil, err + } + gvk := gvks[0] + return c.informerFor(gvk, obj) +} + +// WaitForCacheSync implements Informers +func (c *FakeInformers) WaitForCacheSync(stop <-chan struct{}) bool { + if c.Synced == nil { + return true + } + return *c.Synced +} + +// FakeInformerFor implements Informers +func (c *FakeInformers) FakeInformerFor(obj runtime.Object) (*controllertest.FakeInformer, error) { + if c.Scheme == nil { + c.Scheme = scheme.Scheme + } + gvks, _, err := c.Scheme.ObjectKinds(obj) + if err != nil { + return nil, err + } + gvk := gvks[0] + i, err := c.informerFor(gvk, obj) + if err != nil { + return nil, err + } + return i.(*controllertest.FakeInformer), nil +} + +func (c *FakeInformers) informerFor(gvk schema.GroupVersionKind, _ runtime.Object) (toolscache.SharedIndexInformer, error) { + if c.Error != nil { + return nil, c.Error + } + if c.InformersByGVK == nil { + c.InformersByGVK = map[schema.GroupVersionKind]toolscache.SharedIndexInformer{} + } + informer, ok := c.InformersByGVK[gvk] + if ok { + return informer, nil + } + + c.InformersByGVK[gvk] = &controllertest.FakeInformer{} + return c.InformersByGVK[gvk], nil +} + +// Start implements Informers +func (c *FakeInformers) Start(stopCh <-chan struct{}) error { + return c.Error +} + +// IndexField implements Cache +func (c *FakeInformers) IndexField(ctx context.Context, obj runtime.Object, field string, extractValue client.IndexerFunc) error { + return nil +} + +// Get implements Cache +func (c *FakeInformers) Get(ctx context.Context, key client.ObjectKey, obj runtime.Object) error { + return nil +} + +// List implements Cache +func (c *FakeInformers) List(ctx context.Context, list runtime.Object, opts ...client.ListOption) error { + return nil +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go new file mode 100644 index 0000000000000000000000000000000000000000..e7d3c5c35b7e696ea4577d5db8e3861498acd518 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go @@ -0,0 +1,409 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fake + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strconv" + "strings" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + utilrand "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/testing" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/internal/objectutil" +) + +type versionedTracker struct { + testing.ObjectTracker +} + +type fakeClient struct { + tracker versionedTracker + scheme *runtime.Scheme +} + +var _ client.Client = &fakeClient{} + +const ( + maxNameLength = 63 + randomLength = 5 + maxGeneratedNameLength = maxNameLength - randomLength +) + +// NewFakeClient creates a new fake client for testing. +// You can choose to initialize it with a slice of runtime.Object. +// Deprecated: use NewFakeClientWithScheme. You should always be +// passing an explicit Scheme. +func NewFakeClient(initObjs ...runtime.Object) client.Client { + return NewFakeClientWithScheme(scheme.Scheme, initObjs...) +} + +// NewFakeClientWithScheme creates a new fake client with the given scheme +// for testing. +// You can choose to initialize it with a slice of runtime.Object. +func NewFakeClientWithScheme(clientScheme *runtime.Scheme, initObjs ...runtime.Object) client.Client { + tracker := testing.NewObjectTracker(clientScheme, scheme.Codecs.UniversalDecoder()) + for _, obj := range initObjs { + err := tracker.Add(obj) + if err != nil { + panic(fmt.Errorf("failed to add object %v to fake client: %w", obj, err)) + } + } + return &fakeClient{ + tracker: versionedTracker{tracker}, + scheme: clientScheme, + } +} + +func (t versionedTracker) Create(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error { + accessor, err := meta.Accessor(obj) + if err != nil { + return err + } + if accessor.GetName() == "" { + return apierrors.NewInvalid( + obj.GetObjectKind().GroupVersionKind().GroupKind(), + accessor.GetName(), + field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + } + if accessor.GetResourceVersion() != "" { + return apierrors.NewBadRequest("resourceVersion can not be set for Create requests") + } + accessor.SetResourceVersion("1") + if err := t.ObjectTracker.Create(gvr, obj, ns); err != nil { + accessor.SetResourceVersion("") + return err + } + return nil +} + +func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error { + accessor, err := meta.Accessor(obj) + if err != nil { + return fmt.Errorf("failed to get accessor for object: %v", err) + } + if accessor.GetName() == "" { + return apierrors.NewInvalid( + obj.GetObjectKind().GroupVersionKind().GroupKind(), + accessor.GetName(), + field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + } + oldObject, err := t.ObjectTracker.Get(gvr, ns, accessor.GetName()) + if err != nil { + return err + } + oldAccessor, err := meta.Accessor(oldObject) + if err != nil { + return err + } + if accessor.GetResourceVersion() != oldAccessor.GetResourceVersion() { + return apierrors.NewConflict(gvr.GroupResource(), accessor.GetName(), errors.New("object was modified")) + } + if oldAccessor.GetResourceVersion() == "" { + oldAccessor.SetResourceVersion("0") + } + intResourceVersion, err := strconv.ParseUint(oldAccessor.GetResourceVersion(), 10, 64) + if err != nil { + return fmt.Errorf("can not convert resourceVersion %q to int: %v", oldAccessor.GetResourceVersion(), err) + } + intResourceVersion++ + accessor.SetResourceVersion(strconv.FormatUint(intResourceVersion, 10)) + return t.ObjectTracker.Update(gvr, obj, ns) +} + +func (c *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj runtime.Object) error { + gvr, err := getGVRFromObject(obj, c.scheme) + if err != nil { + return err + } + o, err := c.tracker.Get(gvr, key.Namespace, key.Name) + if err != nil { + return err + } + + gvk, err := apiutil.GVKForObject(obj, c.scheme) + if err != nil { + return err + } + ta, err := meta.TypeAccessor(o) + if err != nil { + return err + } + ta.SetKind(gvk.Kind) + ta.SetAPIVersion(gvk.GroupVersion().String()) + + j, err := json.Marshal(o) + if err != nil { + return err + } + decoder := scheme.Codecs.UniversalDecoder() + _, _, err = decoder.Decode(j, nil, obj) + return err +} + +func (c *fakeClient) List(ctx context.Context, obj runtime.Object, opts ...client.ListOption) error { + gvk, err := apiutil.GVKForObject(obj, c.scheme) + if err != nil { + return err + } + + OriginalKind := gvk.Kind + + if !strings.HasSuffix(gvk.Kind, "List") { + return fmt.Errorf("non-list type %T (kind %q) passed as output", obj, gvk) + } + // we need the non-list GVK, so chop off the "List" from the end of the kind + gvk.Kind = gvk.Kind[:len(gvk.Kind)-4] + + listOpts := client.ListOptions{} + listOpts.ApplyOptions(opts) + + gvr, _ := meta.UnsafeGuessKindToResource(gvk) + o, err := c.tracker.List(gvr, gvk, listOpts.Namespace) + if err != nil { + return err + } + + ta, err := meta.TypeAccessor(o) + if err != nil { + return err + } + ta.SetKind(OriginalKind) + ta.SetAPIVersion(gvk.GroupVersion().String()) + + j, err := json.Marshal(o) + if err != nil { + return err + } + decoder := scheme.Codecs.UniversalDecoder() + _, _, err = decoder.Decode(j, nil, obj) + if err != nil { + return err + } + + if listOpts.LabelSelector != nil { + objs, err := meta.ExtractList(obj) + if err != nil { + return err + } + filteredObjs, err := objectutil.FilterWithLabels(objs, listOpts.LabelSelector) + if err != nil { + return err + } + err = meta.SetList(obj, filteredObjs) + if err != nil { + return err + } + } + return nil +} + +func (c *fakeClient) Create(ctx context.Context, obj runtime.Object, opts ...client.CreateOption) error { + createOptions := &client.CreateOptions{} + createOptions.ApplyOptions(opts) + + for _, dryRunOpt := range createOptions.DryRun { + if dryRunOpt == metav1.DryRunAll { + return nil + } + } + + gvr, err := getGVRFromObject(obj, c.scheme) + if err != nil { + return err + } + accessor, err := meta.Accessor(obj) + if err != nil { + return err + } + + if accessor.GetName() == "" && accessor.GetGenerateName() != "" { + base := accessor.GetGenerateName() + if len(base) > maxGeneratedNameLength { + base = base[:maxGeneratedNameLength] + } + accessor.SetName(fmt.Sprintf("%s%s", base, utilrand.String(randomLength))) + } + + return c.tracker.Create(gvr, obj, accessor.GetNamespace()) +} + +func (c *fakeClient) Delete(ctx context.Context, obj runtime.Object, opts ...client.DeleteOption) error { + gvr, err := getGVRFromObject(obj, c.scheme) + if err != nil { + return err + } + accessor, err := meta.Accessor(obj) + if err != nil { + return err + } + delOptions := client.DeleteOptions{} + delOptions.ApplyOptions(opts) + + //TODO: implement propagation + return c.tracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName()) +} + +func (c *fakeClient) DeleteAllOf(ctx context.Context, obj runtime.Object, opts ...client.DeleteAllOfOption) error { + gvk, err := apiutil.GVKForObject(obj, c.scheme) + if err != nil { + return err + } + + dcOptions := client.DeleteAllOfOptions{} + dcOptions.ApplyOptions(opts) + + gvr, _ := meta.UnsafeGuessKindToResource(gvk) + o, err := c.tracker.List(gvr, gvk, dcOptions.Namespace) + if err != nil { + return err + } + + objs, err := meta.ExtractList(o) + if err != nil { + return err + } + filteredObjs, err := objectutil.FilterWithLabels(objs, dcOptions.LabelSelector) + if err != nil { + return err + } + for _, o := range filteredObjs { + accessor, err := meta.Accessor(o) + if err != nil { + return err + } + err = c.tracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName()) + if err != nil { + return err + } + } + return nil +} + +func (c *fakeClient) Update(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error { + updateOptions := &client.UpdateOptions{} + updateOptions.ApplyOptions(opts) + + for _, dryRunOpt := range updateOptions.DryRun { + if dryRunOpt == metav1.DryRunAll { + return nil + } + } + + gvr, err := getGVRFromObject(obj, c.scheme) + if err != nil { + return err + } + accessor, err := meta.Accessor(obj) + if err != nil { + return err + } + return c.tracker.Update(gvr, obj, accessor.GetNamespace()) +} + +func (c *fakeClient) Patch(ctx context.Context, obj runtime.Object, patch client.Patch, opts ...client.PatchOption) error { + patchOptions := &client.PatchOptions{} + patchOptions.ApplyOptions(opts) + + for _, dryRunOpt := range patchOptions.DryRun { + if dryRunOpt == metav1.DryRunAll { + return nil + } + } + + gvr, err := getGVRFromObject(obj, c.scheme) + if err != nil { + return err + } + accessor, err := meta.Accessor(obj) + if err != nil { + return err + } + data, err := patch.Data(obj) + if err != nil { + return err + } + + reaction := testing.ObjectReaction(c.tracker) + handled, o, err := reaction(testing.NewPatchAction(gvr, accessor.GetNamespace(), accessor.GetName(), patch.Type(), data)) + if err != nil { + return err + } + if !handled { + panic("tracker could not handle patch method") + } + + gvk, err := apiutil.GVKForObject(obj, c.scheme) + if err != nil { + return err + } + ta, err := meta.TypeAccessor(o) + if err != nil { + return err + } + ta.SetKind(gvk.Kind) + ta.SetAPIVersion(gvk.GroupVersion().String()) + + j, err := json.Marshal(o) + if err != nil { + return err + } + decoder := scheme.Codecs.UniversalDecoder() + _, _, err = decoder.Decode(j, nil, obj) + return err +} + +func (c *fakeClient) Status() client.StatusWriter { + return &fakeStatusWriter{client: c} +} + +func getGVRFromObject(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionResource, error) { + gvk, err := apiutil.GVKForObject(obj, scheme) + if err != nil { + return schema.GroupVersionResource{}, err + } + gvr, _ := meta.UnsafeGuessKindToResource(gvk) + return gvr, nil +} + +type fakeStatusWriter struct { + client *fakeClient +} + +func (sw *fakeStatusWriter) Update(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error { + // TODO(droot): This results in full update of the obj (spec + status). Need + // a way to update status field only. + return sw.client.Update(ctx, obj, opts...) +} + +func (sw *fakeStatusWriter) Patch(ctx context.Context, obj runtime.Object, patch client.Patch, opts ...client.PatchOption) error { + // TODO(droot): This results in full update of the obj (spec + status). Need + // a way to update status field only. + return sw.client.Patch(ctx, obj, patch, opts...) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..a45d703320a9690e96cdf0f8fa4e22ab88b9b8c3 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/doc.go @@ -0,0 +1,33 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +Package fake provides a fake client for testing. + +Deprecated: please use pkg/envtest for testing. This package will be dropped +before the v1.0.0 release. + +An fake client is backed by its simple object store indexed by GroupVersionResource. +You can create a fake client with optional objects. + + client := NewFakeClient(initObjs...) // initObjs is a slice of runtime.Object + +You can invoke the methods defined in the Client interface. + +When it doubt, it's almost always better not to use this package and instead use +envtest.Environment with a real client and API server. +*/ +package fake diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..91c5a3e35ef2925411ee4c6bccc5e664fd3c169c --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package controllertest contains fake informers for testing controllers +// When in doubt, it's almost always better to test against a real API server +// using envtest.Environment. +package controllertest diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/testing.go b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/testing.go new file mode 100644 index 0000000000000000000000000000000000000000..570a97e36d6bd3d1d8f5d8a2061ab312ed6735ce --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/testing.go @@ -0,0 +1,62 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllertest + +import ( + "time" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/util/workqueue" +) + +var _ runtime.Object = &ErrorType{} + +// ErrorType implements runtime.Object but isn't registered in any scheme and should cause errors in tests as a result. +type ErrorType struct{} + +// GetObjectKind implements runtime.Object +func (ErrorType) GetObjectKind() schema.ObjectKind { return nil } + +// DeepCopyObject implements runtime.Object +func (ErrorType) DeepCopyObject() runtime.Object { return nil } + +var _ workqueue.RateLimitingInterface = Queue{} + +// Queue implements a RateLimiting queue as a non-ratelimited queue for testing. +// This helps testing by having functions that use a RateLimiting queue synchronously add items to the queue. +type Queue struct { + workqueue.Interface +} + +// AddAfter implements RateLimitingInterface. +func (q Queue) AddAfter(item interface{}, duration time.Duration) { + q.Add(item) +} + +// AddRateLimited implements RateLimitingInterface. TODO(community): Implement this. +func (q Queue) AddRateLimited(item interface{}) { + q.Add(item) +} + +// Forget implements RateLimitingInterface. TODO(community): Implement this. +func (q Queue) Forget(item interface{}) {} + +// NumRequeues implements RateLimitingInterface. TODO(community): Implement this. +func (q Queue) NumRequeues(item interface{}) int { + return 0 +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/unconventionallisttypecrd.go b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/unconventionallisttypecrd.go new file mode 100644 index 0000000000000000000000000000000000000000..a4d23f8abedce54d41088def46030aad983cec01 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/unconventionallisttypecrd.go @@ -0,0 +1,60 @@ +package controllertest + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +var _ runtime.Object = &UnconventionalListType{} +var _ runtime.Object = &UnconventionalListTypeList{} + +// UnconventionalListType is used to test CRDs with List types that +// have a slice of pointers rather than a slice of literals. +type UnconventionalListType struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec string `json:"spec,omitempty"` +} + +// DeepCopyObject implements runtime.Object +// Handwritten for simplicity. +func (u *UnconventionalListType) DeepCopyObject() runtime.Object { + return u.DeepCopy() +} + +// DeepCopy implements *UnconventionalListType +// Handwritten for simplicity. +func (u *UnconventionalListType) DeepCopy() *UnconventionalListType { + return &UnconventionalListType{ + TypeMeta: u.TypeMeta, + ObjectMeta: *u.ObjectMeta.DeepCopy(), + Spec: u.Spec, + } +} + +// UnconventionalListTypeList is used to test CRDs with List types that +// have a slice of pointers rather than a slice of literals. +type UnconventionalListTypeList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []*UnconventionalListType `json:"items"` +} + +// DeepCopyObject implements runtime.Object +// Handwritten for simplicity. +func (u *UnconventionalListTypeList) DeepCopyObject() runtime.Object { + return u.DeepCopy() +} + +// DeepCopy implements *UnconventionalListTypeListt +// Handwritten for simplicity. +func (u *UnconventionalListTypeList) DeepCopy() *UnconventionalListTypeList { + out := &UnconventionalListTypeList{ + TypeMeta: u.TypeMeta, + ListMeta: *u.ListMeta.DeepCopy(), + } + for _, item := range u.Items { + out.Items = append(out.Items, item.DeepCopy()) + } + return out +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/util.go b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/util.go new file mode 100644 index 0000000000000000000000000000000000000000..5557c80cfa568510d73bdcdadc3368c929a23510 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/util.go @@ -0,0 +1,108 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllertest + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/cache" +) + +var _ cache.SharedIndexInformer = &FakeInformer{} + +// FakeInformer provides fake Informer functionality for testing +type FakeInformer struct { + // Synced is returned by the HasSynced functions to implement the Informer interface + Synced bool + + // RunCount is incremented each time RunInformersAndControllers is called + RunCount int + + handlers []cache.ResourceEventHandler +} + +// AddIndexers does nothing. TODO(community): Implement this. +func (f *FakeInformer) AddIndexers(indexers cache.Indexers) error { + return nil +} + +// GetIndexer does nothing. TODO(community): Implement this. +func (f *FakeInformer) GetIndexer() cache.Indexer { + return nil +} + +// Informer returns the fake Informer. +func (f *FakeInformer) Informer() cache.SharedIndexInformer { + return f +} + +// HasSynced implements the Informer interface. Returns f.Synced +func (f *FakeInformer) HasSynced() bool { + return f.Synced +} + +// AddEventHandler implements the Informer interface. Adds an EventHandler to the fake Informers. +func (f *FakeInformer) AddEventHandler(handler cache.ResourceEventHandler) { + f.handlers = append(f.handlers, handler) +} + +// Run implements the Informer interface. Increments f.RunCount +func (f *FakeInformer) Run(<-chan struct{}) { + f.RunCount++ +} + +// Add fakes an Add event for obj +func (f *FakeInformer) Add(obj metav1.Object) { + for _, h := range f.handlers { + h.OnAdd(obj) + } +} + +// Update fakes an Update event for obj +func (f *FakeInformer) Update(oldObj, newObj metav1.Object) { + for _, h := range f.handlers { + h.OnUpdate(oldObj, newObj) + } +} + +// Delete fakes an Delete event for obj +func (f *FakeInformer) Delete(obj metav1.Object) { + for _, h := range f.handlers { + h.OnDelete(obj) + } +} + +// AddEventHandlerWithResyncPeriod does nothing. TODO(community): Implement this. +func (f *FakeInformer) AddEventHandlerWithResyncPeriod(handler cache.ResourceEventHandler, resyncPeriod time.Duration) { + +} + +// GetStore does nothing. TODO(community): Implement this. +func (f *FakeInformer) GetStore() cache.Store { + return nil +} + +// GetController does nothing. TODO(community): Implement this. +func (f *FakeInformer) GetController() cache.Controller { + return nil +} + +// LastSyncResourceVersion does nothing. TODO(community): Implement this. +func (f *FakeInformer) LastSyncResourceVersion() string { + return "" +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/objectutil/filter.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/objectutil/filter.go new file mode 100644 index 0000000000000000000000000000000000000000..8513846e2c173e887978fcd2788513ac655f47aa --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/objectutil/filter.go @@ -0,0 +1,42 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package objectutil + +import ( + apimeta "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" +) + +// FilterWithLabels returns a copy of the items in objs matching labelSel +func FilterWithLabels(objs []runtime.Object, labelSel labels.Selector) ([]runtime.Object, error) { + outItems := make([]runtime.Object, 0, len(objs)) + for _, obj := range objs { + meta, err := apimeta.Accessor(obj) + if err != nil { + return nil, err + } + if labelSel != nil { + lbls := labels.Set(meta.GetLabels()) + if !labelSel.Matches(lbls) { + continue + } + } + outItems = append(outItems, obj.DeepCopyObject()) + } + return outItems, nil +}