mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-08-19 04:06:35 +00:00
✨ add support for custom ClusterClaim configuration (#1004)
Scorecard supply-chain security / Scorecard analysis (push) Failing after 1m14s
Post / coverage (push) Failing after 26m54s
Post / images (amd64) (push) Failing after 3m38s
Post / images (arm64) (push) Failing after 3m13s
Post / image manifest (push) Has been skipped
Post / trigger clusteradm e2e (push) Has been skipped
Close stale issues and PRs / stale (push) Successful in 37s
Scorecard supply-chain security / Scorecard analysis (push) Failing after 1m14s
Post / coverage (push) Failing after 26m54s
Post / images (amd64) (push) Failing after 3m38s
Post / images (arm64) (push) Failing after 3m13s
Post / image manifest (push) Has been skipped
Post / trigger clusteradm e2e (push) Has been skipped
Close stale issues and PRs / stale (push) Successful in 37s
* vendor api Signed-off-by: Omar Farag <omarfarag74@gmail.com> * add support for maxCustomClusterClaim Signed-off-by: Omar Farag <omarfarag74@gmail.com> * support ReservedClusterClaimSuffixes Signed-off-by: Omar Farag <omarfarag74@gmail.com> * add and use klusterletinformer Signed-off-by: Omar Farag <omarfarag74@gmail.com> * fix tests Signed-off-by: Omar Farag <omarfarag74@gmail.com> * update for change in clusterclaimconfiguration api Signed-off-by: Omar Farag <omarfarag74@gmail.com> * requested changes, clean up Signed-off-by: Omar Farag <omarfarag74@gmail.com> * Use flag to pass the reservedClusterClaimSuffixes Signed-off-by: zhujian <jiazhu@redhat.com> * Add cluster claim tests Signed-off-by: zhujian <jiazhu@redhat.com> * use StringSliceVar to parse the reserved cluster claim suffixes flag Signed-off-by: zhujian <jiazhu@redhat.com> * fix rebase issues Signed-off-by: zhujian <jiazhu@redhat.com> * address code review comments Signed-off-by: zhujian <jiazhu@redhat.com> --------- Signed-off-by: Omar Farag <omarfarag74@gmail.com> Signed-off-by: zhujian <jiazhu@redhat.com> Co-authored-by: Omar Farag <omarfarag74@gmail.com>
This commit is contained in:
@@ -107,6 +107,12 @@ spec:
|
||||
{{if gt .AgentKubeAPIBurst 0}}
|
||||
- "--kube-api-burst={{ .AgentKubeAPIBurst }}"
|
||||
{{end}}
|
||||
{{if .MaxCustomClusterClaims}}
|
||||
- "--max-custom-cluster-claims={{ .MaxCustomClusterClaims }}"
|
||||
{{end}}
|
||||
{{if .ReservedClusterClaimSuffixes}}
|
||||
- "--reserved-cluster-claim-suffixes={{ .ReservedClusterClaimSuffixes }}"
|
||||
{{end}}
|
||||
{{if .AppliedManifestWorkEvictionGracePeriod}}
|
||||
- "--appliedmanifestwork-eviction-grace-period={{ .AppliedManifestWorkEvictionGracePeriod }}"
|
||||
{{end}}
|
||||
|
||||
@@ -94,6 +94,12 @@ spec:
|
||||
{{if gt .RegistrationKubeAPIBurst 0}}
|
||||
- "--kube-api-burst={{ .RegistrationKubeAPIBurst }}"
|
||||
{{end}}
|
||||
{{if .MaxCustomClusterClaims}}
|
||||
- "--max-custom-cluster-claims={{ .MaxCustomClusterClaims }}"
|
||||
{{end}}
|
||||
{{if .ReservedClusterClaimSuffixes}}
|
||||
- "--reserved-cluster-claim-suffixes={{ .ReservedClusterClaimSuffixes }}"
|
||||
{{end}}
|
||||
{{if eq .RegistrationDriver.AuthType "awsirsa"}}
|
||||
- "--registration-auth={{ .RegistrationDriver.AuthType }}"
|
||||
- "--hub-cluster-arn={{ .RegistrationDriver.AwsIrsa.HubClusterArn }}"
|
||||
|
||||
+10
@@ -181,6 +181,8 @@ type klusterletConfig struct {
|
||||
ExternalManagedKubeConfigAgentSecret string
|
||||
InstallMode operatorapiv1.InstallMode
|
||||
|
||||
MaxCustomClusterClaims int
|
||||
ReservedClusterClaimSuffixes string
|
||||
// PriorityClassName is the name of the PriorityClass used by the deployed agents
|
||||
PriorityClassName string
|
||||
|
||||
@@ -370,6 +372,14 @@ func (n *klusterletController) sync(ctx context.Context, controllerContext facto
|
||||
AuthType: klusterlet.Spec.RegistrationConfiguration.RegistrationDriver.AuthType,
|
||||
}
|
||||
}
|
||||
|
||||
// include clusterClaimConfig info if it exists
|
||||
if klusterlet.Spec.RegistrationConfiguration.ClusterClaimConfiguration != nil {
|
||||
config.MaxCustomClusterClaims = int(klusterlet.Spec.RegistrationConfiguration.ClusterClaimConfiguration.MaxCustomClusterClaims)
|
||||
config.ReservedClusterClaimSuffixes = strings.Join(
|
||||
klusterlet.Spec.RegistrationConfiguration.ClusterClaimConfiguration.ReservedClusterClaimSuffixes, ",")
|
||||
}
|
||||
|
||||
// construct cluster annotations string, the final format is "key1=value1,key2=value2"
|
||||
var annotationsArray []string
|
||||
for k, v := range commonhelpers.FilterClusterAnnotations(klusterlet.Spec.RegistrationConfiguration.ClusterAnnotations) {
|
||||
|
||||
+60
-10
@@ -377,7 +377,8 @@ func getDeployments(actions []clienttesting.Action, verb, suffix string) *appsv1
|
||||
return nil
|
||||
}
|
||||
|
||||
func assertKlusterletDeployment(t *testing.T, actions []clienttesting.Action, verb, serverURL, clusterName string) {
|
||||
func assertKlusterletDeployment(t *testing.T, registrationAuthType string, actions []clienttesting.Action, verb, serverURL, clusterName string,
|
||||
claimConfig *operatorapiv1.ClusterClaimConfiguration) {
|
||||
deployment := getDeployments(actions, verb, "agent")
|
||||
if deployment == nil {
|
||||
t.Errorf("klusterlet deployment not found")
|
||||
@@ -399,24 +400,39 @@ func assertKlusterletDeployment(t *testing.T, actions []clienttesting.Action, ve
|
||||
"--bootstrap-kubeconfig=/spoke/bootstrap/kubeconfig",
|
||||
}
|
||||
|
||||
expectedArgs = append(expectedArgs, "--agent-id=", "--workload-source-driver=kube", "--workload-source-config=/spoke/hub-kubeconfig/kubeconfig",
|
||||
"--status-sync-interval=60s", "--kube-api-qps=20", "--kube-api-burst=60")
|
||||
|
||||
if serverURL != "" {
|
||||
expectedArgs = append(expectedArgs, fmt.Sprintf("--spoke-external-server-urls=%s", serverURL))
|
||||
}
|
||||
|
||||
expectedArgs = append(expectedArgs, "--agent-id=", "--workload-source-driver=kube", "--workload-source-config=/spoke/hub-kubeconfig/kubeconfig",
|
||||
"--status-sync-interval=60s", "--kube-api-qps=20", "--kube-api-burst=60",
|
||||
"--registration-auth=awsirsa",
|
||||
"--hub-cluster-arn=arn:aws:eks:us-west-2:123456789012:cluster/hub-cluster1",
|
||||
"--managed-cluster-arn=arn:aws:eks:us-west-2:123456789012:cluster/managed-cluster1",
|
||||
"--managed-cluster-role-suffix=7f8141296c75f2871e3d030f85c35692")
|
||||
if claimConfig != nil {
|
||||
if claimConfig.MaxCustomClusterClaims > 0 {
|
||||
expectedArgs = append(expectedArgs, fmt.Sprintf("--max-custom-cluster-claims=%d", claimConfig.MaxCustomClusterClaims))
|
||||
}
|
||||
if len(claimConfig.ReservedClusterClaimSuffixes) > 0 {
|
||||
expectedArgs = append(expectedArgs, fmt.Sprintf("--reserved-cluster-claim-suffixes=%s",
|
||||
strings.Join(claimConfig.ReservedClusterClaimSuffixes, ",")))
|
||||
}
|
||||
}
|
||||
if registrationAuthType == "awsirsa" {
|
||||
expectedArgs = append(expectedArgs,
|
||||
"--registration-auth=awsirsa",
|
||||
"--hub-cluster-arn=arn:aws:eks:us-west-2:123456789012:cluster/hub-cluster1",
|
||||
"--managed-cluster-arn=arn:aws:eks:us-west-2:123456789012:cluster/managed-cluster1",
|
||||
"--managed-cluster-role-suffix=7f8141296c75f2871e3d030f85c35692")
|
||||
}
|
||||
|
||||
if !equality.Semantic.DeepEqual(args, expectedArgs) {
|
||||
t.Errorf("Expect args %v, but got %v", expectedArgs, args)
|
||||
return
|
||||
}
|
||||
|
||||
assert.True(t, isDotAwsMounted(volumeMounts))
|
||||
assert.True(t, isDotAwsVolumePresent(volumes))
|
||||
if registrationAuthType == "awsirsa" {
|
||||
assert.True(t, isDotAwsMounted(volumeMounts))
|
||||
assert.True(t, isDotAwsVolumePresent(volumes))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1075,7 +1091,7 @@ func TestAWSIrsaAuthInSingletonMode(t *testing.T) {
|
||||
t.Errorf("Expected non error when sync, %v", err)
|
||||
}
|
||||
|
||||
assertKlusterletDeployment(t, controller.kubeClient.Actions(), createVerb, "", "cluster1")
|
||||
assertKlusterletDeployment(t, commonhelpers.AwsIrsaAuthType, controller.kubeClient.Actions(), createVerb, "", "cluster1", nil)
|
||||
}
|
||||
|
||||
func TestAWSIrsaAuthInNonSingletonMode(t *testing.T) {
|
||||
@@ -1413,6 +1429,40 @@ func TestRenderingResourceRequirements(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterClaimConfigInSingletonMode(t *testing.T) {
|
||||
klusterlet := newKlusterlet("klusterlet", "testns", "cluster1")
|
||||
claimConfig := &operatorapiv1.ClusterClaimConfiguration{
|
||||
MaxCustomClusterClaims: 2,
|
||||
ReservedClusterClaimSuffixes: []string{"test1.io", "test2.io"},
|
||||
}
|
||||
if klusterlet.Spec.RegistrationConfiguration == nil {
|
||||
klusterlet.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationConfiguration{}
|
||||
}
|
||||
klusterlet.Spec.RegistrationConfiguration.ClusterClaimConfiguration = claimConfig
|
||||
|
||||
klusterlet.Spec.DeployOption.Mode = operatorapiv1.InstallModeSingleton
|
||||
hubSecret := newSecret(helpers.HubKubeConfig, "testns")
|
||||
hubSecret.Data["kubeconfig"] = []byte("dummykubeconfig")
|
||||
hubSecret.Data["cluster-name"] = []byte("cluster1")
|
||||
objects := []runtime.Object{
|
||||
newNamespace("testns"),
|
||||
newSecret(helpers.BootstrapHubKubeConfig, "testns"),
|
||||
hubSecret,
|
||||
}
|
||||
|
||||
syncContext := testingcommon.NewFakeSyncContext(t, "klusterlet")
|
||||
controller := newTestController(t, klusterlet, syncContext.Recorder(), nil, false,
|
||||
objects...)
|
||||
|
||||
err := controller.controller.sync(context.TODO(), syncContext)
|
||||
if err != nil {
|
||||
t.Errorf("Expected non error when sync, %v", err)
|
||||
}
|
||||
|
||||
assertKlusterletDeployment(t, commonhelpers.CSRAuthType, controller.kubeClient.Actions(), createVerb,
|
||||
"", "cluster1", claimConfig)
|
||||
}
|
||||
|
||||
func newKubeConfig(host string) []byte {
|
||||
configData, _ := runtime.Encode(clientcmdlatest.Codec, &clientcmdapi.Config{
|
||||
Clusters: map[string]*clientcmdapi.Cluster{"test-cluster": {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/openshift/library-go/pkg/operator/events"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
@@ -22,9 +23,10 @@ import (
|
||||
const labelCustomizedOnly = "open-cluster-management.io/spoke-only"
|
||||
|
||||
type claimReconcile struct {
|
||||
recorder events.Recorder
|
||||
claimLister clusterv1alpha1listers.ClusterClaimLister
|
||||
maxCustomClusterClaims int
|
||||
recorder events.Recorder
|
||||
claimLister clusterv1alpha1listers.ClusterClaimLister
|
||||
maxCustomClusterClaims int
|
||||
reservedClusterClaimSuffixes []string
|
||||
}
|
||||
|
||||
func (r *claimReconcile) reconcile(ctx context.Context, cluster *clusterv1.ManagedCluster) (*clusterv1.ManagedCluster, reconcileState, error) {
|
||||
@@ -55,13 +57,17 @@ func (r *claimReconcile) exposeClaims(ctx context.Context, cluster *clusterv1.Ma
|
||||
return fmt.Errorf("unable to list cluster claims: %w", err)
|
||||
}
|
||||
|
||||
reservedClaimNames := sets.NewString(clusterv1alpha1.ReservedClusterClaimNames[:]...)
|
||||
// check if the cluster claim is one of the reserved claims or has a reserved suffix.
|
||||
// if so, it will be treated as a reserved claim and will always be exposed.
|
||||
reservedClaimNames := sets.New(clusterv1alpha1.ReservedClusterClaimNames[:]...)
|
||||
reservedClaimSuffixes := sets.New(r.reservedClusterClaimSuffixes...)
|
||||
for _, clusterClaim := range clusterClaims {
|
||||
managedClusterClaim := clusterv1.ManagedClusterClaim{
|
||||
Name: clusterClaim.Name,
|
||||
Value: clusterClaim.Spec.Value,
|
||||
}
|
||||
if reservedClaimNames.Has(clusterClaim.Name) {
|
||||
|
||||
if matchReservedClaims(reservedClaimNames, reservedClaimSuffixes, managedClusterClaim) {
|
||||
reservedClaims = append(reservedClaims, managedClusterClaim)
|
||||
continue
|
||||
}
|
||||
@@ -90,3 +96,16 @@ func (r *claimReconcile) exposeClaims(ctx context.Context, cluster *clusterv1.Ma
|
||||
cluster.Status.ClusterClaims = claims
|
||||
return nil
|
||||
}
|
||||
|
||||
func matchReservedClaims(reservedClaims, reservedSuffixes sets.Set[string], claim clusterv1.ManagedClusterClaim) bool {
|
||||
if reservedClaims.Has(claim.Name) {
|
||||
return true
|
||||
}
|
||||
|
||||
for suffix := range reservedSuffixes {
|
||||
if strings.HasSuffix(claim.Name, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -29,8 +29,11 @@ import (
|
||||
testinghelpers "open-cluster-management.io/ocm/pkg/registration/helpers/testing"
|
||||
)
|
||||
|
||||
func TestSync(t *testing.T) {
|
||||
func init() {
|
||||
utilruntime.Must(features.SpokeMutableFeatureGate.Add(ocmfeature.DefaultSpokeRegistrationFeatureGates))
|
||||
}
|
||||
|
||||
func TestSync(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
cluster runtime.Object
|
||||
@@ -125,6 +128,7 @@ func TestSync(t *testing.T) {
|
||||
clusterInformerFactory.Cluster().V1alpha1().ClusterClaims(),
|
||||
kubeInformerFactory.Core().V1().Nodes(),
|
||||
20,
|
||||
[]string{},
|
||||
eventstesting.NewTestingEventRecorder(t),
|
||||
hubEventRecorder,
|
||||
)
|
||||
@@ -139,12 +143,13 @@ func TestSync(t *testing.T) {
|
||||
|
||||
func TestExposeClaims(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
cluster *clusterv1.ManagedCluster
|
||||
claims []*clusterv1alpha1.ClusterClaim
|
||||
maxCustomClusterClaims int
|
||||
validateActions func(t *testing.T, actions []clienttesting.Action)
|
||||
expectedErr string
|
||||
name string
|
||||
cluster *clusterv1.ManagedCluster
|
||||
claims []*clusterv1alpha1.ClusterClaim
|
||||
maxCustomClusterClaims int
|
||||
reservedClusterClaimSuffixes []string
|
||||
validateActions func(t *testing.T, actions []clienttesting.Action)
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "sync claims into status of the managed cluster",
|
||||
@@ -207,6 +212,14 @@ func TestExposeClaims(t *testing.T) {
|
||||
Value: "cluster1",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "id-test.k8s.io",
|
||||
},
|
||||
Spec: clusterv1alpha1.ClusterClaimSpec{
|
||||
Value: "cluster1",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "c",
|
||||
@@ -245,6 +258,85 @@ func TestExposeClaims(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keep custom reserved cluster claims",
|
||||
cluster: testinghelpers.NewJoinedManagedCluster(),
|
||||
claims: []*clusterv1alpha1.ClusterClaim{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a",
|
||||
},
|
||||
Spec: clusterv1alpha1.ClusterClaimSpec{
|
||||
Value: "b",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "e",
|
||||
},
|
||||
Spec: clusterv1alpha1.ClusterClaimSpec{
|
||||
Value: "f",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "id.k8s.io",
|
||||
},
|
||||
Spec: clusterv1alpha1.ClusterClaimSpec{
|
||||
Value: "cluster1",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "c",
|
||||
},
|
||||
Spec: clusterv1alpha1.ClusterClaimSpec{
|
||||
Value: "d",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test.reserved.io",
|
||||
},
|
||||
Spec: clusterv1alpha1.ClusterClaimSpec{
|
||||
Value: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
maxCustomClusterClaims: 2,
|
||||
reservedClusterClaimSuffixes: []string{"reserved.io"},
|
||||
validateActions: func(t *testing.T, actions []clienttesting.Action) {
|
||||
testingcommon.AssertActions(t, actions, "patch")
|
||||
patch := actions[0].(clienttesting.PatchAction).GetPatch()
|
||||
cluster := &clusterv1.ManagedCluster{}
|
||||
err := json.Unmarshal(patch, cluster)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []clusterv1.ManagedClusterClaim{
|
||||
{
|
||||
Name: "id.k8s.io",
|
||||
Value: "cluster1",
|
||||
},
|
||||
{
|
||||
Name: "test.reserved.io",
|
||||
Value: "test",
|
||||
},
|
||||
{
|
||||
Name: "a",
|
||||
Value: "b",
|
||||
},
|
||||
{
|
||||
Name: "c",
|
||||
Value: "d",
|
||||
},
|
||||
}
|
||||
actual := cluster.Status.ClusterClaims
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("expected cluster claim %v but got: %v", expected, actual)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove claims from managed cluster",
|
||||
cluster: newManagedCluster([]clusterv1.ManagedClusterClaim{
|
||||
@@ -356,6 +448,7 @@ func TestExposeClaims(t *testing.T) {
|
||||
clusterInformerFactory.Cluster().V1alpha1().ClusterClaims(),
|
||||
kubeInformerFactory.Core().V1().Nodes(),
|
||||
c.maxCustomClusterClaims,
|
||||
c.reservedClusterClaimSuffixes,
|
||||
eventstesting.NewTestingEventRecorder(t),
|
||||
hubEventRecorder,
|
||||
)
|
||||
|
||||
@@ -96,6 +96,7 @@ func TestSyncManagedCluster(t *testing.T) {
|
||||
clusterInformerFactory.Cluster().V1alpha1().ClusterClaims(),
|
||||
kubeInformerFactory.Core().V1().Nodes(),
|
||||
20,
|
||||
[]string{},
|
||||
eventstesting.NewTestingEventRecorder(t),
|
||||
hubEventRecorder,
|
||||
)
|
||||
|
||||
@@ -330,6 +330,7 @@ func TestHealthCheck(t *testing.T) {
|
||||
clusterInformerFactory.Cluster().V1alpha1().ClusterClaims(),
|
||||
kubeInformerFactory.Core().V1().Nodes(),
|
||||
20,
|
||||
[]string{},
|
||||
eventstesting.NewTestingEventRecorder(t),
|
||||
hubEventRecorder,
|
||||
)
|
||||
|
||||
@@ -54,6 +54,7 @@ func NewManagedClusterStatusController(
|
||||
claimInformer clusterv1alpha1informer.ClusterClaimInformer,
|
||||
nodeInformer corev1informers.NodeInformer,
|
||||
maxCustomClusterClaims int,
|
||||
reservedClusterClaimSuffixes []string,
|
||||
resyncInterval time.Duration,
|
||||
recorder events.Recorder,
|
||||
hubEventRecorder kevents.EventRecorder) factory.Controller {
|
||||
@@ -65,6 +66,7 @@ func NewManagedClusterStatusController(
|
||||
claimInformer,
|
||||
nodeInformer,
|
||||
maxCustomClusterClaims,
|
||||
reservedClusterClaimSuffixes,
|
||||
recorder,
|
||||
hubEventRecorder,
|
||||
)
|
||||
@@ -84,6 +86,7 @@ func newManagedClusterStatusController(
|
||||
claimInformer clusterv1alpha1informer.ClusterClaimInformer,
|
||||
nodeInformer corev1informers.NodeInformer,
|
||||
maxCustomClusterClaims int,
|
||||
reservedClusterClaimSuffixes []string,
|
||||
recorder events.Recorder,
|
||||
hubEventRecorder kevents.EventRecorder) *managedClusterStatusController {
|
||||
return &managedClusterStatusController{
|
||||
@@ -94,7 +97,8 @@ func newManagedClusterStatusController(
|
||||
reconcilers: []statusReconcile{
|
||||
&joiningReconcile{recorder: recorder},
|
||||
&resoureReconcile{managedClusterDiscoveryClient: managedClusterDiscoveryClient, nodeLister: nodeInformer.Lister()},
|
||||
&claimReconcile{claimLister: claimInformer.Lister(), recorder: recorder, maxCustomClusterClaims: maxCustomClusterClaims},
|
||||
&claimReconcile{claimLister: claimInformer.Lister(), recorder: recorder,
|
||||
maxCustomClusterClaims: maxCustomClusterClaims, reservedClusterClaimSuffixes: reservedClusterClaimSuffixes},
|
||||
},
|
||||
hubClusterLister: hubClusterInformer.Lister(),
|
||||
hubEventRecorder: hubEventRecorder,
|
||||
|
||||
@@ -30,11 +30,12 @@ type SpokeAgentOptions struct {
|
||||
// See more details in: https://github.com/open-cluster-management-io/ocm/pull/443#discussion_r1610868646
|
||||
HubConnectionTimeoutSeconds int32
|
||||
|
||||
HubKubeconfigSecret string
|
||||
SpokeExternalServerURLs []string
|
||||
ClusterHealthCheckPeriod time.Duration
|
||||
MaxCustomClusterClaims int
|
||||
ClusterAnnotations map[string]string
|
||||
HubKubeconfigSecret string
|
||||
SpokeExternalServerURLs []string
|
||||
ClusterHealthCheckPeriod time.Duration
|
||||
MaxCustomClusterClaims int
|
||||
ReservedClusterClaimSuffixes []string
|
||||
ClusterAnnotations map[string]string
|
||||
|
||||
RegisterDriverOption *registerfactory.Options
|
||||
}
|
||||
@@ -71,6 +72,8 @@ func (o *SpokeAgentOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
"The period to check managed cluster kube-apiserver health")
|
||||
fs.IntVar(&o.MaxCustomClusterClaims, "max-custom-cluster-claims", o.MaxCustomClusterClaims,
|
||||
"The max number of custom cluster claims to expose.")
|
||||
fs.StringSliceVar(&o.ReservedClusterClaimSuffixes, "reserved-cluster-claim-suffixes", o.ReservedClusterClaimSuffixes,
|
||||
"A list of suffixes for reserved cluster claims.")
|
||||
fs.StringToStringVar(&o.ClusterAnnotations, "cluster-annotations", o.ClusterAnnotations, `the annotations with the reserve
|
||||
prefix "agent.open-cluster-management.io" set on ManagedCluster when creating only, other actors can update it afterwards.`)
|
||||
|
||||
|
||||
@@ -358,6 +358,7 @@ func (o *SpokeAgentConfig) RunSpokeAgentWithSpokeInformers(ctx context.Context,
|
||||
spokeClusterInformerFactory.Cluster().V1alpha1().ClusterClaims(),
|
||||
spokeKubeInformerFactory.Core().V1().Nodes(),
|
||||
o.registrationOption.MaxCustomClusterClaims,
|
||||
o.registrationOption.ReservedClusterClaimSuffixes,
|
||||
o.registrationOption.ClusterHealthCheckPeriod,
|
||||
recorder,
|
||||
hubEventRecorder,
|
||||
|
||||
@@ -789,6 +789,74 @@ var _ = ginkgo.Describe("Klusterlet", func() {
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
})
|
||||
|
||||
ginkgo.It("Deployment should be updated when klusterlet claim configure is changed", func() {
|
||||
_, err := operatorClient.OperatorV1().Klusterlets().Create(context.Background(), klusterlet, metav1.CreateOptions{})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
gomega.Eventually(func() bool {
|
||||
if _, err := kubeClient.AppsV1().Deployments(klusterletNamespace).Get(context.Background(), workDeploymentName, metav1.GetOptions{}); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
|
||||
// Check if generations are correct
|
||||
gomega.Eventually(func() bool {
|
||||
actual, err := operatorClient.OperatorV1().Klusterlets().Get(context.Background(), klusterlet.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if actual.Generation != actual.Status.ObservedGeneration {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
|
||||
gomega.Eventually(func() error {
|
||||
klusterlet, err = operatorClient.OperatorV1().Klusterlets().Get(context.Background(), klusterlet.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
klusterlet.Spec.RegistrationConfiguration = &operatorapiv1.RegistrationConfiguration{
|
||||
ClusterClaimConfiguration: &operatorapiv1.ClusterClaimConfiguration{
|
||||
MaxCustomClusterClaims: 2,
|
||||
ReservedClusterClaimSuffixes: []string{"reserved1.io", "reserved2.io"},
|
||||
},
|
||||
}
|
||||
_, err = operatorClient.OperatorV1().Klusterlets().Update(context.Background(), klusterlet, metav1.UpdateOptions{})
|
||||
return err
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.Succeed())
|
||||
|
||||
gomega.Eventually(func() bool {
|
||||
actual, err := kubeClient.AppsV1().Deployments(klusterletNamespace).Get(context.Background(), registrationDeploymentName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
gomega.Expect(len(actual.Spec.Template.Spec.Containers)).Should(gomega.Equal(1))
|
||||
if len(actual.Spec.Template.Spec.Containers[0].Args) != 9 {
|
||||
return false
|
||||
}
|
||||
return actual.Spec.Template.Spec.Containers[0].Args[7] == "--max-custom-cluster-claims=2" &&
|
||||
actual.Spec.Template.Spec.Containers[0].Args[8] == "--reserved-cluster-claim-suffixes=reserved1.io,reserved2.io"
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
|
||||
// Check if generations are correct
|
||||
gomega.Eventually(func() bool {
|
||||
actual, err := operatorClient.OperatorV1().Klusterlets().Get(context.Background(), klusterlet.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if actual.Generation != actual.Status.ObservedGeneration {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("klusterlet statuses", func() {
|
||||
|
||||
@@ -27,6 +27,7 @@ var _ = ginkgo.Describe("Cluster Claim", func() {
|
||||
var managedClusterName, hubKubeconfigSecret, hubKubeconfigDir string
|
||||
var claims []*clusterv1alpha1.ClusterClaim
|
||||
var maxCustomClusterClaims int
|
||||
var reservedClusterClaimSuffixes []string
|
||||
var err error
|
||||
var cancel context.CancelFunc
|
||||
|
||||
@@ -52,11 +53,12 @@ var _ = ginkgo.Describe("Cluster Claim", func() {
|
||||
|
||||
// run registration agent
|
||||
agentOptions := &spoke.SpokeAgentOptions{
|
||||
BootstrapKubeconfig: bootstrapKubeConfigFile,
|
||||
HubKubeconfigSecret: hubKubeconfigSecret,
|
||||
ClusterHealthCheckPeriod: 1 * time.Minute,
|
||||
MaxCustomClusterClaims: maxCustomClusterClaims,
|
||||
RegisterDriverOption: registerfactory.NewOptions(),
|
||||
BootstrapKubeconfig: bootstrapKubeConfigFile,
|
||||
HubKubeconfigSecret: hubKubeconfigSecret,
|
||||
ClusterHealthCheckPeriod: 1 * time.Minute,
|
||||
MaxCustomClusterClaims: maxCustomClusterClaims,
|
||||
ReservedClusterClaimSuffixes: reservedClusterClaimSuffixes,
|
||||
RegisterDriverOption: registerfactory.NewOptions(),
|
||||
}
|
||||
commOptions := commonoptions.NewAgentOptions()
|
||||
commOptions.HubKubeconfigDir = hubKubeconfigDir
|
||||
@@ -271,4 +273,45 @@ var _ = ginkgo.Describe("Cluster Claim", func() {
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("Keep custom reserved claims", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
maxCustomClusterClaims = 5
|
||||
reservedClusterClaimSuffixes = []string{"reserved.io"}
|
||||
claims = []*clusterv1alpha1.ClusterClaim{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "claim.reserved.io",
|
||||
},
|
||||
Spec: clusterv1alpha1.ClusterClaimSpec{
|
||||
Value: "value-test",
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
claims = append(claims, &clusterv1alpha1.ClusterClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("claim-%d", i),
|
||||
},
|
||||
Spec: clusterv1alpha1.ClusterClaimSpec{
|
||||
Value: fmt.Sprintf("value-%d", i),
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
ginkgo.It("should sync custom suffix claims to status of ManagedCluster", func() {
|
||||
assertSuccessBootstrap()
|
||||
|
||||
ginkgo.By("Sync claims")
|
||||
gomega.Eventually(func() bool {
|
||||
spokeCluster, err := util.GetManagedCluster(clusterClient, managedClusterName)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(spokeCluster.Status.ClusterClaims) == maxCustomClusterClaims+1
|
||||
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user