Skip to content

Commit

Permalink
feat: repository controller supports sync serviceaccount
Browse files Browse the repository at this point in the history
  • Loading branch information
0xff-dev committed Aug 16, 2023
1 parent 4910337 commit aa4c4d6
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 3 deletions.
107 changes: 107 additions & 0 deletions api/v1alpha1/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,18 @@ limitations under the License.
package v1alpha1

import (
"context"
"fmt"
"os"
"strings"

v1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/utils/env"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
)

const (
Expand All @@ -31,6 +40,15 @@ const (

ComponentRepositoryLabel = "kubebb.component.repository"
RepositoryTypeLabel = "kubebb.repository.type"

RatingServiceAccountEnv = "RATING_SERVICEACCOUNT"
RatingClusterRoleEnv = "RATING_CLUSTERROLE"
RatingClusterRoleBindingEnv = "RATING_CLUSTERROLEBINDING"
RatingEnableEnv = "RATING_ENABLE"

DefaultRatingServiaceAccount = "rating-serviceaccount"
DefaultRatingClusterRole = "rating-clusterrole"
DefaultRatingClusterRoleBinding = "rating-clusterrolebinding"
)

// NamespacedName return the namespaced name of the repository in string format
Expand Down Expand Up @@ -58,3 +76,92 @@ func GetImageOverridePath() []string {
}
return strings.Split(v, ":")
}

func EnsureRatingResources() {
if !RatingEnabled() {
return
}

cfg := config.GetConfigOrDie()
c := kubernetes.NewForConfigOrDie(cfg)
clusterRoleName := GetRatingClusterRole()
if _, err := c.RbacV1().ClusterRoles().Get(context.Background(), clusterRoleName, metav1.GetOptions{}); err != nil {
panic(err)
}

clusterRolebingName := GetRatingClusterRoleBinding()
if _, err := c.RbacV1().ClusterRoleBindings().Get(context.Background(), clusterRolebingName, metav1.GetOptions{}); err != nil {
panic(err)
}
}

func RatingEnabled() bool {
r, _ := env.GetBool(RatingEnableEnv, false)
return r
}

func GetRatingServiceAccount() string {
return env.GetString(RatingServiceAccountEnv, DefaultRatingServiaceAccount)
}

func GetRatingClusterRole() string {
return env.GetString(RatingClusterRoleEnv, DefaultRatingClusterRole)
}

func GetRatingClusterRoleBinding() string {
return env.GetString(RatingClusterRoleBindingEnv, DefaultRatingClusterRoleBinding)
}

func AddSubjectToClusterRoleBinding(ctx context.Context, c client.Client, namespace string) error {
if !RatingEnabled() {
return nil
}

clusterRoleBinding := GetRatingClusterRoleBinding()
serviceAccount := GetRatingServiceAccount()
crb := v1.ClusterRoleBinding{}
if err := c.Get(ctx, types.NamespacedName{Name: clusterRoleBinding}, &crb); err != nil {
return err
}

add := true
for _, sub := range crb.Subjects {
if sub.Kind == "ServiceAccount" && sub.Name == serviceAccount && sub.Namespace == namespace {
add = false
break
}
}
if add {
crb.Subjects = append(crb.Subjects, v1.Subject{Kind: "ServiceAccount", Name: serviceAccount, Namespace: namespace})
return c.Update(ctx, &crb)
}
return nil
}

func RemoveSubjectFromClusterRoleBinding(ctx context.Context, c client.Client, namespace string) error {
if !RatingEnabled() {
return nil
}

clusterRoleBinding := GetRatingClusterRoleBinding()
serviceAccount := GetRatingServiceAccount()
crb := v1.ClusterRoleBinding{}
if err := c.Get(ctx, types.NamespacedName{Name: clusterRoleBinding}, &crb); err != nil {
return err
}

index, length := 0, len(crb.Subjects)
for idx := 0; idx < length; idx++ {
if crb.Subjects[idx].Kind == "ServiceAccount" && crb.Subjects[idx].Name == serviceAccount && crb.Subjects[idx].Namespace == namespace {
continue
}
crb.Subjects[index] = crb.Subjects[idx]
index++
}
if index != length {
crb.Subjects = crb.Subjects[:index]
return c.Update(ctx, &crb)
}

return nil
}
66 changes: 63 additions & 3 deletions controllers/repository_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,18 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"

corev1alpha1 "github.com/kubebb/core/api/v1alpha1"
"github.com/kubebb/core/pkg/repository"
Expand Down Expand Up @@ -162,6 +166,10 @@ func (r *RepositoryReconciler) UpdateRepository(ctx context.Context, logger logr
return false, err
}

if err := r.EnsureRatingServiceAccount(ctx, instance.GetNamespace()); err != nil {
return false, err
}

return true, nil
}

Expand Down Expand Up @@ -192,7 +200,7 @@ func (r *RepositoryReconciler) OnRepositryUpdate(u event.UpdateEvent) bool {

// SetupWithManager sets up the controller with the Manager.
func (r *RepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
manager := ctrl.NewControllerManagedBy(mgr).
For(&corev1alpha1.Repository{}, builder.WithPredicates(predicate.Funcs{
CreateFunc: func(ce event.CreateEvent) bool {
obj := ce.Object.(*corev1alpha1.Repository)
Expand All @@ -205,6 +213,58 @@ func (r *RepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
r.Recorder.Eventf(obj, v1.EventTypeNormal, "Deleted", "delete repository %s", obj.GetName())
return true
},
})).
Complete(r)
})).Watches(&source.Kind{Type: &v1.ServiceAccount{}}, handler.Funcs{})

if corev1alpha1.RatingEnabled() {
manager = manager.Watches(&source.Kind{Type: &v1.ServiceAccount{}}, handler.Funcs{
CreateFunc: func(ce event.CreateEvent, _ workqueue.RateLimitingInterface) {
sa := ce.Object.(*v1.ServiceAccount)
if sa.Name == corev1alpha1.GetRatingServiceAccount() {
_ = corev1alpha1.AddSubjectToClusterRoleBinding(context.Background(), r.Client, sa.GetNamespace())
}
},
UpdateFunc: func(ue event.UpdateEvent, _ workqueue.RateLimitingInterface) {
sa := ue.ObjectNew.(*v1.ServiceAccount)
if sa.Name == corev1alpha1.GetRatingServiceAccount() {
_ = corev1alpha1.AddSubjectToClusterRoleBinding(context.Background(), r.Client, sa.GetNamespace())
}
},
DeleteFunc: func(de event.DeleteEvent, _ workqueue.RateLimitingInterface) {
sa := de.Object.(*v1.ServiceAccount)
if sa.Name == corev1alpha1.GetRatingServiceAccount() {
repoList := corev1alpha1.RepositoryList{}
if err := r.List(context.Background(), &repoList, client.InNamespace(sa.GetNamespace())); err == nil {
if len(repoList.Items) > 0 {
_ = r.EnsureRatingServiceAccount(context.Background(), sa.GetNamespace())
} else {
_ = corev1alpha1.RemoveSubjectFromClusterRoleBinding(context.Background(), r.Client, sa.GetNamespace())
}
}
}
},
})
}
return manager.Complete(r)
}

func (r RepositoryReconciler) EnsureRatingServiceAccount(ctx context.Context, namespace string) error {
if !corev1alpha1.RatingEnabled() {
return nil
}

ratingUsedServiceAccount := corev1alpha1.GetRatingServiceAccount()
sa := v1.ServiceAccount{}
if err := r.Get(ctx, types.NamespacedName{Namespace: namespace, Name: ratingUsedServiceAccount}, &sa); err != nil {
if !errors.IsNotFound(err) {
return err
}

sa.Name = ratingUsedServiceAccount
sa.Namespace = namespace
if err = r.Create(ctx, &sa); err != nil && !errors.IsConflict(err) {
return err
}
}

return nil
}
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func main() {
opts.BindFlags(flag.CommandLine)
flag.Parse()

corev1alpha1.EnsureRatingResources()
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

var err error
Expand Down

0 comments on commit aa4c4d6

Please sign in to comment.