package testing import ( "crypto/ecdsa" "crypto/elliptic" cryptorand "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "math/big" "math/rand" "net" "os" "time" certv1 "k8s.io/api/certificates/v1" certv1beta1 "k8s.io/api/certificates/v1beta1" coordv1 "k8s.io/api/coordination/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kubeversion "k8s.io/client-go/pkg/version" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" certutil "k8s.io/client-go/util/cert" "k8s.io/client-go/util/keyutil" addonv1beta1 "open-cluster-management.io/api/addon/v1beta1" clusterv1 "open-cluster-management.io/api/cluster/v1" workapiv1 "open-cluster-management.io/api/work/v1" ) const ( TestLeaseDurationSeconds int32 = 1 TestManagedClusterName = "testmanagedcluster" ) func NewManagedCluster() *clusterv1.ManagedCluster { return &clusterv1.ManagedCluster{ ObjectMeta: metav1.ObjectMeta{ Name: TestManagedClusterName, }, } } func NewAcceptingManagedCluster() *clusterv1.ManagedCluster { managedCluster := NewManagedCluster() managedCluster.Finalizers = []string{clusterv1.ManagedClusterFinalizer} managedCluster.Spec.HubAcceptsClient = true return managedCluster } func NewAcceptedManagedCluster() *clusterv1.ManagedCluster { managedCluster := NewAcceptingManagedCluster() acceptedCondtion := NewManagedClusterCondition( clusterv1.ManagedClusterConditionHubAccepted, "True", "HubClusterAdminAccepted", "Accepted by hub cluster admin", nil, ) managedCluster.Status.Conditions = append(managedCluster.Status.Conditions, acceptedCondtion) managedCluster.Spec.LeaseDurationSeconds = TestLeaseDurationSeconds return managedCluster } func NewAvailableManagedCluster() *clusterv1.ManagedCluster { managedCluster := NewAcceptedManagedCluster() availableCondition := NewManagedClusterCondition( clusterv1.ManagedClusterConditionAvailable, "True", "ManagedClusterAvailable", "Managed cluster is available", nil, ) managedCluster.Status.Conditions = append(managedCluster.Status.Conditions, availableCondition) return managedCluster } func NewUnAvailableManagedCluster() *clusterv1.ManagedCluster { managedCluster := NewAcceptedManagedCluster() condition := NewManagedClusterCondition( clusterv1.ManagedClusterConditionAvailable, "False", "ManagedClusterUnAvailable", "Managed cluster is Unavailable", nil, ) managedCluster.Status.Conditions = append(managedCluster.Status.Conditions, condition) return managedCluster } func NewUnknownManagedCluster() *clusterv1.ManagedCluster { managedCluster := NewAcceptedManagedCluster() availableCondtion := NewManagedClusterCondition( clusterv1.ManagedClusterConditionAvailable, "Unknown", "ManagedClusterUnknown", "Managed cluster is unknown", nil, ) managedCluster.Status.Conditions = append(managedCluster.Status.Conditions, availableCondtion) return managedCluster } func NewJoinedManagedCluster() *clusterv1.ManagedCluster { managedCluster := NewAcceptedManagedCluster() joinedCondtion := NewManagedClusterCondition( clusterv1.ManagedClusterConditionJoined, "True", "ManagedClusterJoined", "Managed cluster joined", nil, ) managedCluster.Status.Conditions = append(managedCluster.Status.Conditions, joinedCondtion) return managedCluster } func NewManagedClusterWithStatus(capacity, allocatable clusterv1.ResourceList) *clusterv1.ManagedCluster { managedCluster := NewJoinedManagedCluster() managedCluster.Status.Capacity = capacity managedCluster.Status.Allocatable = allocatable managedCluster.Status.Version = clusterv1.ManagedClusterVersion{ Kubernetes: kubeversion.Get().GitVersion, } return managedCluster } func NewDeniedManagedCluster(acceptedConditionStatus string) *clusterv1.ManagedCluster { managedCluster := NewAcceptedManagedCluster() managedCluster.Spec.HubAcceptsClient = false meta.SetStatusCondition(&managedCluster.Status.Conditions, NewManagedClusterCondition( clusterv1.ManagedClusterConditionHubAccepted, acceptedConditionStatus, "", "", nil, )) return managedCluster } func NewDeletingManagedCluster() *clusterv1.ManagedCluster { now := metav1.Now() return &clusterv1.ManagedCluster{ ObjectMeta: metav1.ObjectMeta{ Name: TestManagedClusterName, DeletionTimestamp: &now, Finalizers: []string{clusterv1.ManagedClusterFinalizer}, }, } } func NewDeletingManagedClusterWithFinalizers(finalizers []string) *clusterv1.ManagedCluster { now := metav1.Now() return &clusterv1.ManagedCluster{ ObjectMeta: metav1.ObjectMeta{ Name: TestManagedClusterName, DeletionTimestamp: &now, Finalizers: finalizers, }, } } func NewManagedClusterCondition(name, status, reason, message string, lastTransition *metav1.Time) metav1.Condition { ret := metav1.Condition{ Type: name, Status: metav1.ConditionStatus(status), Reason: reason, Message: message, } if lastTransition != nil { ret.LastTransitionTime = *lastTransition } return ret } func NewManagedClusterLease(name string, renewTime time.Time) *coordv1.Lease { return &coordv1.Lease{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: TestManagedClusterName, }, Spec: coordv1.LeaseSpec{ RenewTime: &metav1.MicroTime{Time: renewTime}, }, } } func NewAddOnLease(namespace, name string, renewTime time.Time) *coordv1.Lease { return &coordv1.Lease{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, Spec: coordv1.LeaseSpec{ RenewTime: &metav1.MicroTime{Time: renewTime}, }, } } func NewManifestWork(namespace, name string, finalizers []string, labels, annotations map[string]string, deletionTimestamp *metav1.Time) *workapiv1.ManifestWork { work := &workapiv1.ManifestWork{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: name, Labels: labels, Annotations: annotations, Finalizers: finalizers, DeletionTimestamp: deletionTimestamp, }, } return work } func NewRoleBinding(namespace, name string, finalizers []string, labels map[string]string, terminated bool) *rbacv1.RoleBinding { rolebinding := &rbacv1.RoleBinding{} rolebinding.Namespace = namespace rolebinding.Name = name rolebinding.Finalizers = finalizers rolebinding.Labels = labels if terminated { now := metav1.Now() rolebinding.DeletionTimestamp = &now } return rolebinding } func NewClusterRole(name string, finalizers []string, labels map[string]string, terminated bool) *rbacv1.ClusterRole { clusterRole := &rbacv1.ClusterRole{} clusterRole.Name = name clusterRole.Finalizers = finalizers clusterRole.Labels = labels if terminated { now := metav1.Now() clusterRole.DeletionTimestamp = &now } return clusterRole } func NewClusterRoleBinding(name string, finalizers []string, labels map[string]string, terminated bool) *rbacv1.ClusterRoleBinding { clusterRoleBinding := &rbacv1.ClusterRoleBinding{} clusterRoleBinding.Name = name clusterRoleBinding.Finalizers = finalizers clusterRoleBinding.Labels = labels if terminated { now := metav1.Now() clusterRoleBinding.DeletionTimestamp = &now } return clusterRoleBinding } func NewResourceList(cpu, mem int) corev1.ResourceList { return corev1.ResourceList{ corev1.ResourceCPU: *resource.NewQuantity(int64(cpu), resource.DecimalExponent), corev1.ResourceMemory: *resource.NewQuantity(int64(1024*1024*mem), resource.BinarySI), } } func NewNode(name string, capacity, allocatable corev1.ResourceList) *corev1.Node { return &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: name, }, Status: corev1.NodeStatus{ Capacity: capacity, Allocatable: allocatable, }, } } type CSRHolder struct { Name string Labels map[string]string SignerName string CN string Orgs []string Username string ReqBlockType string } func NewCSR(holder CSRHolder) *certv1.CertificateSigningRequest { insecureRand := rand.New(rand.NewSource(0)) //nolint:gosec pk, err := ecdsa.GenerateKey(elliptic.P256(), insecureRand) if err != nil { panic(err) } csrb, err := x509.CreateCertificateRequest(insecureRand, &x509.CertificateRequest{ Subject: pkix.Name{ CommonName: holder.CN, Organization: holder.Orgs, }, DNSNames: []string{}, EmailAddresses: []string{}, IPAddresses: []net.IP{}, }, pk) if err != nil { panic(err) } return &certv1.CertificateSigningRequest{ ObjectMeta: metav1.ObjectMeta{ Name: holder.Name, GenerateName: "csr-", Labels: holder.Labels, }, Spec: certv1.CertificateSigningRequestSpec{ Username: holder.Username, Usages: []certv1.KeyUsage{}, SignerName: holder.SignerName, Request: pem.EncodeToMemory(&pem.Block{Type: holder.ReqBlockType, Bytes: csrb}), }, } } func NewV1beta1CSR(holder CSRHolder) *certv1beta1.CertificateSigningRequest { insecureRand := rand.New(rand.NewSource(0)) //nolint:gosec pk, err := ecdsa.GenerateKey(elliptic.P256(), insecureRand) if err != nil { panic(err) } csrb, err := x509.CreateCertificateRequest(insecureRand, &x509.CertificateRequest{ Subject: pkix.Name{ CommonName: holder.CN, Organization: holder.Orgs, }, DNSNames: []string{}, EmailAddresses: []string{}, IPAddresses: []net.IP{}, }, pk) if err != nil { panic(err) } return &certv1beta1.CertificateSigningRequest{ ObjectMeta: metav1.ObjectMeta{ Name: holder.Name, GenerateName: "csr-", Labels: holder.Labels, }, Spec: certv1beta1.CertificateSigningRequestSpec{ Username: holder.Username, Usages: []certv1beta1.KeyUsage{}, SignerName: &holder.SignerName, Request: pem.EncodeToMemory(&pem.Block{Type: holder.ReqBlockType, Bytes: csrb}), }, } } func NewDeniedCSR(holder CSRHolder) *certv1.CertificateSigningRequest { csr := NewCSR(holder) csr.Status.Conditions = append(csr.Status.Conditions, certv1.CertificateSigningRequestCondition{ Type: certv1.CertificateDenied, Status: corev1.ConditionTrue, }) return csr } func NewDeniedV1beta1CSR(holder CSRHolder) *certv1beta1.CertificateSigningRequest { csr := NewV1beta1CSR(holder) csr.Status.Conditions = append(csr.Status.Conditions, certv1beta1.CertificateSigningRequestCondition{ Type: certv1beta1.CertificateDenied, Status: corev1.ConditionTrue, }) return csr } func NewApprovedCSR(holder CSRHolder) *certv1.CertificateSigningRequest { csr := NewCSR(holder) csr.Status.Conditions = append(csr.Status.Conditions, certv1.CertificateSigningRequestCondition{ Type: certv1.CertificateApproved, Status: corev1.ConditionTrue, }) return csr } func NewApprovedV1beta1CSR(holder CSRHolder) *certv1beta1.CertificateSigningRequest { csr := NewV1beta1CSR(holder) csr.Status.Conditions = append(csr.Status.Conditions, certv1beta1.CertificateSigningRequestCondition{ Type: certv1beta1.CertificateApproved, Status: corev1.ConditionTrue, }) return csr } func NewKubeconfig(clusterName, server, proxyURL, ca string, caData, key, cert []byte) []byte { var clientKey, clientCertificate string var clientKeyData, clientCertificateData []byte if key != nil { clientKeyData = key } else { clientKey = "tls.key" } if cert != nil { clientCertificateData = cert } else { clientCertificate = "tls.crt" } kubeconfig := clientcmdapi.Config{ Clusters: map[string]*clientcmdapi.Cluster{ clusterName: { Server: server, ProxyURL: proxyURL, CertificateAuthority: ca, CertificateAuthorityData: caData, }}, AuthInfos: map[string]*clientcmdapi.AuthInfo{"default-auth": { ClientCertificate: clientCertificate, ClientCertificateData: clientCertificateData, ClientKey: clientKey, ClientKeyData: clientKeyData, }}, Contexts: map[string]*clientcmdapi.Context{"default-context": { Cluster: clusterName, AuthInfo: "default-auth", Namespace: "default", }}, CurrentContext: "default-context", } kubeconfigData, err := clientcmd.Write(kubeconfig) if err != nil { panic(err) } return kubeconfigData } type TestCert struct { Cert []byte Key []byte } // TODO: Remove this struct once we have the function fully implemented for the AWSIRSADriver type TestIrsaRequest struct { } func NewHubKubeconfigSecret(namespace, name, resourceVersion string, cert *TestCert, data map[string][]byte) *corev1.Secret { secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: name, ResourceVersion: resourceVersion, }, Data: data, } if cert != nil && cert.Cert != nil { secret.Data["tls.crt"] = cert.Cert } if cert != nil && cert.Key != nil { secret.Data["tls.key"] = cert.Key } return secret } func NewTestCertWithSubject(subject pkix.Name, duration time.Duration) *TestCert { caKey, err := rsa.GenerateKey(cryptorand.Reader, 2048) if err != nil { panic(err) } caCert, err := certutil.NewSelfSignedCACert(certutil.Config{CommonName: "open-cluster-management.io"}, caKey) if err != nil { panic(err) } key, err := rsa.GenerateKey(cryptorand.Reader, 2048) if err != nil { panic(err) } certDERBytes, err := x509.CreateCertificate( cryptorand.Reader, &x509.Certificate{ Subject: subject, SerialNumber: big.NewInt(1), NotBefore: caCert.NotBefore, NotAfter: time.Now().Add(duration).UTC(), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, }, caCert, key.Public(), caKey, ) if err != nil { panic(err) } cert, err := x509.ParseCertificate(certDERBytes) if err != nil { panic(err) } return &TestCert{ Cert: pem.EncodeToMemory(&pem.Block{ Type: certutil.CertificateBlockType, Bytes: cert.Raw, }), Key: pem.EncodeToMemory(&pem.Block{ Type: keyutil.RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(key), }), } } func NewTestCert(commonName string, duration time.Duration) *TestCert { return NewTestCertWithSubject(pkix.Name{ CommonName: commonName, }, duration) } func WriteFile(filename string, data []byte) { if err := os.WriteFile(filename, data, 0600); err != nil { panic(err) } } func NewManagedClusterAddons(name, namespace string, finalizers []string, deletionTimestamp *metav1.Time) *addonv1beta1.ManagedClusterAddOn { return &addonv1beta1.ManagedClusterAddOn{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, Finalizers: finalizers, DeletionTimestamp: deletionTimestamp, }, } }