|
|
|
|
@@ -12,13 +12,20 @@
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
package notification
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"net/http"
|
|
|
|
|
"reflect"
|
|
|
|
|
|
|
|
|
|
"github.com/emicklei/go-restful"
|
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
|
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
|
|
|
@@ -26,8 +33,8 @@ import (
|
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
|
"k8s.io/client-go/kubernetes"
|
|
|
|
|
"k8s.io/klog"
|
|
|
|
|
|
|
|
|
|
"kubesphere.io/api/notification/v2beta1"
|
|
|
|
|
"kubesphere.io/api/notification/v2beta2"
|
|
|
|
|
|
|
|
|
|
"kubesphere.io/kubesphere/pkg/api"
|
|
|
|
|
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
|
|
|
|
@@ -35,21 +42,34 @@ import (
|
|
|
|
|
"kubesphere.io/kubesphere/pkg/constants"
|
|
|
|
|
"kubesphere.io/kubesphere/pkg/informers"
|
|
|
|
|
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
|
|
|
|
|
"kubesphere.io/kubesphere/pkg/simple/client/notification"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
Secret = "secrets"
|
|
|
|
|
Secret = "secrets"
|
|
|
|
|
VerificationAPIPath = "/api/v2/verify"
|
|
|
|
|
|
|
|
|
|
V2beta1 = "v2beta1"
|
|
|
|
|
V2beta2 = "v2beta2"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Operator interface {
|
|
|
|
|
ListV2beta1(user, resource, subresource string, query *query.Query) (*api.ListResult, error)
|
|
|
|
|
GetV2beta1(user, resource, name, subresource string) (runtime.Object, error)
|
|
|
|
|
CreateV2beta1(user, resource string, obj runtime.Object) (runtime.Object, error)
|
|
|
|
|
DeleteV2beta1(user, resource, name string) error
|
|
|
|
|
UpdateV2beta1(user, resource, name string, obj runtime.Object) (runtime.Object, error)
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
Verify(request *restful.Request, response *restful.Response)
|
|
|
|
|
|
|
|
|
|
GetObject(resource, version string) runtime.Object
|
|
|
|
|
IsKnownResource(resource, version, subresource string) bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type operator struct {
|
|
|
|
|
@@ -57,24 +77,52 @@ type operator struct {
|
|
|
|
|
ksClient kubesphere.Interface
|
|
|
|
|
informers informers.InformerFactory
|
|
|
|
|
resourceGetter *resource.ResourceGetter
|
|
|
|
|
options *notification.Options
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Data struct {
|
|
|
|
|
Config v2beta2.Config `json:"config"`
|
|
|
|
|
Receiver v2beta2.Receiver `json:"receiver"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Result struct {
|
|
|
|
|
Code int `json:"Status"`
|
|
|
|
|
Message string `json:"Message"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewOperator(
|
|
|
|
|
informers informers.InformerFactory,
|
|
|
|
|
k8sClient kubernetes.Interface,
|
|
|
|
|
ksClient kubesphere.Interface) Operator {
|
|
|
|
|
ksClient kubesphere.Interface,
|
|
|
|
|
options *notification.Options) Operator {
|
|
|
|
|
|
|
|
|
|
return &operator{
|
|
|
|
|
informers: informers,
|
|
|
|
|
k8sClient: k8sClient,
|
|
|
|
|
ksClient: ksClient,
|
|
|
|
|
resourceGetter: resource.NewResourceGetter(informers, nil),
|
|
|
|
|
options: options,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ListV2beta1 list objects of version v2beta1. 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) ListV2beta1(user, resource, subresource string, q *query.Query) (*api.ListResult, error) {
|
|
|
|
|
return o.list(user, resource, V2beta1, subresource, q)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
|
return o.list(user, resource, V2beta2, subresource, q)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *operator) list(user, resource, version, subresource string, q *query.Query) (*api.ListResult, error) {
|
|
|
|
|
|
|
|
|
|
if user != "" && resource == v2beta2.ResourcesPluralRouter {
|
|
|
|
|
return nil, errors.NewForbidden(v2beta2.Resource(v2beta2.ResourcesPluralRouter), "",
|
|
|
|
|
fmt.Errorf("tenant can not list router"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(q.LabelSelector) > 0 {
|
|
|
|
|
q.LabelSelector = q.LabelSelector + ","
|
|
|
|
|
@@ -83,7 +131,7 @@ func (o *operator) List(user, resource, subresource string, q *query.Query) (*ap
|
|
|
|
|
filter := ""
|
|
|
|
|
// If user is nil, it will list all global object.
|
|
|
|
|
if user == "" {
|
|
|
|
|
if isConfig(o.GetObject(resource)) {
|
|
|
|
|
if isConfig(o.GetObject(resource, version)) {
|
|
|
|
|
filter = "type=default"
|
|
|
|
|
} else {
|
|
|
|
|
filter = "type=global"
|
|
|
|
|
@@ -102,6 +150,7 @@ func (o *operator) List(user, resource, subresource string, q *query.Query) (*ap
|
|
|
|
|
|
|
|
|
|
res, err := o.resourceGetter.List(resource, ns, q)
|
|
|
|
|
if err != nil {
|
|
|
|
|
klog.Error(err)
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -113,6 +162,9 @@ func (o *operator) List(user, resource, subresource string, q *query.Query) (*ap
|
|
|
|
|
for _, item := range res.Items {
|
|
|
|
|
obj := clean(item, resource, subresource)
|
|
|
|
|
if obj != nil {
|
|
|
|
|
if version == V2beta1 {
|
|
|
|
|
obj = convert(obj)
|
|
|
|
|
}
|
|
|
|
|
results.Items = append(results.Items, obj)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -121,9 +173,24 @@ func (o *operator) List(user, resource, subresource string, q *query.Query) (*ap
|
|
|
|
|
return results, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetV2beta1 get the specified object of version v2beta1, 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) GetV2beta1(user, resource, name, subresource string) (runtime.Object, error) {
|
|
|
|
|
return o.get(user, resource, V2beta1, name, subresource)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
|
return o.get(user, resource, V2beta2, name, subresource)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *operator) get(user, resource, version, name, subresource string) (runtime.Object, error) {
|
|
|
|
|
|
|
|
|
|
if user != "" && resource == v2beta2.ResourcesPluralRouter {
|
|
|
|
|
return nil, errors.NewForbidden(v2beta2.Resource(v2beta2.ResourcesPluralRouter), "",
|
|
|
|
|
fmt.Errorf("tenant can not get router"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ns := ""
|
|
|
|
|
if resource == Secret {
|
|
|
|
|
@@ -132,10 +199,12 @@ func (o *operator) Get(user, resource, name, subresource string) (runtime.Object
|
|
|
|
|
|
|
|
|
|
obj, err := o.resourceGetter.Get(resource, ns, name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
klog.Error(err)
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := authorizer(user, obj); err != nil {
|
|
|
|
|
klog.Error(err)
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -148,22 +217,53 @@ func (o *operator) Get(user, resource, name, subresource string) (runtime.Object
|
|
|
|
|
return nil, errors.NewNotFound(v2beta1.Resource(obj.GetObjectKind().GroupVersionKind().GroupKind().Kind), name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if version == V2beta1 {
|
|
|
|
|
res = convert(res)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CreateV2beta1 an object of version v2beta1. 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) CreateV2beta1(user, resource string, obj runtime.Object) (runtime.Object, error) {
|
|
|
|
|
return o.create(user, resource, V2beta1, obj)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
|
return o.create(user, resource, V2beta2, obj)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *operator) create(user, resource, version string, obj runtime.Object) (runtime.Object, error) {
|
|
|
|
|
|
|
|
|
|
if err := appendLabel(user, resource, obj); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if user != "" && resource == v2beta2.ResourcesPluralRouter {
|
|
|
|
|
return nil, errors.NewForbidden(v2beta2.Resource(v2beta2.ResourcesPluralRouter), "",
|
|
|
|
|
fmt.Errorf("tenant can not create router"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 v2beta2.ResourcesPluralConfig:
|
|
|
|
|
if version == V2beta1 {
|
|
|
|
|
return o.ksClient.NotificationV2beta1().Configs().Create(context.Background(), obj.(*v2beta1.Config), v1.CreateOptions{})
|
|
|
|
|
} else {
|
|
|
|
|
return o.ksClient.NotificationV2beta2().Configs().Create(context.Background(), obj.(*v2beta2.Config), v1.CreateOptions{})
|
|
|
|
|
}
|
|
|
|
|
case v2beta2.ResourcesPluralReceiver:
|
|
|
|
|
if version == V2beta1 {
|
|
|
|
|
return o.ksClient.NotificationV2beta1().Receivers().Create(context.Background(), obj.(*v2beta1.Receiver), v1.CreateOptions{})
|
|
|
|
|
} else {
|
|
|
|
|
return o.ksClient.NotificationV2beta2().Receivers().Create(context.Background(), obj.(*v2beta2.Receiver), v1.CreateOptions{})
|
|
|
|
|
}
|
|
|
|
|
case v2beta2.ResourcesPluralRouter:
|
|
|
|
|
return o.ksClient.NotificationV2beta2().Routers().Create(context.Background(), obj.(*v2beta2.Router), v1.CreateOptions{})
|
|
|
|
|
case v2beta2.ResourcesPluralSilence:
|
|
|
|
|
return o.ksClient.NotificationV2beta2().Silences().Create(context.Background(), obj.(*v2beta2.Silence), v1.CreateOptions{})
|
|
|
|
|
case "secrets":
|
|
|
|
|
return o.k8sClient.CoreV1().Secrets(constants.NotificationSecretNamespace).Create(context.Background(), obj.(*corev1.Secret), v1.CreateOptions{})
|
|
|
|
|
default:
|
|
|
|
|
@@ -171,23 +271,44 @@ func (o *operator) Create(user, resource string, obj runtime.Object) (runtime.Ob
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DeleteV2beta1 an object of version v2beta1. 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) DeleteV2beta1(user, resource, name string) error {
|
|
|
|
|
return o.delete(user, resource, name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
|
return o.delete(user, resource, name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *operator) delete(user, resource, name string) error {
|
|
|
|
|
|
|
|
|
|
if user != "" && resource == v2beta2.ResourcesPluralRouter {
|
|
|
|
|
return errors.NewForbidden(v2beta2.Resource(v2beta2.ResourcesPluralRouter), "",
|
|
|
|
|
fmt.Errorf("tenant can not delete router"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if obj, err := o.Get(user, resource, name, ""); err != nil {
|
|
|
|
|
klog.Error(err)
|
|
|
|
|
return err
|
|
|
|
|
} else {
|
|
|
|
|
if err := authorizer(user, obj); err != nil {
|
|
|
|
|
klog.Error(err)
|
|
|
|
|
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 v2beta2.ResourcesPluralConfig:
|
|
|
|
|
return o.ksClient.NotificationV2beta2().Configs().Delete(context.Background(), name, v1.DeleteOptions{})
|
|
|
|
|
case v2beta2.ResourcesPluralReceiver:
|
|
|
|
|
return o.ksClient.NotificationV2beta2().Receivers().Delete(context.Background(), name, v1.DeleteOptions{})
|
|
|
|
|
case v2beta2.ResourcesPluralRouter:
|
|
|
|
|
return o.ksClient.NotificationV2beta2().Routers().Delete(context.Background(), name, v1.DeleteOptions{})
|
|
|
|
|
case v2beta2.ResourcesPluralSilence:
|
|
|
|
|
return o.ksClient.NotificationV2beta2().Silences().Delete(context.Background(), name, v1.DeleteOptions{})
|
|
|
|
|
case "secrets":
|
|
|
|
|
return o.k8sClient.CoreV1().Secrets(constants.NotificationSecretNamespace).Delete(context.Background(), name, v1.DeleteOptions{})
|
|
|
|
|
default:
|
|
|
|
|
@@ -195,27 +316,56 @@ func (o *operator) Delete(user, resource, name string) error {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UpdateV2beta1 an object of version v2beta1, 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) UpdateV2beta1(user, resource, name string, obj runtime.Object) (runtime.Object, error) {
|
|
|
|
|
return o.update(user, resource, V2beta1, name, obj)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
|
return o.update(user, resource, V2beta2, name, obj)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *operator) update(user, resource, version, name string, obj runtime.Object) (runtime.Object, error) {
|
|
|
|
|
|
|
|
|
|
if user != "" && resource == v2beta2.ResourcesPluralRouter {
|
|
|
|
|
return nil, errors.NewForbidden(v2beta2.Resource(v2beta2.ResourcesPluralRouter), "",
|
|
|
|
|
fmt.Errorf("tenant can not update router"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := appendLabel(user, resource, obj); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if old, err := o.Get(user, resource, name, ""); err != nil {
|
|
|
|
|
klog.Error(err)
|
|
|
|
|
return nil, err
|
|
|
|
|
} else {
|
|
|
|
|
if err := authorizer(user, old); err != nil {
|
|
|
|
|
klog.Error(err)
|
|
|
|
|
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 v2beta2.ResourcesPluralConfig:
|
|
|
|
|
if version == V2beta1 {
|
|
|
|
|
return o.ksClient.NotificationV2beta1().Configs().Update(context.Background(), obj.(*v2beta1.Config), v1.UpdateOptions{})
|
|
|
|
|
} else {
|
|
|
|
|
return o.ksClient.NotificationV2beta2().Configs().Update(context.Background(), obj.(*v2beta2.Config), v1.UpdateOptions{})
|
|
|
|
|
}
|
|
|
|
|
case v2beta2.ResourcesPluralReceiver:
|
|
|
|
|
if version == V2beta1 {
|
|
|
|
|
return o.ksClient.NotificationV2beta1().Receivers().Update(context.Background(), obj.(*v2beta1.Receiver), v1.UpdateOptions{})
|
|
|
|
|
} else {
|
|
|
|
|
return o.ksClient.NotificationV2beta2().Receivers().Update(context.Background(), obj.(*v2beta2.Receiver), v1.UpdateOptions{})
|
|
|
|
|
}
|
|
|
|
|
case v2beta2.ResourcesPluralRouter:
|
|
|
|
|
return o.ksClient.NotificationV2beta2().Routers().Update(context.Background(), obj.(*v2beta2.Router), v1.UpdateOptions{})
|
|
|
|
|
case v2beta2.ResourcesPluralSilence:
|
|
|
|
|
return o.ksClient.NotificationV2beta2().Silences().Update(context.Background(), obj.(*v2beta2.Silence), v1.UpdateOptions{})
|
|
|
|
|
case "secrets":
|
|
|
|
|
return o.k8sClient.CoreV1().Secrets(constants.NotificationSecretNamespace).Update(context.Background(), obj.(*corev1.Secret), v1.UpdateOptions{})
|
|
|
|
|
default:
|
|
|
|
|
@@ -223,13 +373,31 @@ func (o *operator) Update(user, resource, name string, obj runtime.Object) (runt
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *operator) GetObject(resource string) runtime.Object {
|
|
|
|
|
func (o *operator) GetObject(resource, version string) runtime.Object {
|
|
|
|
|
|
|
|
|
|
switch resource {
|
|
|
|
|
case v2beta1.ResourcesPluralConfig:
|
|
|
|
|
return &v2beta1.Config{}
|
|
|
|
|
case v2beta1.ResourcesPluralReceiver:
|
|
|
|
|
return &v2beta1.Receiver{}
|
|
|
|
|
case v2beta2.ResourcesPluralConfig:
|
|
|
|
|
if version == V2beta1 {
|
|
|
|
|
return &v2beta1.Config{}
|
|
|
|
|
} else {
|
|
|
|
|
return &v2beta2.Config{}
|
|
|
|
|
}
|
|
|
|
|
case v2beta2.ResourcesPluralReceiver:
|
|
|
|
|
if version == V2beta1 {
|
|
|
|
|
return &v2beta1.Receiver{}
|
|
|
|
|
} else {
|
|
|
|
|
return &v2beta2.Receiver{}
|
|
|
|
|
}
|
|
|
|
|
case v2beta2.ResourcesPluralRouter:
|
|
|
|
|
if version == V2beta1 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return &v2beta2.Router{}
|
|
|
|
|
case v2beta2.ResourcesPluralSilence:
|
|
|
|
|
if version == V2beta1 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return &v2beta2.Silence{}
|
|
|
|
|
case Secret:
|
|
|
|
|
return &corev1.Secret{}
|
|
|
|
|
default:
|
|
|
|
|
@@ -237,36 +405,105 @@ func (o *operator) GetObject(resource string) runtime.Object {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *operator) IsKnownResource(resource, subresource string) bool {
|
|
|
|
|
func (o *operator) IsKnownResource(resource, version, subresource string) bool {
|
|
|
|
|
|
|
|
|
|
if obj := o.GetObject(resource); obj == nil {
|
|
|
|
|
if obj := o.GetObject(resource, version); obj == nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res := false
|
|
|
|
|
// "" means get all types of the config or receiver.
|
|
|
|
|
if subresource != "dingtalk" &&
|
|
|
|
|
subresource != "email" &&
|
|
|
|
|
subresource != "slack" &&
|
|
|
|
|
subresource != "webhook" &&
|
|
|
|
|
subresource != "wechat" &&
|
|
|
|
|
subresource != "" {
|
|
|
|
|
return false
|
|
|
|
|
if subresource == "dingtalk" ||
|
|
|
|
|
subresource == "email" ||
|
|
|
|
|
subresource == "slack" ||
|
|
|
|
|
subresource == "webhook" ||
|
|
|
|
|
subresource == "wechat" ||
|
|
|
|
|
subresource == "" {
|
|
|
|
|
res = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
if version == V2beta2 && subresource == "feishu" {
|
|
|
|
|
res = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *operator) Verify(request *restful.Request, response *restful.Response) {
|
|
|
|
|
if o.options == nil || len(o.options.Endpoint) == 0 {
|
|
|
|
|
_ = response.WriteAsJson(Result{
|
|
|
|
|
http.StatusInternalServerError,
|
|
|
|
|
"Cannot find Notification Manager endpoint",
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reqBody, err := ioutil.ReadAll(request.Request.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
klog.Error(err)
|
|
|
|
|
_ = response.WriteHeaderAndEntity(http.StatusBadRequest, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data := Data{}
|
|
|
|
|
err = json.Unmarshal(reqBody, &data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
_ = response.WriteHeaderAndEntity(http.StatusBadRequest, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
receiver := data.Receiver
|
|
|
|
|
user := request.PathParameter("user")
|
|
|
|
|
|
|
|
|
|
if err := authorizer(user, &receiver); err != nil {
|
|
|
|
|
klog.Error(err)
|
|
|
|
|
_ = response.WriteHeaderAndEntity(http.StatusBadRequest, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req, err := http.NewRequest("POST", fmt.Sprintf("%s%s", o.options.Endpoint, VerificationAPIPath), bytes.NewReader(reqBody))
|
|
|
|
|
if err != nil {
|
|
|
|
|
klog.Error(err)
|
|
|
|
|
_ = response.WriteHeaderAndEntity(http.StatusInternalServerError, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
req.Header = request.Request.Header
|
|
|
|
|
|
|
|
|
|
client := &http.Client{}
|
|
|
|
|
resp, err := client.Do(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
klog.Error(err)
|
|
|
|
|
_ = response.WriteHeaderAndEntity(http.StatusInternalServerError, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer func() {
|
|
|
|
|
_ = resp.Body.Close()
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
klog.Error(err)
|
|
|
|
|
// return 500
|
|
|
|
|
_ = response.WriteHeaderAndEntity(http.StatusInternalServerError, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response.AddHeader(restful.HEADER_ContentType, restful.MIME_JSON)
|
|
|
|
|
response.WriteHeader(http.StatusOK)
|
|
|
|
|
_, _ = response.Write(body)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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), "",
|
|
|
|
|
return errors.NewForbidden(v2beta2.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), "",
|
|
|
|
|
return errors.NewForbidden(v2beta2.Resource(obj.GetObjectKind().GroupVersionKind().GroupKind().Kind), "",
|
|
|
|
|
fmt.Errorf("object is not a global object"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -282,12 +519,12 @@ func isOwner(user string, obj interface{}) bool {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return accessor.GetLabels()["user"] == user
|
|
|
|
|
return accessor.GetLabels()["user"] == user && accessor.GetLabels()["type"] == "tenant"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isConfig(obj runtime.Object) bool {
|
|
|
|
|
switch obj.(type) {
|
|
|
|
|
case *v2beta1.Config:
|
|
|
|
|
case *v2beta1.Config, *v2beta2.Config:
|
|
|
|
|
return true
|
|
|
|
|
default:
|
|
|
|
|
return false
|
|
|
|
|
@@ -342,15 +579,17 @@ func appendLabel(user, resource string, obj runtime.Object) error {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func clean(obj interface{}, resource, subresource string) runtime.Object {
|
|
|
|
|
if resource == v2beta1.ResourcesPluralConfig {
|
|
|
|
|
config := obj.(*v2beta1.Config)
|
|
|
|
|
if resource == v2beta2.ResourcesPluralConfig {
|
|
|
|
|
config := obj.(*v2beta2.Config)
|
|
|
|
|
newConfig := config.DeepCopy()
|
|
|
|
|
newConfig.Spec = v2beta1.ConfigSpec{}
|
|
|
|
|
newConfig.Spec = v2beta2.ConfigSpec{}
|
|
|
|
|
switch subresource {
|
|
|
|
|
case "dingtalk":
|
|
|
|
|
newConfig.Spec.DingTalk = config.Spec.DingTalk
|
|
|
|
|
case "email":
|
|
|
|
|
newConfig.Spec.Email = config.Spec.Email
|
|
|
|
|
case "feishu":
|
|
|
|
|
newConfig.Spec.Feishu = config.Spec.Feishu
|
|
|
|
|
case "slack":
|
|
|
|
|
newConfig.Spec.Slack = config.Spec.Slack
|
|
|
|
|
case "webhook":
|
|
|
|
|
@@ -366,15 +605,17 @@ func clean(obj interface{}, resource, subresource string) runtime.Object {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return newConfig
|
|
|
|
|
} else {
|
|
|
|
|
receiver := obj.(*v2beta1.Receiver)
|
|
|
|
|
} else if resource == v2beta2.ResourcesPluralReceiver {
|
|
|
|
|
receiver := obj.(*v2beta2.Receiver)
|
|
|
|
|
newReceiver := receiver.DeepCopy()
|
|
|
|
|
newReceiver.Spec = v2beta1.ReceiverSpec{}
|
|
|
|
|
newReceiver.Spec = v2beta2.ReceiverSpec{}
|
|
|
|
|
switch subresource {
|
|
|
|
|
case "dingtalk":
|
|
|
|
|
newReceiver.Spec.DingTalk = receiver.Spec.DingTalk
|
|
|
|
|
case "email":
|
|
|
|
|
newReceiver.Spec.Email = receiver.Spec.Email
|
|
|
|
|
case "feishu":
|
|
|
|
|
newReceiver.Spec.Feishu = receiver.Spec.Feishu
|
|
|
|
|
case "slack":
|
|
|
|
|
newReceiver.Spec.Slack = receiver.Spec.Slack
|
|
|
|
|
case "webhook":
|
|
|
|
|
@@ -390,5 +631,22 @@ func clean(obj interface{}, resource, subresource string) runtime.Object {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return newReceiver
|
|
|
|
|
} else {
|
|
|
|
|
return obj.(runtime.Object)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func convert(obj runtime.Object) runtime.Object {
|
|
|
|
|
switch obj := obj.(type) {
|
|
|
|
|
case *v2beta2.Config:
|
|
|
|
|
dst := &v2beta1.Config{}
|
|
|
|
|
_ = obj.ConvertTo(dst)
|
|
|
|
|
return dst
|
|
|
|
|
case *v2beta2.Receiver:
|
|
|
|
|
dst := &v2beta1.Receiver{}
|
|
|
|
|
_ = obj.ConvertTo(dst)
|
|
|
|
|
return dst
|
|
|
|
|
default:
|
|
|
|
|
return obj
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|