Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
d87f701
Restrict membercluster name to a maximum of 63 characters
jamyct Nov 7, 2024
a159847
remove validation from v1alpha1 API
jamyct Nov 8, 2024
6fd1b17
add validation to APIs
jamyct Nov 8, 2024
a974e49
fix validation
jamyct Nov 8, 2024
caa0566
fix validation
jamyct Nov 8, 2024
e15893d
remove new type
jamyct Nov 8, 2024
edef393
switch restrictions to metav1.ObjectMeta identities
jamyct Nov 13, 2024
d318431
latest version
jamyct Nov 14, 2024
fa71b42
Use self.metadata.name instead of self.name
jamyct Nov 14, 2024
df5611f
update CEL
jamyct Nov 14, 2024
1ed2227
fix CEL line
jamyct Nov 14, 2024
5755525
create e2e test file for name validation
jamyct Nov 14, 2024
43b6b29
fix issues
jamyct Nov 15, 2024
21472e3
add regex e2e tests
jamyct Nov 15, 2024
b46b64f
remove testing example
jamyct Nov 15, 2024
1a8e07f
Merge branch 'main' into restrict-memberclustername-length
jamyct Nov 15, 2024
f36f2a4
fix k8serrors capital
jamyct Nov 15, 2024
f6d3545
move from e2e to integration
jamyct Nov 15, 2024
885d9a1
run goimports
jamyct Nov 15, 2024
5068e83
move back to e2e for timebeing
jamyct Nov 15, 2024
6c00ada
add positive e2e cases
jamyct Nov 15, 2024
20e6fd4
run goimports
jamyct Nov 15, 2024
0d0e028
fix import order & replace impersonateHubClient
jamyct Nov 18, 2024
e08ff73
remove line to delete mc
jamyct Nov 18, 2024
f1ea905
separate tests in two types: deny or allow
jamyct Nov 18, 2024
cb1e7d9
shorten error string
jamyct Nov 19, 2024
6b33b10
remove unnecessary get validation
jamyct Nov 19, 2024
50fe538
test v1 not v1beta1
jamyct Nov 19, 2024
f343941
move tests to apis placement directory
jamyct Nov 19, 2024
6075a0b
Add context
jamyct Nov 19, 2024
5b27590
removed unused variables from utils test file
jamyct Nov 19, 2024
97ade6a
run goimports
jamyct Nov 19, 2024
ac29273
run goimports on suite test
jamyct Nov 19, 2024
2f41141
fix import order arrangement
jamyct Nov 19, 2024
ffa954f
fix issues
jamyct Nov 19, 2024
550fddd
remove unused file
jamyct Nov 19, 2024
13b78d0
remove unused constants
jamyct Nov 19, 2024
28adb83
run goimports
jamyct Nov 19, 2024
c6f2879
Merge branch 'main' of github.com:jamyct/fleet into restrict-membercl…
jamyct Nov 20, 2024
032fbda
switch import from placement to cluster directory
jamyct Nov 20, 2024
5d81c6d
add copyright banner to suit_test.go
jamyct Nov 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apis/cluster/v1/membercluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
// +kubebuilder:printcolumn:JSONPath=`.status.resourceUsage.allocatable.memory`,name="Allocatable-Memory", priority=1, type=string

// MemberCluster is a resource created in the hub cluster to represent a member cluster within a fleet.
// +kubebuilder:validation:XValidation:rule="size(self.metadata.name) < 64",message="metadata.name max length is 63"
type MemberCluster struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand Down
1 change: 1 addition & 0 deletions apis/cluster/v1beta1/membercluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
// +kubebuilder:printcolumn:JSONPath=`.status.resourceUsage.allocatable.memory`,name="Allocatable-Memory", priority=1, type=string

// MemberCluster is a resource created in the hub cluster to represent a member cluster within a fleet.
// +kubebuilder:validation:XValidation:rule="size(self.metadata.name) < 64",message="metadata.name max length is 63"
type MemberCluster struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,9 @@ spec:
required:
- spec
type: object
x-kubernetes-validations:
- message: metadata.name max length is 63
rule: size(self.metadata.name) < 64
served: true
storage: false
subresources:
Expand Down Expand Up @@ -801,6 +804,9 @@ spec:
required:
- spec
type: object
x-kubernetes-validations:
- message: metadata.name max length is 63
rule: size(self.metadata.name) < 64
served: true
storage: true
subresources:
Expand Down
217 changes: 217 additions & 0 deletions test/apis/cluster/v1/api_validation_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/*
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
*/
package v1

import (
"errors"
"fmt"
"reflect"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
rbacv1 "k8s.io/api/rbac/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

clusterv1 "go.goms.io/fleet/apis/cluster/v1"
)

var _ = Describe("Test cluster v1 API validation", func() {
Context("Test MemberCluster API validation - invalid cases", func() {
It("should deny creating API with invalid name size", func() {
var name = "abcdef-123456789-123456789-123456789-123456789-123456789-123456789-123456789"
// Create the API.
memberClusterName := &clusterv1.MemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: clusterv1.MemberClusterSpec{
Identity: rbacv1.Subject{
Name: "fleet-member-agent-cluster-1",
Kind: "ServiceAccount",
Namespace: "fleet-system",
APIGroup: "",
},
},
}
By(fmt.Sprintf("expecting denial of CREATE API %s", name))
var err = hubClient.Create(ctx, memberClusterName)
var statusErr *k8serrors.StatusError
Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Create API call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8serrors.StatusError{})))
Expect(statusErr.Status().Message).Should(ContainSubstring("metadata.name max length is 63"))
})

It("should deny creating API with invalid name starting with non-alphanumeric character", func() {
var name = "-abcdef-123456789-123456789-123456789-123456789-123456789"
// Create the API.
memberClusterName := &clusterv1.MemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: clusterv1.MemberClusterSpec{
Identity: rbacv1.Subject{
Name: "fleet-member-agent-cluster-1",
Kind: "ServiceAccount",
Namespace: "fleet-system",
APIGroup: "",
},
},
}
By(fmt.Sprintf("expecting denial of CREATE API %s", name))
err := hubClient.Create(ctx, memberClusterName)
var statusErr *k8serrors.StatusError
Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Create API call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8serrors.StatusError{})))
Expect(statusErr.Status().Message).Should(ContainSubstring("a lowercase RFC 1123 subdomain"))
})

It("should deny creating API with invalid name ending with non-alphanumeric character", func() {
var name = "abcdef-123456789-123456789-123456789-123456789-123456789-"
// Create the API.
memberClusterName := &clusterv1.MemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: clusterv1.MemberClusterSpec{
Identity: rbacv1.Subject{
Name: "fleet-member-agent-cluster-1",
Kind: "ServiceAccount",
Namespace: "fleet-system",
APIGroup: "",
},
},
}
By(fmt.Sprintf("expecting denial of CREATE API %s", name))
err := hubClient.Create(ctx, memberClusterName)
var statusErr *k8serrors.StatusError
Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Create API call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8serrors.StatusError{})))
Expect(statusErr.Status().Message).Should(ContainSubstring("a lowercase RFC 1123 subdomain"))
})

It("should deny creating API with invalid name containing character that is not alphanumeric and not -", func() {
var name = "a_bcdef-123456789-123456789-123456789-123456789-123456789-123456789-123456789"
// Create the API.
memberClusterName := &clusterv1.MemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: clusterv1.MemberClusterSpec{
Identity: rbacv1.Subject{
Name: "fleet-member-agent-cluster-1",
Kind: "ServiceAccount",
Namespace: "fleet-system",
APIGroup: "",
},
},
}
By(fmt.Sprintf("expecting denial of CREATE API %s", name))
err := hubClient.Create(ctx, memberClusterName)
var statusErr *k8serrors.StatusError
Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Create API call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8serrors.StatusError{})))
Expect(statusErr.Status().Message).Should(ContainSubstring("a lowercase RFC 1123 subdomain"))
})
})

Context("Test Member Cluster creation API validation - valid cases", func() {
It("should allow creating API with valid name size", func() {
var name = "abc-123456789-123456789-123456789-123456789-123456789-123456789"
// Create the API.
memberClusterName := &clusterv1.MemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: clusterv1.MemberClusterSpec{
Identity: rbacv1.Subject{
Name: "fleet-member-agent-cluster-1",
Kind: "ServiceAccount",
Namespace: "fleet-system",
APIGroup: "",
},
},
}
Expect(hubClient.Create(ctx, memberClusterName)).Should(Succeed())
Expect(hubClient.Delete(ctx, memberClusterName)).Should(Succeed())
})

It("should allow creating API with valid name starting with alphabet character", func() {
var name = "abc-123456789"
// Create the API.
memberClusterName := &clusterv1.MemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: clusterv1.MemberClusterSpec{
Identity: rbacv1.Subject{
Name: "fleet-member-agent-cluster-1",
Kind: "ServiceAccount",
Namespace: "fleet-system",
APIGroup: "",
},
},
}
Expect(hubClient.Create(ctx, memberClusterName)).Should(Succeed())
Expect(hubClient.Delete(ctx, memberClusterName)).Should(Succeed())
})

It("should allow creating API with valid name starting with numeric character", func() {
var name = "123-123456789"
// Create the API.
memberClusterName := &clusterv1.MemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: clusterv1.MemberClusterSpec{
Identity: rbacv1.Subject{
Name: "fleet-member-agent-cluster-1",
Kind: "ServiceAccount",
Namespace: "fleet-system",
APIGroup: "",
},
},
}
Expect(hubClient.Create(ctx, memberClusterName)).Should(Succeed())
Expect(hubClient.Delete(ctx, memberClusterName)).Should(Succeed())
})

It("should allow creating API with valid name ending with alphabet character", func() {
var name = "123456789-abc"
// Create the API.
memberClusterName := &clusterv1.MemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: clusterv1.MemberClusterSpec{
Identity: rbacv1.Subject{
Name: "fleet-member-agent-cluster-1",
Kind: "ServiceAccount",
Namespace: "fleet-system",
APIGroup: "",
},
},
}
Expect(hubClient.Create(ctx, memberClusterName)).Should(Succeed())
Expect(hubClient.Delete(ctx, memberClusterName)).Should(Succeed())
})

It("should allow creating API with valid name ending with numeric character", func() {
var name = "123456789-123"
// Create the API.
memberClusterName := &clusterv1.MemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: clusterv1.MemberClusterSpec{
Identity: rbacv1.Subject{
Name: "fleet-member-agent-cluster-1",
Kind: "ServiceAccount",
Namespace: "fleet-system",
APIGroup: "",
},
},
}
Expect(hubClient.Create(ctx, memberClusterName)).Should(Succeed())
Expect(hubClient.Delete(ctx, memberClusterName)).Should(Succeed())
})
})
})
92 changes: 92 additions & 0 deletions test/apis/cluster/v1/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
*/
package v1

import (
"context"
"flag"
"path/filepath"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog/v2"
"k8s.io/klog/v2/textlogger"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"

clusterv1 "go.goms.io/fleet/apis/cluster/v1"
)

var (
hubTestEnv *envtest.Environment
hubClient client.Client
ctx context.Context
cancel context.CancelFunc
)

func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)

RunSpecs(t, "ClusterResourcePlacement Controller Suite")
}

var _ = BeforeSuite(func() {
By("Setup klog")
fs := flag.NewFlagSet("klog", flag.ContinueOnError)
klog.InitFlags(fs)
Expect(fs.Parse([]string{"--v", "5", "-add_dir_header", "true"})).Should(Succeed())

ctx, cancel = context.WithCancel(context.TODO())

By("bootstrap the test environment")
// Start the cluster.
hubTestEnv = &envtest.Environment{
CRDDirectoryPaths: []string{
filepath.Join("..", "..", "..", "..", "config", "crd", "bases"),
},
ErrorIfCRDPathMissing: true,
}
hubCfg, err := hubTestEnv.Start()
Expect(err).NotTo(HaveOccurred())
Expect(hubCfg).NotTo(BeNil())

Expect(clusterv1.AddToScheme(scheme.Scheme)).Should(Succeed())

klog.InitFlags(flag.CommandLine)
flag.Parse()
// Create the hub controller manager.
hubCtrlMgr, err := ctrl.NewManager(hubCfg, ctrl.Options{
Scheme: scheme.Scheme,
Metrics: metricsserver.Options{
BindAddress: "0",
},
Logger: textlogger.NewLogger(textlogger.NewConfig(textlogger.Verbosity(4))),
})
Expect(err).NotTo(HaveOccurred())

// Set up the client.
// The client must be one with cache (i.e. configured by the controller manager) to make
// use of the cache indexes.
hubClient = hubCtrlMgr.GetClient()
Expect(hubClient).NotTo(BeNil())

go func() {
defer GinkgoRecover()
err = hubCtrlMgr.Start(ctx)
Expect(err).ToNot(HaveOccurred(), "failed to start manager for hub")
}()
})

var _ = AfterSuite(func() {
defer klog.Flush()
cancel()

By("tearing down the test environment")
Expect(hubTestEnv.Stop()).Should(Succeed())
})