namespace_controller.go 11.2 KB
Newer Older
H
hongming 已提交
1
/*
H
hongming 已提交
2
Copyright 2019 The KubeSphere Authors.
H
hongming 已提交
3

H
hongming 已提交
4 5 6
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
H
hongming 已提交
7

H
hongming 已提交
8
    http://www.apache.org/licenses/LICENSE-2.0
H
hongming 已提交
9

H
hongming 已提交
10 11 12 13 14
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.
H
hongming 已提交
15 16 17 18 19
*/

package namespace

import (
20
	"bytes"
H
hongming 已提交
21
	"context"
22
	"fmt"
H
hongming 已提交
23
	"github.com/go-logr/logr"
J
Jeff 已提交
24
	appsv1 "k8s.io/api/apps/v1"
H
hongming 已提交
25
	corev1 "k8s.io/api/core/v1"
26
	rbacv1 "k8s.io/api/rbac/v1"
H
hongming 已提交
27 28
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
	"k8s.io/apimachinery/pkg/labels"
H
hongming 已提交
30
	"k8s.io/apimachinery/pkg/types"
31
	"k8s.io/apimachinery/pkg/util/yaml"
H
hongming 已提交
32 33
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/record"
34 35
	iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
	tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
H
hongming 已提交
36
	"kubesphere.io/kubesphere/pkg/constants"
H
hongming 已提交
37
	controllerutils "kubesphere.io/kubesphere/pkg/controller/utils/controller"
H
hongming 已提交
38
	"kubesphere.io/kubesphere/pkg/utils/k8sutil"
H
hongming 已提交
39
	"kubesphere.io/kubesphere/pkg/utils/sliceutil"
40
	"reflect"
H
hongming 已提交
41
	ctrl "sigs.k8s.io/controller-runtime"
H
hongming 已提交
42 43 44 45 46
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

H
hongming 已提交
47 48 49
const (
	controllerName = "namespace-controller"
)
H
hongming 已提交
50

H
hongming 已提交
51 52 53 54 55 56
// Reconciler reconciles a Namespace object
type Reconciler struct {
	client.Client
	Logger                  logr.Logger
	Recorder                record.EventRecorder
	MaxConcurrentReconciles int
H
hongming 已提交
57 58
}

H
hongming 已提交
59 60 61
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
	if r.Client == nil {
		r.Client = mgr.GetClient()
H
hongming 已提交
62
	}
H
hongming 已提交
63 64
	if r.Logger == nil {
		r.Logger = ctrl.Log.WithName("controllers").WithName(controllerName)
H
hongming 已提交
65
	}
H
hongming 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78
	if r.Recorder == nil {
		r.Recorder = mgr.GetEventRecorderFor(controllerName)
	}
	if r.MaxConcurrentReconciles <= 0 {
		r.MaxConcurrentReconciles = 1
	}
	return ctrl.NewControllerManagedBy(mgr).
		Named(controllerName).
		WithOptions(controller.Options{
			MaxConcurrentReconciles: r.MaxConcurrentReconciles,
		}).
		For(&corev1.Namespace{}).
		Complete(r)
H
hongming 已提交
79 80
}

H
hongming 已提交
81 82 83 84 85 86 87 88 89 90 91
// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=tenant.kubesphere.io,resources=workspaces,verbs=get;list;watch
// +kubebuilder:rbac:groups=iam.kubesphere.io,resources=rolebases,verbs=get;list;watch
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles,verbs=get;list;watch
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=get;list;watch;create;update;patch;delete
func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
	logger := r.Logger.WithValues("namespace", req.NamespacedName)
	rootCtx := context.Background()
	namespace := &corev1.Namespace{}
	if err := r.Get(rootCtx, req.NamespacedName, namespace); err != nil {
		return ctrl.Result{}, client.IgnoreNotFound(err)
H
hongming 已提交
92
	}
H
hongming 已提交
93 94
	// name of your custom finalizer
	finalizer := "finalizers.kubesphere.io/namespaces"
J
Jeff 已提交
95

H
hongming 已提交
96
	if namespace.ObjectMeta.DeletionTimestamp.IsZero() {
H
hongming 已提交
97 98
		// The object is not being deleted, so if it does not have our finalizer,
		// then lets add the finalizer and update the object.
H
hongming 已提交
99 100 101 102 103 104 105 106
		if !sliceutil.HasString(namespace.ObjectMeta.Finalizers, finalizer) {
			// create only once, ignore already exists error
			if err := r.initCreatorRoleBinding(rootCtx, logger, namespace); err != nil {
				return ctrl.Result{}, err
			}
			namespace.ObjectMeta.Finalizers = append(namespace.ObjectMeta.Finalizers, finalizer)
			if namespace.Labels == nil {
				namespace.Labels = make(map[string]string)
D
Duan Jiong 已提交
107
			}
H
hongming 已提交
108 109 110 111
			// used for NetworkPolicyPeer.NamespaceSelector
			namespace.Labels[constants.NamespaceLabelKey] = namespace.Name
			if err := r.Update(rootCtx, namespace); err != nil {
				return ctrl.Result{}, err
H
hongming 已提交
112
			}
J
Jeff 已提交
113
		}
H
hongming 已提交
114 115
	} else {
		// The object is being deleted
H
hongming 已提交
116 117 118
		if sliceutil.HasString(namespace.ObjectMeta.Finalizers, finalizer) {
			if err := r.deleteRouter(rootCtx, logger, namespace.Name); err != nil {
				return ctrl.Result{}, err
H
hongming 已提交
119 120
			}
			// remove our finalizer from the list and update it.
H
hongming 已提交
121
			namespace.ObjectMeta.Finalizers = sliceutil.RemoveString(namespace.ObjectMeta.Finalizers, func(item string) bool {
H
hongming 已提交
122 123
				return item == finalizer
			})
H
hongming 已提交
124 125
			if err := r.Update(rootCtx, namespace); err != nil {
				return ctrl.Result{}, err
H
hongming 已提交
126
			}
H
hongming 已提交
127
		}
H
hongming 已提交
128
		// Our finalizer has finished, so the reconciler can do nothing.
H
hongming 已提交
129
		return ctrl.Result{}, nil
H
hongming 已提交
130 131
	}

H
hongming 已提交
132
	// initialize subresource if created by kubesphere
H
hongming 已提交
133 134 135
	if workspace := namespace.Labels[tenantv1alpha1.WorkspaceLabel]; workspace != "" {
		if err := r.bindWorkspace(rootCtx, logger, namespace); err != nil {
			return ctrl.Result{}, err
H
hongming 已提交
136
		}
H
hongming 已提交
137 138
		if err := r.initRoles(rootCtx, logger, namespace); err != nil {
			return ctrl.Result{}, err
H
hongming 已提交
139 140
		}
	} else {
H
hongming 已提交
141 142 143
		if err := r.unbindWorkspace(rootCtx, logger, namespace); err != nil {
			return ctrl.Result{}, err
		}
H
hongming 已提交
144 145
	}

H
hongming 已提交
146 147
	r.Recorder.Event(namespace, corev1.EventTypeNormal, controllerutils.SuccessSynced, controllerutils.MessageResourceSynced)
	return ctrl.Result{}, nil
H
hongming 已提交
148 149
}

H
hongming 已提交
150
func (r *Reconciler) bindWorkspace(ctx context.Context, logger logr.Logger, namespace *corev1.Namespace) error {
151
	workspace := &tenantv1alpha1.Workspace{}
H
hongming 已提交
152 153 154 155
	if err := r.Get(ctx, types.NamespacedName{Name: namespace.Labels[constants.WorkspaceLabelKey]}, workspace); err != nil {
		// remove existed owner reference if workspace not found
		if errors.IsNotFound(err) && k8sutil.IsControlledBy(namespace.OwnerReferences, tenantv1alpha1.ResourceKindWorkspace, "") {
			return r.unbindWorkspace(ctx, logger, namespace)
H
hongming 已提交
156
		}
H
hongming 已提交
157 158
		// skip if workspace not found
		return client.IgnoreNotFound(err)
H
hongming 已提交
159
	}
H
hongming 已提交
160
	// owner reference not match workspace label
H
hongming 已提交
161
	if !metav1.IsControlledBy(namespace, workspace) {
H
hongming 已提交
162 163 164 165
		namespace := namespace.DeepCopy()
		namespace.OwnerReferences = k8sutil.RemoveWorkspaceOwnerReference(namespace.OwnerReferences)
		if err := controllerutil.SetControllerReference(workspace, namespace, scheme.Scheme); err != nil {
			logger.Error(err, "set controller reference failed")
H
hongming 已提交
166 167
			return err
		}
H
hongming 已提交
168 169 170
		logger.V(4).Info("update namespace owner reference", "workspace", workspace.Name)
		if err := r.Update(ctx, namespace); err != nil {
			logger.Error(err, "update namespace failed")
H
hongming 已提交
171 172 173 174 175 176
			return err
		}
	}
	return nil
}

H
hongming 已提交
177
func (r *Reconciler) unbindWorkspace(ctx context.Context, logger logr.Logger, namespace *corev1.Namespace) error {
H
hongming 已提交
178 179
	if k8sutil.IsControlledBy(namespace.OwnerReferences, tenantv1alpha1.ResourceKindWorkspace, "") {
		namespace := namespace.DeepCopy()
H
hongming 已提交
180 181 182 183
		namespace.OwnerReferences = k8sutil.RemoveWorkspaceOwnerReference(namespace.OwnerReferences)
		logger.V(4).Info("remove owner reference", "workspace", namespace.Labels[constants.WorkspaceLabelKey])
		if err := r.Update(ctx, namespace); err != nil {
			logger.Error(err, "update owner reference failed")
H
hongming 已提交
184 185 186 187 188 189
			return err
		}
	}
	return nil
}

H
hongming 已提交
190
func (r *Reconciler) deleteRouter(ctx context.Context, logger logr.Logger, namespace string) error {
J
Jeff 已提交
191
	routerName := constants.IngressControllerPrefix + namespace
H
hongming 已提交
192

J
Jeff 已提交
193
	// delete service first
H
hongming 已提交
194 195
	service := corev1.Service{}
	err := r.Get(ctx, types.NamespacedName{Namespace: constants.IngressControllerNamespace, Name: routerName}, &service)
J
Jeff 已提交
196
	if err != nil {
H
hongming 已提交
197
		return client.IgnoreNotFound(err)
J
Jeff 已提交
198
	}
H
hongming 已提交
199 200
	logger.V(4).Info("delete router service", "namespace", service.Namespace, "service", service.Name)
	err = r.Delete(ctx, &service)
J
Jeff 已提交
201
	if err != nil {
H
hongming 已提交
202
		return client.IgnoreNotFound(err)
J
Jeff 已提交
203 204 205 206
	}

	// delete deployment
	deploy := appsv1.Deployment{}
H
hongming 已提交
207
	err = r.Get(ctx, types.NamespacedName{Namespace: constants.IngressControllerNamespace, Name: routerName}, &deploy)
J
Jeff 已提交
208
	if err != nil {
H
hongming 已提交
209
		logger.Error(err, "delete router deployment failed")
J
Jeff 已提交
210 211 212
		return err
	}

H
hongming 已提交
213 214
	logger.V(4).Info("delete router deployment", "namespace", deploy.Namespace, "deployment", deploy.Name)
	err = r.Delete(ctx, &deploy)
J
Jeff 已提交
215
	if err != nil {
H
hongming 已提交
216
		return client.IgnoreNotFound(err)
J
Jeff 已提交
217 218 219
	}

	return nil
220 221
}

H
hongming 已提交
222 223
func (r *Reconciler) initRoles(ctx context.Context, logger logr.Logger, namespace *corev1.Namespace) error {
	var templates iamv1alpha2.RoleBaseList
224 225 226 227 228 229 230 231 232
	var labelKey string
	// filtering initial roles by label
	if namespace.Labels[constants.DevOpsProjectLabelKey] != "" {
		// scope.kubesphere.io/devops: ""
		labelKey = fmt.Sprintf(iamv1alpha2.ScopeLabelFormat, iamv1alpha2.ScopeDevOps)
	} else {
		// scope.kubesphere.io/namespace: ""
		labelKey = fmt.Sprintf(iamv1alpha2.ScopeLabelFormat, iamv1alpha2.ScopeNamespace)
	}
H
hongming 已提交
233 234 235

	if err := r.List(ctx, &templates, client.MatchingLabelsSelector{Selector: labels.SelectorFromSet(labels.Set{labelKey: ""})}); err != nil {
		logger.Error(err, "list role bases failed")
236 237
		return err
	}
H
hongming 已提交
238
	for _, template := range templates.Items {
239
		var role rbacv1.Role
H
hongming 已提交
240
		if err := yaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(template.Role.Raw), 1024).Decode(&role); err == nil && role.Kind == iamv1alpha2.ResourceKindRole {
241
			var old rbacv1.Role
H
hongming 已提交
242
			if err := r.Client.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: role.Name}, &old); err != nil {
243 244
				if errors.IsNotFound(err) {
					role.Namespace = namespace.Name
H
hongming 已提交
245 246 247
					logger.V(4).Info("init builtin role", "role", role.Name)
					if err := r.Client.Create(ctx, &role); err != nil {
						logger.Error(err, "create role failed")
248 249 250 251 252 253 254 255 256 257 258 259 260
						return err
					}
					continue
				}
			}
			if !reflect.DeepEqual(role.Labels, old.Labels) ||
				!reflect.DeepEqual(role.Annotations, old.Annotations) ||
				!reflect.DeepEqual(role.Rules, old.Rules) {

				old.Labels = role.Labels
				old.Annotations = role.Annotations
				old.Rules = role.Rules

H
hongming 已提交
261 262 263
				logger.V(4).Info("update builtin role", "role", role.Name)
				if err := r.Update(ctx, &old); err != nil {
					logger.Error(err, "update role failed")
264 265
					return err
				}
266
			}
H
hongming 已提交
267 268
		} else if err != nil {
			logger.Error(fmt.Errorf("invalid role base found"), "init roles failed", "name", template.Name)
269 270 271 272 273
		}
	}
	return nil
}

H
hongming 已提交
274
func (r *Reconciler) initCreatorRoleBinding(ctx context.Context, logger logr.Logger, namespace *corev1.Namespace) error {
H
hongming 已提交
275 276 277 278 279
	creator := namespace.Annotations[constants.CreatorAnnotationKey]
	if creator == "" {
		return nil
	}
	var user iamv1alpha2.User
H
hongming 已提交
280 281 282 283 284 285 286
	if err := r.Get(ctx, types.NamespacedName{Name: creator}, &user); err != nil {
		return client.IgnoreNotFound(err)
	}
	creatorRoleBinding := newCreatorRoleBinding(creator, namespace.Name)
	logger.V(4).Info("init creator role binding", "creator", user.Name)
	if err := r.Client.Create(ctx, creatorRoleBinding); err != nil {
		if errors.IsAlreadyExists(err) {
287
			return nil
H
hongming 已提交
288
		}
H
hongming 已提交
289
		logger.Error(err, "create role binding failed")
H
hongming 已提交
290 291
		return err
	}
H
hongming 已提交
292 293
	return nil
}
H
hongming 已提交
294

H
hongming 已提交
295 296
func newCreatorRoleBinding(creator string, namespace string) *rbacv1.RoleBinding {
	return &rbacv1.RoleBinding{
H
hongming 已提交
297 298 299
		ObjectMeta: metav1.ObjectMeta{
			Name:      fmt.Sprintf("%s-%s", creator, iamv1alpha2.NamespaceAdmin),
			Labels:    map[string]string{iamv1alpha2.UserReferenceLabel: creator},
H
hongming 已提交
300
			Namespace: namespace,
H
hongming 已提交
301 302 303 304 305 306 307 308 309 310
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: rbacv1.GroupName,
			Kind:     iamv1alpha2.ResourceKindRole,
			Name:     iamv1alpha2.NamespaceAdmin,
		},
		Subjects: []rbacv1.Subject{
			{
				Name:     creator,
				Kind:     iamv1alpha2.ResourceKindUser,
311 312
				APIGroup: rbacv1.GroupName,
			},
H
hongming 已提交
313 314
		},
	}
J
Jeff 已提交
315
}