Skip to content

Commit

Permalink
Add support to enable/disable features in multicluster (#1)
Browse files Browse the repository at this point in the history
Signed-off-by: Rokibul Hasan <[email protected]>
  • Loading branch information
RokibulHasan7 authored Oct 7, 2024
1 parent 48918f3 commit 16bc6e1
Show file tree
Hide file tree
Showing 6,090 changed files with 2,065,727 additions and 7,358 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ const (
type ManagedClusterProfileBindingSpec struct {
ProfileRef core.LocalObjectReference `json:"profileRef"`
ClusterMetadata kmapi.ClusterInfo `json:"clusterMetadata"`
// Features map[string]FeatureSpec `json:"features,omitempty"`
}

type BindingStatusPhase string
Expand Down
2 changes: 2 additions & 0 deletions apis/profile/v1alpha1/managedclustersetprofile_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type ManagedClusterSetProfileSpec struct {
}

type FeatureSpec struct {
// +optional
FeatureSet string `json:"featureSet,omitempty"`
// Chart specifies the chart information that will be used by the FluxCD to install the respective feature
// +optional
Chart uiapi.ChartInfo `json:"chart,omitempty"`
Expand Down
2 changes: 2 additions & 0 deletions crds/profile.k8s.appscode.com_managedclustersetprofiles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ spec:
- name
- sourceRef
type: object
featureSet:
type: string
values:
x-kubernetes-preserve-unknown-fields: true
valuesFrom:
Expand Down
201 changes: 175 additions & 26 deletions go.mod

Large diffs are not rendered by default.

1,223 changes: 1,164 additions & 59 deletions go.sum

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions pkg/cmds/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
clusterv1 "open-cluster-management.io/api/cluster/v1"
clusterv1beta2 "open-cluster-management.io/api/cluster/v1beta2"
workv1 "open-cluster-management.io/api/work/v1"
workv1alpha1 "open-cluster-management.io/api/work/v1alpha1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
Expand All @@ -51,6 +52,7 @@ func init() {
utilruntime.Must(clusterv1.Install(scheme))
utilruntime.Must(clusterv1beta2.Install(scheme))
utilruntime.Must(workv1.Install(scheme))
utilruntime.Must(workv1alpha1.Install(scheme))
}

func NewCmdManager() *cobra.Command {
Expand Down
23 changes: 23 additions & 0 deletions pkg/common/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
Copyright AppsCode Inc. and Contributors.
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 common

const (
ClusterClaimClusterInfo = "cluster.ace.info"
LabelAceFeatureSet = "featureset.appscode.com/managed"
ProfileLabel = "profile.appscode.com/name"
)
59 changes: 59 additions & 0 deletions pkg/controller/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
Copyright AppsCode Inc. and Contributors.
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 controller

import (
"errors"
"fmt"

profilev1alpha1 "github.com/kluster-manager/cluster-profile/apis/profile/v1alpha1"
"github.com/kluster-manager/cluster-profile/pkg/common"

kmapi "kmodules.xyz/client-go/api/v1"
v1 "open-cluster-management.io/api/cluster/v1"
"sigs.k8s.io/yaml"
)

func GetClusterMetadata(cluster v1.ManagedCluster) (kmapi.ClusterInfo, error) {
var clusterInfo kmapi.ClusterInfo
var clusterMetadata struct {
ClusterMetadata kmapi.ClusterInfo `yaml:"clusterMetadata"`
}

for _, claim := range cluster.Status.ClusterClaims {
if claim.Name == common.ClusterClaimClusterInfo {
yamlData := []byte(claim.Value)
if err := yaml.Unmarshal(yamlData, &clusterMetadata); err != nil {
return clusterInfo, err // Return error if YAML unmarshaling fails
}
clusterInfo = clusterMetadata.ClusterMetadata
return clusterInfo, nil
}
}

return clusterInfo, errors.New("cluster info not found")
}

func validateFeatureList(profile *profilev1alpha1.ManagedClusterSetProfile) error {
requiredFeatures := []string{"opscenter-features", "kube-ui-server"}
for _, f := range requiredFeatures {
if _, found := profile.Spec.Features[f]; !found {
return fmt.Errorf("%s not found in feature list", f)
}
}
return nil
}
75 changes: 72 additions & 3 deletions pkg/controller/managedclusterprofilebinding_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ import (
"context"

profilev1alpha1 "github.com/kluster-manager/cluster-profile/apis/profile/v1alpha1"
"github.com/kluster-manager/cluster-profile/pkg/feature_installer"

"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

// ManagedClusterProfileBindingReconciler reconciles a ManagedClusterProfileBinding object
Expand All @@ -47,16 +52,80 @@ type ManagedClusterProfileBindingReconciler struct {
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
func (r *ManagedClusterProfileBindingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
logger := log.FromContext(ctx)
logger.Info("Start reconciling")

// TODO(user): your logic here
profileBinding := &profilev1alpha1.ManagedClusterProfileBinding{}
err := r.Client.Get(ctx, req.NamespacedName, profileBinding)
if err != nil {
return reconcile.Result{}, client.IgnoreNotFound(err)
}

return ctrl.Result{}, nil
profile := &profilev1alpha1.ManagedClusterSetProfile{}
err = r.Client.Get(ctx, types.NamespacedName{Name: profileBinding.Spec.ProfileRef.Name}, profile)
if err != nil && errors.IsNotFound(err) {
if err = r.Delete(ctx, profileBinding); err != nil {
return reconcile.Result{}, err
}
} else if err != nil {
return reconcile.Result{}, err
}

if err = validateFeatureList(profile); err != nil {
return reconcile.Result{}, err
}

featureInfo := make(map[string][]string)
for f, val := range profile.Spec.Features {
featureInfo[val.FeatureSet] = append(featureInfo[val.FeatureSet], f)
}

if err = feature_installer.EnableFeatures(ctx, r.Client, profileBinding, featureInfo, profile); err != nil {
return reconcile.Result{}, err
}

return reconcile.Result{}, nil
}

func (r *ManagedClusterProfileBindingReconciler) mapManagedClusterProfileBindingToProfile(ctx context.Context, obj client.Object) []reconcile.Request {
logger := log.FromContext(ctx)
profile, ok := obj.(*profilev1alpha1.ManagedClusterSetProfile)
if !ok {
return nil
}

logger.Info("ManagedClusterSetProfile updated", "name", profile.GetName())

profileBindingList := &profilev1alpha1.ManagedClusterProfileBindingList{}
err := r.List(ctx, profileBindingList)
if err != nil {
logger.Error(err, "Failed to list ManagedClusterProfileBinding objects")
return nil
}

var requests []reconcile.Request
for _, profileBinding := range profileBindingList.Items {
if profileBinding.Spec.ProfileRef.Name == profile.GetName() {
requests = append(requests, reconcile.Request{
NamespacedName: types.NamespacedName{
Name: profileBinding.Name,
Namespace: profileBinding.Namespace,
},
})
logger.Info("Enqueuing request", "name", profileBinding.Name, "namespace", profileBinding.Namespace)
}
}

return requests
}

// SetupWithManager sets up the controller with the Manager.
func (r *ManagedClusterProfileBindingReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&profilev1alpha1.ManagedClusterProfileBinding{}).
Watches(
&profilev1alpha1.ManagedClusterSetProfile{},
handler.EnqueueRequestsFromMapFunc(r.mapManagedClusterProfileBindingToProfile),
).
Complete(r)
}
140 changes: 136 additions & 4 deletions pkg/controller/managedclustersetprofile_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,27 @@ package controller

import (
"context"
"fmt"

profilev1alpha1 "github.com/kluster-manager/cluster-profile/apis/profile/v1alpha1"
"github.com/kluster-manager/cluster-profile/pkg/common"

"gomodules.xyz/x/strings"
core "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
cu "kmodules.xyz/client-go/client"
clusterv1 "open-cluster-management.io/api/cluster/v1"
clusterv1beta2 "open-cluster-management.io/api/cluster/v1beta2"
clustersdkv1beta2 "open-cluster-management.io/sdk-go/pkg/apis/cluster/v1beta2"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

// ManagedClusterSetProfileReconciler reconciles a ManagedClusterSetProfile object
Expand All @@ -47,16 +61,134 @@ type ManagedClusterSetProfileReconciler struct {
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
func (r *ManagedClusterSetProfileReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
logger := log.FromContext(ctx)
logger.Info("Start reconciling")

// TODO(user): your logic here
profile := &profilev1alpha1.ManagedClusterSetProfile{}
err := r.Client.Get(ctx, req.NamespacedName, profile)
if err != nil {
return reconcile.Result{}, client.IgnoreNotFound(err)
}

return ctrl.Result{}, nil
var managedClusterSet clusterv1beta2.ManagedClusterSet
err = r.Get(ctx, types.NamespacedName{Name: profile.Name}, &managedClusterSet)
if err != nil && errors.IsNotFound(err) {
if err = r.Delete(ctx, profile); err != nil {
return reconcile.Result{}, err
}
} else if err != nil {
return reconcile.Result{}, err
}

sel, err := clustersdkv1beta2.BuildClusterSelector(&managedClusterSet)
if err != nil {
return reconcile.Result{}, err
}

var clusters clusterv1.ManagedClusterList
err = r.List(ctx, &clusters, client.MatchingLabelsSelector{Selector: sel})
if err != nil {
return reconcile.Result{}, err
}

label := map[string]string{
common.ProfileLabel: profile.Name,
}
profileBindingList := &profilev1alpha1.ManagedClusterProfileBindingList{}
err = r.List(ctx, profileBindingList, client.MatchingLabelsSelector{
Selector: labels.SelectorFromSet(label),
})
if err != nil {
return reconcile.Result{}, err
}

clusterNameList := make([]string, 0)
for _, cluster := range clusters.Items {
clusterNameList = append(clusterNameList, cluster.Name)
}

for _, pb := range profileBindingList.Items {
if !strings.Contains(clusterNameList, pb.Namespace) {
if err = r.Delete(ctx, &pb); err != nil {
return reconcile.Result{}, err
}
}
}

// create ManagedClusterProfileBinding for every cluster of this clusterSet
for _, cluster := range clusters.Items {
clusterMetadata, err := GetClusterMetadata(cluster)
if err != nil {
return reconcile.Result{}, err
}

profileBinding := &profilev1alpha1.ManagedClusterProfileBinding{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", profile.Name, cluster.Name),
Namespace: cluster.Name,
Labels: map[string]string{
common.ProfileLabel: profile.Name,
},
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(profile, profilev1alpha1.SchemeGroupVersion.WithKind(profilev1alpha1.ResourceKindManagedClusterSetProfile)),
},
},
Spec: profilev1alpha1.ManagedClusterProfileBindingSpec{
ProfileRef: core.LocalObjectReference{Name: profile.Name},
ClusterMetadata: clusterMetadata,
},
}

_, err = cu.CreateOrPatch(context.Background(), r.Client, profileBinding, func(obj client.Object, createOp bool) client.Object {
in := obj.(*profilev1alpha1.ManagedClusterProfileBinding)
in.Spec = profileBinding.Spec
return in
})
if err != nil {
return reconcile.Result{}, err
}
}

return reconcile.Result{}, nil
}

func (r *ManagedClusterSetProfileReconciler) mapManagedClusterSetToProfile(ctx context.Context, obj client.Object) []reconcile.Request {
logger := log.FromContext(ctx)
managedClusterSet, ok := obj.(*clusterv1beta2.ManagedClusterSet)
if !ok {
return nil
}

logger.Info("ManagedClusterSet updated", "name", managedClusterSet.GetName())

profileList := &profilev1alpha1.ManagedClusterSetProfileList{}
err := r.List(ctx, profileList)
if err != nil {
logger.Error(err, "Failed to list ManagedClusterSetRoleBinding objects")
return nil
}

var requests []reconcile.Request
for _, profile := range profileList.Items {
if profile.Name == managedClusterSet.GetName() {
requests = append(requests, reconcile.Request{
NamespacedName: types.NamespacedName{
Name: profile.Name,
},
})
logger.Info("Enqueuing request", "name", profile.Name)
}
}

return requests
}

// SetupWithManager sets up the controller with the Manager.
func (r *ManagedClusterSetProfileReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&profilev1alpha1.ManagedClusterSetProfile{}).
Watches(
&clusterv1beta2.ManagedClusterSet{},
handler.EnqueueRequestsFromMapFunc(r.mapManagedClusterSetToProfile),
).
Complete(r)
}
Loading

0 comments on commit 16bc6e1

Please sign in to comment.