support customize notification receiver

Signed-off-by: wanjunlei <wanjunlei@yunify.com>
This commit is contained in:
wanjunlei
2021-01-27 14:37:44 +08:00
parent 01600081f6
commit f9213fcdc8
130 changed files with 18870 additions and 1 deletions

View File

@@ -0,0 +1,383 @@
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"
v2 "kubesphere.io/kubesphere/pkg/apis/notification/v2"
"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"
)
type Operator interface {
List(user, resource string, query *query.Query) (*api.ListResult, error)
Get(user, resource, name string) (runtime.Object, error)
Create(user, resource string, obj runtime.Object) (runtime.Object, error)
Delete(user, resource, name string) error
Update(user, resource string, obj runtime.Object) (runtime.Object, error)
ListSecret(query *query.Query) (*api.ListResult, error)
GetSecret(name string) (interface{}, error)
CreateOrUpdateSecret(obj *corev1.Secret) (*corev1.Secret, error)
DeleteSecret(name string) error
GetObject(resource string) runtime.Object
IsKnownResource(resource 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, if the user is nil, it will return the global object.
// If the user it not nil, it will return tenant object which the tenant specified in the labels of object
// equal to this user.
func (o *operator) List(user, resource string, q *query.Query) (*api.ListResult, error) {
// If user is nil, it will list all global object.
if user == "" {
appendGlobalLabel(resource, q)
} else {
// If the user is not nil, only return the object belong to this user.
appendTenantLabel(user, q)
}
return o.resourceGetter.List(resource, "", q)
}
// 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 string) (runtime.Object, error) {
obj, err := o.resourceGetter.Get(resource, "", name)
if err != nil {
return nil, err
}
if err := authorizer(user, obj); err != nil {
return nil, err
}
return obj, nil
}
// Create a object, if the user is nil, it only can create a global object.
// If the user is not nil, it only can create a tenant object.
func (o *operator) Create(user, resource string, obj runtime.Object) (runtime.Object, error) {
if err := authorizer(user, obj); err != nil {
return nil, err
}
switch resource {
case v2.ResourcesPluralDingTalkConfig:
return o.ksClient.NotificationV2().DingTalkConfigs().Create(context.Background(), obj.(*v2.DingTalkConfig), v1.CreateOptions{})
case v2.ResourcesPluralDingTalkReceiver:
return o.ksClient.NotificationV2().DingTalkReceivers().Create(context.Background(), obj.(*v2.DingTalkReceiver), v1.CreateOptions{})
case v2.ResourcesPluralEmailConfig:
return o.ksClient.NotificationV2().EmailConfigs().Create(context.Background(), obj.(*v2.EmailConfig), v1.CreateOptions{})
case v2.ResourcesPluralEmailReceiver:
return o.ksClient.NotificationV2().EmailReceivers().Create(context.Background(), obj.(*v2.EmailReceiver), v1.CreateOptions{})
case v2.ResourcesPluralSlackConfig:
return o.ksClient.NotificationV2().SlackConfigs().Create(context.Background(), obj.(*v2.SlackConfig), v1.CreateOptions{})
case v2.ResourcesPluralSlackReceiver:
return o.ksClient.NotificationV2().SlackReceivers().Create(context.Background(), obj.(*v2.SlackReceiver), v1.CreateOptions{})
case v2.ResourcesPluralWebhookConfig:
return o.ksClient.NotificationV2().WebhookConfigs().Create(context.Background(), obj.(*v2.WebhookConfig), v1.CreateOptions{})
case v2.ResourcesPluralWebhookReceiver:
return o.ksClient.NotificationV2().WebhookReceivers().Create(context.Background(), obj.(*v2.WebhookReceiver), v1.CreateOptions{})
case v2.ResourcesPluralWechatConfig:
return o.ksClient.NotificationV2().WechatConfigs().Create(context.Background(), obj.(*v2.WechatConfig), v1.CreateOptions{})
case v2.ResourcesPluralWechatReceiver:
return o.ksClient.NotificationV2().WechatReceivers().Create(context.Background(), obj.(*v2.WechatReceiver), v1.CreateOptions{})
default:
return nil, errors.NewInternalError(nil)
}
}
// Delete a object, if the user is nil, it only can delete the global object.
// If the user is not nil, it only can delete the tenant object which the tenant
// specified in the labels of the object equal to this user.
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 v2.ResourcesPluralDingTalkConfig:
return o.ksClient.NotificationV2().DingTalkConfigs().Delete(context.Background(), name, v1.DeleteOptions{})
case v2.ResourcesPluralDingTalkReceiver:
return o.ksClient.NotificationV2().DingTalkReceivers().Delete(context.Background(), name, v1.DeleteOptions{})
case v2.ResourcesPluralEmailConfig:
return o.ksClient.NotificationV2().EmailConfigs().Delete(context.Background(), name, v1.DeleteOptions{})
case v2.ResourcesPluralEmailReceiver:
return o.ksClient.NotificationV2().EmailReceivers().Delete(context.Background(), name, v1.DeleteOptions{})
case v2.ResourcesPluralSlackConfig:
return o.ksClient.NotificationV2().SlackConfigs().Delete(context.Background(), name, v1.DeleteOptions{})
case v2.ResourcesPluralSlackReceiver:
return o.ksClient.NotificationV2().SlackReceivers().Delete(context.Background(), name, v1.DeleteOptions{})
case v2.ResourcesPluralWebhookConfig:
return o.ksClient.NotificationV2().WebhookConfigs().Delete(context.Background(), name, v1.DeleteOptions{})
case v2.ResourcesPluralWebhookReceiver:
return o.ksClient.NotificationV2().WebhookReceivers().Delete(context.Background(), name, v1.DeleteOptions{})
case v2.ResourcesPluralWechatConfig:
return o.ksClient.NotificationV2().WechatConfigs().Delete(context.Background(), name, v1.DeleteOptions{})
case v2.ResourcesPluralWechatReceiver:
return o.ksClient.NotificationV2().WechatReceivers().Delete(context.Background(), name, v1.DeleteOptions{})
default:
return errors.NewInternalError(nil)
}
}
// Update a object, if the user is nil, it only can update the global object.
// If the user is not nil, it only can delete the tenant object which the tenant
// specified in the labels of the object equal to this user.
func (o *operator) Update(user, resource string, obj runtime.Object) (runtime.Object, error) {
name, err := getName(obj)
if err != nil {
return nil, err
}
if _, err := o.Get(user, resource, name); err != nil {
return nil, err
} else {
if err := authorizer(user, obj); err != nil {
return nil, err
}
}
switch resource {
case v2.ResourcesPluralDingTalkConfig:
return o.ksClient.NotificationV2().DingTalkConfigs().Update(context.Background(), obj.(*v2.DingTalkConfig), v1.UpdateOptions{})
case v2.ResourcesPluralDingTalkReceiver:
return o.ksClient.NotificationV2().DingTalkReceivers().Update(context.Background(), obj.(*v2.DingTalkReceiver), v1.UpdateOptions{})
case v2.ResourcesPluralEmailConfig:
return o.ksClient.NotificationV2().EmailConfigs().Update(context.Background(), obj.(*v2.EmailConfig), v1.UpdateOptions{})
case v2.ResourcesPluralEmailReceiver:
return o.ksClient.NotificationV2().EmailReceivers().Update(context.Background(), obj.(*v2.EmailReceiver), v1.UpdateOptions{})
case v2.ResourcesPluralSlackConfig:
return o.ksClient.NotificationV2().SlackConfigs().Update(context.Background(), obj.(*v2.SlackConfig), v1.UpdateOptions{})
case v2.ResourcesPluralSlackReceiver:
return o.ksClient.NotificationV2().SlackReceivers().Update(context.Background(), obj.(*v2.SlackReceiver), v1.UpdateOptions{})
case v2.ResourcesPluralWebhookConfig:
return o.ksClient.NotificationV2().WebhookConfigs().Update(context.Background(), obj.(*v2.WebhookConfig), v1.UpdateOptions{})
case v2.ResourcesPluralWebhookReceiver:
return o.ksClient.NotificationV2().WebhookReceivers().Update(context.Background(), obj.(*v2.WebhookReceiver), v1.UpdateOptions{})
case v2.ResourcesPluralWechatConfig:
return o.ksClient.NotificationV2().WechatConfigs().Update(context.Background(), obj.(*v2.WechatConfig), v1.UpdateOptions{})
case v2.ResourcesPluralWechatReceiver:
return o.ksClient.NotificationV2().WechatReceivers().Update(context.Background(), obj.(*v2.WechatReceiver), v1.UpdateOptions{})
default:
return nil, errors.NewInternalError(nil)
}
}
func (o *operator) ListSecret(q *query.Query) (*api.ListResult, error) {
appendManagedLabel(q)
return o.resourceGetter.List("secrets", constants.KubeSphereNotificationNamespace, q)
}
func (o *operator) GetSecret(name string) (interface{}, error) {
obj, err := o.resourceGetter.Get("secrets", constants.KubeSphereNotificationNamespace, name)
if err != nil {
return nil, err
}
if !isManagedByNotification(obj.(*corev1.Secret)) {
return nil, errors.NewForbidden(v2.Resource(obj.GetObjectKind().GroupVersionKind().GroupKind().Kind), "",
fmt.Errorf("secret '%s' is not managed by notification", name))
}
return obj, nil
}
func (o *operator) CreateOrUpdateSecret(obj *corev1.Secret) (*corev1.Secret, error) {
obj.Namespace = constants.KubeSphereNotificationNamespace
if obj.Labels == nil {
obj.Labels = make(map[string]string)
}
obj.Labels[constants.NotificationManagedLabel] = "true"
if obj.ResourceVersion == "" {
return o.k8sClient.CoreV1().Secrets(constants.KubeSphereNotificationNamespace).Create(context.Background(), obj, v1.CreateOptions{})
} else {
return o.k8sClient.CoreV1().Secrets(constants.KubeSphereNotificationNamespace).Update(context.Background(), obj, v1.UpdateOptions{})
}
}
func (o *operator) DeleteSecret(name string) error {
if _, err := o.GetSecret(name); err != nil {
return err
}
return o.k8sClient.CoreV1().Secrets(constants.KubeSphereNotificationNamespace).Delete(context.Background(), name, v1.DeleteOptions{})
}
func (o *operator) GetObject(resource string) runtime.Object {
switch resource {
case v2.ResourcesPluralDingTalkConfig:
return &v2.DingTalkConfig{}
case v2.ResourcesPluralDingTalkReceiver:
return &v2.DingTalkReceiver{}
case v2.ResourcesPluralEmailConfig:
return &v2.EmailConfig{}
case v2.ResourcesPluralEmailReceiver:
return &v2.EmailReceiver{}
case v2.ResourcesPluralSlackConfig:
return &v2.SlackConfig{}
case v2.ResourcesPluralSlackReceiver:
return &v2.SlackReceiver{}
case v2.ResourcesPluralWebhookConfig:
return &v2.WebhookConfig{}
case v2.ResourcesPluralWebhookReceiver:
return &v2.WebhookReceiver{}
case v2.ResourcesPluralWechatConfig:
return &v2.WechatConfig{}
case v2.ResourcesPluralWechatReceiver:
return &v2.WechatReceiver{}
default:
return nil
}
}
func (o *operator) IsKnownResource(resource string) bool {
if obj := o.GetObject(resource); obj == nil {
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(v2.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(v2.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 *v2.DingTalkConfig, *v2.EmailConfig, *v2.SlackConfig, *v2.WebhookConfig, *v2.WechatConfig:
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 appendTenantLabel(user string, q *query.Query) {
if len(q.LabelSelector) > 0 {
q.LabelSelector = q.LabelSelector + ","
}
q.LabelSelector = q.LabelSelector + "type=tenant,user=" + user
}
func appendGlobalLabel(resource string, q *query.Query) {
if len(q.LabelSelector) > 0 {
q.LabelSelector = q.LabelSelector + ","
}
switch resource {
case v2.ResourcesPluralDingTalkConfig, v2.ResourcesPluralEmailConfig,
v2.ResourcesPluralSlackConfig, v2.ResourcesPluralWebhookConfig, v2.ResourcesPluralWechatConfig:
q.LabelSelector = q.LabelSelector + "type=default"
case v2.ResourcesPluralDingTalkReceiver, v2.ResourcesPluralEmailReceiver,
v2.ResourcesPluralSlackReceiver, v2.ResourcesPluralWebhookReceiver, v2.ResourcesPluralWechatReceiver:
q.LabelSelector = q.LabelSelector + "type=global"
}
}
func appendManagedLabel(q *query.Query) {
if len(q.LabelSelector) > 0 {
q.LabelSelector = q.LabelSelector + ","
}
q.LabelSelector = q.LabelSelector + constants.NotificationManagedLabel + "=" + "true"
}
func isManagedByNotification(secret *corev1.Secret) bool {
return secret.Labels[constants.NotificationManagedLabel] == "true"
}
func getName(obj runtime.Object) (string, error) {
accessor, err := meta.Accessor(obj)
if err != nil {
return "", err
}
return accessor.GetName(), nil
}

View File

@@ -0,0 +1,206 @@
/*
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_ListSecret(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.ListSecret(&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_GetSecret(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.GetSecret(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_CreateOrUpdateSecret(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.KubeSphereNotificationNamespace,
Labels: map[string]string{constants.NotificationManagedLabel: "true"},
},
},
secret: &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
},
expectError: nil,
},
}
for i, test := range tests {
result, err := o.CreateOrUpdateSecret(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_DeleteSecret(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.DeleteSecret(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.KubeSphereNotificationNamespace,
Labels: map[string]string{constants.NotificationManagedLabel: "true"},
},
}
secret2 = &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "foo2",
Namespace: constants.KubeSphereNotificationNamespace,
Labels: map[string]string{constants.NotificationManagedLabel: "true"},
},
}
secret3 = &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "foo3",
Namespace: constants.KubeSphereNotificationNamespace,
Labels: map[string]string{constants.NotificationManagedLabel: "true"},
},
}
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,302 @@
/*
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"
)
type dingtalkConfigGetter struct {
ksInformer ksinformers.SharedInformerFactory
}
func NewDingTalkConfigGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface {
return &dingtalkConfigGetter{ksInformer: informer}
}
func (g *dingtalkConfigGetter) Get(_, name string) (runtime.Object, error) {
return g.ksInformer.Notification().V2().DingTalkConfigs().Lister().Get(name)
}
func (g *dingtalkConfigGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
objs, err := g.ksInformer.Notification().V2().DingTalkConfigs().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 dingtalkReceiverGetter struct {
ksInformer ksinformers.SharedInformerFactory
}
func NewDingTalkReceiverGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface {
return &dingtalkReceiverGetter{ksInformer: informer}
}
func (g *dingtalkReceiverGetter) Get(_, name string) (runtime.Object, error) {
return g.ksInformer.Notification().V2().DingTalkReceivers().Lister().Get(name)
}
func (g *dingtalkReceiverGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
objs, err := g.ksInformer.Notification().V2().DingTalkReceivers().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 emailConfigGetter struct {
ksInformer ksinformers.SharedInformerFactory
}
func NewEmailConfigGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface {
return &emailConfigGetter{ksInformer: informer}
}
func (g *emailConfigGetter) Get(_, name string) (runtime.Object, error) {
return g.ksInformer.Notification().V2().EmailConfigs().Lister().Get(name)
}
func (g *emailConfigGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
objs, err := g.ksInformer.Notification().V2().EmailConfigs().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 emailReceiverGetter struct {
ksInformer ksinformers.SharedInformerFactory
}
func NewEmailReceiverGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface {
return &emailReceiverGetter{ksInformer: informer}
}
func (g *emailReceiverGetter) Get(_, name string) (runtime.Object, error) {
return g.ksInformer.Notification().V2().EmailReceivers().Lister().Get(name)
}
func (g *emailReceiverGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
objs, err := g.ksInformer.Notification().V2().EmailReceivers().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 slackConfigGetter struct {
ksInformer ksinformers.SharedInformerFactory
}
func NewSlackConfigGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface {
return &slackConfigGetter{ksInformer: informer}
}
func (g *slackConfigGetter) Get(_, name string) (runtime.Object, error) {
return g.ksInformer.Notification().V2().SlackConfigs().Lister().Get(name)
}
func (g *slackConfigGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
objs, err := g.ksInformer.Notification().V2().SlackConfigs().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 slackReceiverGetter struct {
ksInformer ksinformers.SharedInformerFactory
}
func NewSlackReceiverGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface {
return &slackReceiverGetter{ksInformer: informer}
}
func (g *slackReceiverGetter) Get(_, name string) (runtime.Object, error) {
return g.ksInformer.Notification().V2().SlackReceivers().Lister().Get(name)
}
func (g *slackReceiverGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
objs, err := g.ksInformer.Notification().V2().SlackReceivers().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 webhookConfigGetter struct {
ksInformer ksinformers.SharedInformerFactory
}
func NewWebhookConfigGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface {
return &webhookConfigGetter{ksInformer: informer}
}
func (g *webhookConfigGetter) Get(_, name string) (runtime.Object, error) {
return g.ksInformer.Notification().V2().WebhookConfigs().Lister().Get(name)
}
func (g *webhookConfigGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
objs, err := g.ksInformer.Notification().V2().WebhookConfigs().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 webhookReceiverGetter struct {
ksInformer ksinformers.SharedInformerFactory
}
func NewWebhookReceiverGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface {
return &webhookReceiverGetter{ksInformer: informer}
}
func (g *webhookReceiverGetter) Get(_, name string) (runtime.Object, error) {
return g.ksInformer.Notification().V2().WebhookReceivers().Lister().Get(name)
}
func (g *webhookReceiverGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
objs, err := g.ksInformer.Notification().V2().WebhookReceivers().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 wechatConfigGetter struct {
ksInformer ksinformers.SharedInformerFactory
}
func NewWechatConfigGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface {
return &wechatConfigGetter{ksInformer: informer}
}
func (g *wechatConfigGetter) Get(_, name string) (runtime.Object, error) {
return g.ksInformer.Notification().V2().WechatConfigs().Lister().Get(name)
}
func (g *wechatConfigGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
objs, err := g.ksInformer.Notification().V2().WechatConfigs().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 wechatReceiverGetter struct {
ksInformer ksinformers.SharedInformerFactory
}
func NewWechatReceiverGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface {
return &wechatReceiverGetter{ksInformer: informer}
}
func (g *wechatReceiverGetter) Get(_, name string) (runtime.Object, error) {
return g.ksInformer.Notification().V2().WechatReceivers().Lister().Get(name)
}
func (g *wechatReceiverGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
objs, err := g.ksInformer.Notification().V2().WechatReceivers().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
}
return v1alpha3.DefaultObjectMetaFilter(meta.AsPartialObjectMetadata(accessor).ObjectMeta, filter)
}

View File

@@ -0,0 +1,204 @@
/*
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"
v2 "kubesphere.io/kubesphere/pkg/apis/notification/v2"
"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",
v2.ResourcesPluralDingTalkConfig,
},
{
"test name filter",
v2.ResourcesPluralDingTalkReceiver,
},
{
"test name filter",
v2.ResourcesPluralEmailConfig,
},
{
"test name filter",
v2.ResourcesPluralEmailReceiver,
},
{
"test name filter",
v2.ResourcesPluralSlackConfig,
},
{
"test name filter",
v2.ResourcesPluralSlackReceiver,
},
{
"test name filter",
v2.ResourcesPluralWebhookConfig,
},
{
"test name filter",
v2.ResourcesPluralWebhookReceiver,
},
{
"test name filter",
v2.ResourcesPluralWechatConfig,
},
{
"test name filter",
v2.ResourcesPluralWechatReceiver,
},
}
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 v2.ResourcesPluralDingTalkConfig:
indexer = informer.Notification().V2().DingTalkConfigs().Informer().GetIndexer()
getter = NewDingTalkConfigGetter
obj = &v2.DingTalkConfig{}
case v2.ResourcesPluralDingTalkReceiver:
indexer = informer.Notification().V2().DingTalkReceivers().Informer().GetIndexer()
getter = NewDingTalkReceiverGetter
obj = &v2.DingTalkReceiver{}
case v2.ResourcesPluralEmailConfig:
indexer = informer.Notification().V2().EmailConfigs().Informer().GetIndexer()
getter = NewEmailConfigGetter
obj = &v2.EmailConfig{}
case v2.ResourcesPluralEmailReceiver:
indexer = informer.Notification().V2().EmailReceivers().Informer().GetIndexer()
getter = NewEmailReceiverGetter
obj = &v2.EmailReceiver{}
case v2.ResourcesPluralSlackConfig:
indexer = informer.Notification().V2().SlackConfigs().Informer().GetIndexer()
getter = NewSlackConfigGetter
obj = &v2.SlackConfig{}
case v2.ResourcesPluralSlackReceiver:
indexer = informer.Notification().V2().SlackReceivers().Informer().GetIndexer()
getter = NewSlackReceiverGetter
obj = &v2.SlackReceiver{}
case v2.ResourcesPluralWebhookConfig:
indexer = informer.Notification().V2().WebhookConfigs().Informer().GetIndexer()
getter = NewWebhookConfigGetter
obj = &v2.WebhookConfig{}
case v2.ResourcesPluralWebhookReceiver:
indexer = informer.Notification().V2().WebhookReceivers().Informer().GetIndexer()
getter = NewWebhookReceiverGetter
obj = &v2.WebhookReceiver{}
case v2.ResourcesPluralWechatConfig:
indexer = informer.Notification().V2().WechatConfigs().Informer().GetIndexer()
getter = NewWechatConfigGetter
obj = &v2.WechatConfig{}
case v2.ResourcesPluralWechatReceiver:
indexer = informer.Notification().V2().WechatReceivers().Informer().GetIndexer()
getter = NewWechatReceiverGetter
obj = &v2.WechatReceiver{}
default:
return nil, nil, errors.New("unkonwed 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"
notificationv2 "kubesphere.io/kubesphere/pkg/apis/notification/v2"
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,17 @@ 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[notificationv2.SchemeGroupVersion.WithResource(notificationv2.ResourcesPluralDingTalkConfig)] = notification.NewDingTalkConfigGetter(factory.KubeSphereSharedInformerFactory())
getters[notificationv2.SchemeGroupVersion.WithResource(notificationv2.ResourcesPluralDingTalkReceiver)] = notification.NewDingTalkReceiverGetter(factory.KubeSphereSharedInformerFactory())
getters[notificationv2.SchemeGroupVersion.WithResource(notificationv2.ResourcesPluralEmailConfig)] = notification.NewEmailConfigGetter(factory.KubeSphereSharedInformerFactory())
getters[notificationv2.SchemeGroupVersion.WithResource(notificationv2.ResourcesPluralEmailReceiver)] = notification.NewEmailReceiverGetter(factory.KubeSphereSharedInformerFactory())
getters[notificationv2.SchemeGroupVersion.WithResource(notificationv2.ResourcesPluralSlackConfig)] = notification.NewSlackConfigGetter(factory.KubeSphereSharedInformerFactory())
getters[notificationv2.SchemeGroupVersion.WithResource(notificationv2.ResourcesPluralSlackReceiver)] = notification.NewSlackReceiverGetter(factory.KubeSphereSharedInformerFactory())
getters[notificationv2.SchemeGroupVersion.WithResource(notificationv2.ResourcesPluralWebhookConfig)] = notification.NewWebhookConfigGetter(factory.KubeSphereSharedInformerFactory())
getters[notificationv2.SchemeGroupVersion.WithResource(notificationv2.ResourcesPluralWebhookReceiver)] = notification.NewWebhookReceiverGetter(factory.KubeSphereSharedInformerFactory())
getters[notificationv2.SchemeGroupVersion.WithResource(notificationv2.ResourcesPluralWechatConfig)] = notification.NewWechatConfigGetter(factory.KubeSphereSharedInformerFactory())
getters[notificationv2.SchemeGroupVersion.WithResource(notificationv2.ResourcesPluralWechatReceiver)] = notification.NewWechatReceiverGetter(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)
}