diff --git a/api/go.mod b/api/go.mod index 3156dbb..dfc9512 100644 --- a/api/go.mod +++ b/api/go.mod @@ -3,7 +3,7 @@ module github.com/openmcp-project/openmcp-operator/api go 1.25.1 require ( - github.com/openmcp-project/controller-utils v0.21.0 + github.com/openmcp-project/controller-utils v0.22.0 k8s.io/api v0.34.1 k8s.io/apiextensions-apiserver v0.34.1 k8s.io/apimachinery v0.34.1 diff --git a/api/go.sum b/api/go.sum index 6bb60fe..a2697b7 100644 --- a/api/go.sum +++ b/api/go.sum @@ -73,8 +73,8 @@ github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/openmcp-project/controller-utils v0.21.0 h1:X5yN/BnJuNX9CXWgIU/cCynWFFv1KLR4zW9nnJl5Izg= -github.com/openmcp-project/controller-utils v0.21.0/go.mod h1:b8VcTK6iXFgkW6pXtIEDbaiQtzqYycVMFmElc7SFBQQ= +github.com/openmcp-project/controller-utils v0.22.0 h1:kdWGds+LOyOaOuKqWZGsJUv17e78HCr5y3bJOMSkdqE= +github.com/openmcp-project/controller-utils v0.22.0/go.mod h1:aIF4lk7agc+yCNRN5Oqg4BLlzRKsGixqwsGmxPoO5ak= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/go.mod b/go.mod index 096b1ea..c4c01ce 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( dario.cat/mergo v1.0.2 github.com/onsi/ginkgo/v2 v2.25.3 github.com/onsi/gomega v1.38.2 - github.com/openmcp-project/controller-utils v0.21.0 + github.com/openmcp-project/controller-utils v0.22.0 github.com/openmcp-project/openmcp-operator/api v0.14.0 github.com/openmcp-project/openmcp-operator/lib v0.14.0 github.com/spf13/cobra v1.10.1 diff --git a/go.sum b/go.sum index 4a1d08d..0954434 100644 --- a/go.sum +++ b/go.sum @@ -97,8 +97,8 @@ github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/openmcp-project/controller-utils v0.21.0 h1:X5yN/BnJuNX9CXWgIU/cCynWFFv1KLR4zW9nnJl5Izg= -github.com/openmcp-project/controller-utils v0.21.0/go.mod h1:b8VcTK6iXFgkW6pXtIEDbaiQtzqYycVMFmElc7SFBQQ= +github.com/openmcp-project/controller-utils v0.22.0 h1:kdWGds+LOyOaOuKqWZGsJUv17e78HCr5y3bJOMSkdqE= +github.com/openmcp-project/controller-utils v0.22.0/go.mod h1:aIF4lk7agc+yCNRN5Oqg4BLlzRKsGixqwsGmxPoO5ak= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/lib/clusteraccess/clusteraccess.go b/lib/clusteraccess/clusteraccess.go index 861cc7b..9db80bf 100644 --- a/lib/clusteraccess/clusteraccess.go +++ b/lib/clusteraccess/clusteraccess.go @@ -11,19 +11,20 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/clientcmd" - "github.com/openmcp-project/controller-utils/pkg/clusters" - "github.com/openmcp-project/controller-utils/pkg/resources" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/openmcp-project/controller-utils/pkg/clusters" + ctrlutils "github.com/openmcp-project/controller-utils/pkg/controller" + "github.com/openmcp-project/controller-utils/pkg/resources" + clustersv1alpha1 "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1" + commonapi "github.com/openmcp-project/openmcp-operator/api/common" constv1alpha1 "github.com/openmcp-project/openmcp-operator/api/constants" libutils "github.com/openmcp-project/openmcp-operator/lib/utils" - - commonapi "github.com/openmcp-project/openmcp-operator/api/common" ) const ( @@ -839,11 +840,13 @@ func (m *accessRequestMutator) Mutate(accessRequest *clustersv1alpha1.AccessRequ // StableRequestName generates a stable name for a Cluster- or AccessRequest related to an MCP. // This basically results in '--'. +// If the resulting string exceeds the Kubernetes name length limit of 63 characters, it will be truncated with the last characters replaced by a hash of what was removed. func StableRequestName(controllerName string, request reconcile.Request) string { return StableRequestNameFromLocalName(controllerName, request.Name) } +// StableRequestNameFromLocalName works like StableRequestName but takes a local name directly instead of a reconcile.Request. func StableRequestNameFromLocalName(controllerName, localName string) string { controllerName = strings.ToLower(controllerName) - return fmt.Sprintf("%s--%s", controllerName, localName) + return ctrlutils.ShortenToXCharactersUnsafe(fmt.Sprintf("%s--%s", controllerName, localName), ctrlutils.K8sMaxNameLength) } diff --git a/lib/clusteraccess/testing.go b/lib/clusteraccess/testing.go new file mode 100644 index 0000000..7db60df --- /dev/null +++ b/lib/clusteraccess/testing.go @@ -0,0 +1,135 @@ +package clusteraccess + +import ( + "context" + "fmt" + "time" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + "github.com/openmcp-project/controller-utils/pkg/clusters" + "github.com/openmcp-project/controller-utils/pkg/resources" + + clustersv1alpha1 "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1" + commonapi "github.com/openmcp-project/openmcp-operator/api/common" + openmcpconst "github.com/openmcp-project/openmcp-operator/api/constants" +) + +func NewTestClusterAccessManager(platformClusterClient client.Client, controllerName, controllerNamespace string, fakeClientMapping map[string]client.Client) Manager { + return &testManagerImpl{ + managerImpl: managerImpl{ + platformClusterClient: platformClusterClient, + controllerName: controllerName, + controllerNamespace: controllerNamespace, + timeout: 5 * time.Minute, + interval: 10 * time.Second, + log: nil, // Default to no logging + }, + fakeClientMapping: fakeClientMapping, + } +} + +type testManagerImpl struct { + managerImpl + fakeClientMapping map[string]client.Client +} + +var _ Manager = &testManagerImpl{} + +// CreateAndWaitForCluster implements Manager. +func (m *testManagerImpl) CreateAndWaitForCluster(ctx context.Context, localName, purpose string, scheme *runtime.Scheme, permissions []clustersv1alpha1.PermissionsRequest) (*clusters.Cluster, error) { + cr := &clustersv1alpha1.ClusterRequest{ + ObjectMeta: metav1.ObjectMeta{ + Name: StableRequestNameFromLocalName(m.controllerName, localName), + Namespace: m.controllerNamespace, + }, + } + + clusterRequestMutator := newClusterRequestMutator(cr.Name, cr.Namespace, purpose) + clusterRequestMutator.WithMetadata(resources.NewMetadataMutator().WithLabels(map[string]string{ + openmcpconst.ManagedByLabel: m.controllerName, + })) + + if err := resources.CreateOrUpdateResource(ctx, m.platformClusterClient, clusterRequestMutator); err != nil { + return nil, fmt.Errorf("failed to create/update ClusterRequest: %w", err) + } + + // TESTING MODIFICATIONS + // fake ClusterRequest readiness + old := cr.DeepCopy() + cr.Status.Phase = clustersv1alpha1.REQUEST_GRANTED + if err := m.platformClusterClient.Status().Patch(ctx, cr, client.MergeFrom(old)); err != nil { + return nil, fmt.Errorf("failed to update ClusterRequest status: %w", err) + } + + cl, _, err := m.WaitForClusterAccess(ctx, localName, scheme, &commonapi.ObjectReference{ + Name: cr.Name, + Namespace: cr.Namespace, + }, ReferenceToClusterRequest, permissions) + return cl, err +} + +// WaitForClusterAccess implements Manager. +func (m *testManagerImpl) WaitForClusterAccess(ctx context.Context, localName string, scheme *runtime.Scheme, ref *commonapi.ObjectReference, refType ClusterReferenceType, permissions []clustersv1alpha1.PermissionsRequest) (*clusters.Cluster, *clustersv1alpha1.AccessRequest, error) { + ar := &clustersv1alpha1.AccessRequest{ + ObjectMeta: metav1.ObjectMeta{ + Name: StableRequestNameFromLocalName(m.controllerName, localName), + Namespace: m.controllerNamespace, + }, + } + + accessRequestMutator := newAccessRequestMutator(ar.Name, ar.Namespace) + switch refType { + case ReferenceToCluster: + accessRequestMutator.WithClusterRef(ref) + case ReferenceToClusterRequest: + accessRequestMutator.WithRequestRef(ref) + default: + return nil, nil, fmt.Errorf("invalid ClusterReferenceType: %s", refType) + } + accessRequestMutator.WithTokenPermissions(permissions) + accessRequestMutator.WithMetadata(resources.NewMetadataMutator().WithLabels(map[string]string{ + openmcpconst.ManagedByLabel: m.controllerName, + })) + + if err := resources.CreateOrUpdateResource(ctx, m.platformClusterClient, accessRequestMutator); err != nil { + return nil, nil, fmt.Errorf("failed to create/update AccessRequest: %w", err) + } + + // TESTING MODIFICATIONS + // fake AccessRequest readiness and return cluster from fake client mapping + fakeClient, ok := m.fakeClientMapping[localName] + if !ok { + return nil, nil, fmt.Errorf("no fake client found for local cluster name %q", localName) // simulate AccessRequest not being granted + } + old := ar.DeepCopy() + ar.Status.Phase = clustersv1alpha1.REQUEST_GRANTED + ar.Status.SecretRef = &commonapi.ObjectReference{ + Name: ar.Name, + Namespace: ar.Namespace, + } + if err := m.platformClusterClient.Status().Patch(ctx, ar, client.MergeFrom(old)); err != nil { + return nil, nil, fmt.Errorf("failed to update AccessRequest status: %w", err) + } + sec := &corev1.Secret{} + sec.Name = ar.Status.SecretRef.Name + sec.Namespace = ar.Status.SecretRef.Namespace + if _, err := controllerutil.CreateOrUpdate(ctx, m.platformClusterClient, sec, func() error { + sec.Data = map[string][]byte{ + clustersv1alpha1.SecretKeyKubeconfig: []byte("fake:" + localName), + } + return nil + }); err != nil { + return nil, nil, fmt.Errorf("failed to create/update fake kubeconfig secret for AccessRequest: %w", err) + } + if fakeClient == nil { + // create new default fake client if none was provided + fakeClient = fake.NewClientBuilder().WithScheme(scheme).Build() + } + return clusters.NewTestClusterFromClient(localName, fakeClient), ar, nil +} diff --git a/lib/go.mod b/lib/go.mod index 3cb4146..6afd61a 100644 --- a/lib/go.mod +++ b/lib/go.mod @@ -7,7 +7,7 @@ replace github.com/openmcp-project/openmcp-operator/api => ../api require ( github.com/onsi/ginkgo/v2 v2.25.3 github.com/onsi/gomega v1.38.2 - github.com/openmcp-project/controller-utils v0.21.0 + github.com/openmcp-project/controller-utils v0.22.0 github.com/openmcp-project/openmcp-operator/api v0.14.0 k8s.io/api v0.34.1 k8s.io/apimachinery v0.34.1 diff --git a/lib/go.sum b/lib/go.sum index 5f2a820..d2b2553 100644 --- a/lib/go.sum +++ b/lib/go.sum @@ -73,8 +73,8 @@ github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/openmcp-project/controller-utils v0.21.0 h1:X5yN/BnJuNX9CXWgIU/cCynWFFv1KLR4zW9nnJl5Izg= -github.com/openmcp-project/controller-utils v0.21.0/go.mod h1:b8VcTK6iXFgkW6pXtIEDbaiQtzqYycVMFmElc7SFBQQ= +github.com/openmcp-project/controller-utils v0.22.0 h1:kdWGds+LOyOaOuKqWZGsJUv17e78HCr5y3bJOMSkdqE= +github.com/openmcp-project/controller-utils v0.22.0/go.mod h1:aIF4lk7agc+yCNRN5Oqg4BLlzRKsGixqwsGmxPoO5ak= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=