Skip to content

Commit

Permalink
Merge pull request #231 from wanjunlei/sidecar
Browse files Browse the repository at this point in the history
add tenant sidecar for ks v4.0
  • Loading branch information
benjaminhuo authored Dec 11, 2023
2 parents f8457c9 + 0fabdf4 commit 6dcf2d4
Show file tree
Hide file tree
Showing 14 changed files with 754 additions and 4 deletions.
8 changes: 8 additions & 0 deletions config/bundle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,10 @@ spec:
type: array
type: object
type: object
annotations:
additionalProperties:
type: string
type: object
args:
description: Arguments to the entrypoint. The docker image's CMD is
used if this is not provided.
Expand Down Expand Up @@ -2282,6 +2286,10 @@ spec:
description: Image pull policy. One of Always, Never, IfNotPresent.
Defaults to IfNotPresent if not specified
type: string
labels:
additionalProperties:
type: string
type: object
nodeSelector:
additionalProperties:
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,10 @@ spec:
type: array
type: object
type: object
annotations:
additionalProperties:
type: string
type: object
args:
description: Arguments to the entrypoint. The docker image's CMD is
used if this is not provided.
Expand Down Expand Up @@ -1413,6 +1417,10 @@ spec:
description: Image pull policy. One of Always, Never, IfNotPresent.
Defaults to IfNotPresent if not specified
type: string
labels:
additionalProperties:
type: string
type: object
nodeSelector:
additionalProperties:
type: string
Expand Down
11 changes: 7 additions & 4 deletions controllers/notificationmanager_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,17 @@ func (r *NotificationManagerReconciler) mutateDeployment(deploy *appsv1.Deployme
nm.Spec.ServiceAccountName = defaultServiceAccountName
}

deploy.ObjectMeta.Labels = *r.makeCommonLabels(nm)
deploy.Spec.Replicas = nm.Spec.Replicas
podLabels := deploy.ObjectMeta.Labels
podLabels := *r.makeCommonLabels(nm)
for k, v := range nm.Spec.Labels {
podLabels[k] = v
}
deploy.Spec.Selector = &metav1.LabelSelector{
MatchLabels: podLabels,
MatchLabels: *r.makeCommonLabels(nm),
}
deploy.Spec.Template.ObjectMeta = metav1.ObjectMeta{
Labels: podLabels,
Labels: podLabels,
Annotations: nm.Spec.Annotations,
}
deploy.Spec.Template.Spec.NodeSelector = nm.Spec.NodeSelector
deploy.Spec.Template.Spec.Affinity = nm.Spec.Affinity
Expand Down
8 changes: 8 additions & 0 deletions helm/crds/bundle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,10 @@ spec:
type: array
type: object
type: object
annotations:
additionalProperties:
type: string
type: object
args:
description: Arguments to the entrypoint. The docker image's CMD is
used if this is not provided.
Expand Down Expand Up @@ -2282,6 +2286,10 @@ spec:
description: Image pull policy. One of Always, Never, IfNotPresent.
Defaults to IfNotPresent if not specified
type: string
labels:
additionalProperties:
type: string
type: object
nodeSelector:
additionalProperties:
type: string
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/v2beta2/notificationmanager_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ type NotificationManagerSpec struct {
RoutePolicy string `json:"routePolicy,omitempty"`
// Template used to define information about templates
Template *Template `json:"template,omitempty"`

Annotations map[string]string `json:"annotations,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
}

type ReceiversSpec struct {
Expand Down
15 changes: 15 additions & 0 deletions pkg/apis/v2beta2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions sidecar/kubesphere/4.0.0/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Use of this source code is governed by a Apache license
# that can be found in the LICENSE file.

FROM golang:1.20 as tenant-sidecar

COPY / /
WORKDIR /
ENV GOPROXY=https://goproxy.io
RUN CGO_ENABLED=0 GO111MODULE=on go build -a -o tenant-sidecar main.go backend.go

FROM kubesphere/distroless-static:nonroot
WORKDIR /
COPY --from=tenant-sidecar /tenant-sidecar .
USER nonroot:nonroot

ENTRYPOINT ["/tenant-sidecar"]

24 changes: 24 additions & 0 deletions sidecar/kubesphere/4.0.0/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2018 The KubeSphere Authors. All rights reserved.
# Use of this source code is governed by a Apache license
# that can be found in the LICENSE file.

IMG ?= leiwanjun/notification-tenant-sidecar:v4.0.0
AMD64 ?= -amd64

all: docker-build

# Build tenant sidecar binary
tenant-sidecar:
go build -o tenant-sidecar main.go backend.go

# Build the docker image
docker-build:
docker buildx build --platform linux/amd64,linux/arm64 --push -f Dockerfile -t ${IMG} .

# Build the docker image for arm64
docker-build-amd64:
docker build -f Dockerfile -t ${IMG}${AMD64} .

# Push the docker image
push-amd64:
docker push ${IMG}${AMD64}
3 changes: 3 additions & 0 deletions sidecar/kubesphere/4.0.0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Notification tenant sidecar

It is a tenant sidecar for kubesphere v4.0.0.
177 changes: 177 additions & 0 deletions sidecar/kubesphere/4.0.0/backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package main

import (
"context"
"time"

"k8s.io/api/authorization/v1beta1"
v1 "k8s.io/api/core/v1"
"k8s.io/klog"
iamv1beta1 "kubesphere.io/api/iam/v1beta1"
"kubesphere.io/client-go/rest"
)

type Backend struct {
ksClient *rest.RESTClient
tenants map[string]map[string]string

interval time.Duration
}

func NewBackend(host, username, password string, interval time.Duration) (*Backend, error) {
var config *rest.Config
if username != "" && password != "" {
config = &rest.Config{
Host: host,
Username: username,
Password: password,
}
} else {
config = &rest.Config{
Host: host,
BearerTokenFile: "/var/run/secrets/kubesphere.io/serviceaccount/token",
}
}

c, err := rest.UnversionedRESTClientFor(config)
if err != nil {
return nil, err
}

return &Backend{
ksClient: c,
tenants: make(map[string]map[string]string),
interval: interval,
}, err
}

func (b *Backend) FromNamespace(ns string) []string {

m, ok := b.tenants[ns]
if !ok {
return nil
}

array := make([]string, 0)
for k := range m {
array = append(array, k)
}
return array
}

func (b *Backend) Run() {
b.reload()

ticker := time.NewTicker(b.interval)
go func() {
for {
select {
case <-ticker.C:
b.reload()
}
}
}()
}

func (b *Backend) reload() {
klog.Info("start reload tenant")
defer func() {
klog.Info("end reload tenant")
}()

users, err := b.listUsers()
if err != nil {
klog.Errorf("list users error, %s", err.Error())
}

namespaces, err := b.listNamespaces()
if err != nil {
klog.Errorf("list namespaces error, %s", err.Error())
}

m := make(map[string]map[string]string)
for _, namespace := range namespaces {
for _, user := range users {
allow, err := b.canAccess(user, namespace)
if err != nil {
klog.Errorf("get access view for user %s, namespace %s error, %s", err.Error())
return
}

if allow {
array, ok := m[namespace]
if !ok {
array = make(map[string]string)
}
array[user] = ""
m[namespace] = array
}
}
}

b.tenants = m
}

func (b *Backend) listUsers() ([]string, error) {
res := b.ksClient.Get().AbsPath("/kapis/iam.kubesphere.io/v1beta1/users").Do(context.Background())
if err := res.Error(); err != nil {
return nil, err
}
userList := &iamv1beta1.UserList{}
err := res.Into(userList)
if err != nil {
return nil, err
}

var users []string
for _, user := range userList.Items {
users = append(users, user.Name)
}

return users, nil
}

func (b *Backend) listNamespaces() ([]string, error) {
res := b.ksClient.Get().AbsPath("/api/v1/namespaces").Do(context.Background())
if err := res.Error(); err != nil {
return nil, err
}
namespacesList := &v1.NamespaceList{}
err := res.Into(namespacesList)
if err != nil {
return nil, err
}

var namespaces []string
for _, n := range namespacesList.Items {
namespaces = append(namespaces, n.Name)
}

return namespaces, nil
}

func (b *Backend) canAccess(user, namespace string) (bool, error) {
subjectAccessReview := &v1beta1.SubjectAccessReview{
Spec: v1beta1.SubjectAccessReviewSpec{
ResourceAttributes: &v1beta1.ResourceAttributes{
Namespace: namespace,
Verb: "get",
Group: "notification.kubesphere.io",
Version: "v2beta2",
Resource: "receivenotification",
},
NonResourceAttributes: nil,
User: user, // "X-Remote-User" request header
Groups: []string{}, // "X-Remote-Group" request header
},
}

if err := b.ksClient.Post().AbsPath("/kapis/iam.kubesphere.io/v1beta1/subjectaccessreviews").
Body(subjectAccessReview).
Do(context.Background()).
Into(subjectAccessReview); err != nil {
return false, err
}

return subjectAccessReview.Status.Allowed, nil
}
Loading

0 comments on commit 6dcf2d4

Please sign in to comment.