Merge pull request #3356 from wanjunlei/nm

support customize notification receiver
This commit is contained in:
KubeSphere CI Bot
2021-03-12 16:53:01 +08:00
committed by GitHub
65 changed files with 8900 additions and 2 deletions

View File

@@ -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 {

View File

@@ -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: []

View File

@@ -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: []

View File

@@ -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: []

View File

@@ -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: []

View File

@@ -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: []

View File

@@ -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: []

View File

@@ -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"

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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{})
}

View File

@@ -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

View File

@@ -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{})
}

View File

@@ -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{})
}

View File

@@ -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()
}

View File

@@ -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"
)

View File

@@ -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)
}

File diff suppressed because it is too large Load Diff

View File

@@ -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"`
}

View File

@@ -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"`
}

View File

@@ -62,6 +62,10 @@ func init() {
&FederatedLimitRangeList{},
&FederatedNamespace{},
&FederatedNamespaceList{},
&FederatedNotificationConfig{},
&FederatedNotificationConfigList{},
&FederatedNotificationReceiver{},
&FederatedNotificationReceiverList{},
&FederatedPersistentVolumeClaim{},
&FederatedPersistentVolumeClaimList{},
&FederatedResourceQuota{},

View File

@@ -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.

View File

@@ -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{

View File

@@ -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)

View File

@@ -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}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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{}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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)
},
},
&notificationv2beta1.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(&notificationv2beta1.Config{}, f.defaultInformer)
}
func (f *configInformer) Lister() v2beta1.ConfigLister {
return v2beta1.NewConfigLister(f.Informer().GetIndexer())
}

View File

@@ -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}
}

View File

@@ -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)
},
},
&notificationv2beta1.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(&notificationv2beta1.Receiver{}, f.defaultInformer)
}
func (f *receiverInformer) Lister() v2beta1.ReceiverLister {
return v2beta1.NewReceiverLister(f.Informer().GetIndexer())
}

View File

@@ -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
}

View File

@@ -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{}

View File

@@ -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
}

View File

@@ -125,6 +125,9 @@ const (
ApplicationName = "app.kubernetes.io/name"
ApplicationVersion = "app.kubernetes.io/version"
AlertingTag = "Alerting"
NotificationTag = "Notification"
NotificationSecretNamespace = "kubesphere-monitoring-federated"
)
var (

View File

@@ -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
}

View File

@@ -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())
})

View File

@@ -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())
})
})
})
)

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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)
}

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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())

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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...)
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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 ""
}

View File

@@ -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
}