diff --git a/Makefile b/Makefile index 55101f96..a702e6e1 100644 --- a/Makefile +++ b/Makefile @@ -68,7 +68,6 @@ push: docker push ${IMAGE_PATH} build-push: image push - docker push ${IMAGE_PATH} .PHONY: coverage coverage: test diff --git a/pkg/auth/auth_service.go b/pkg/auth/auth_service.go index 20ef32dd..d87fccaf 100644 --- a/pkg/auth/auth_service.go +++ b/pkg/auth/auth_service.go @@ -22,7 +22,6 @@ import ( "k8s.io/api/core/v1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" "k8s.io/klog/v2" "github.com/oracle/oci-native-ingress-controller/pkg/types" @@ -30,10 +29,10 @@ import ( const httpClientTimeout = 20 * time.Second -func GetConfigurationProvider(ctx context.Context, opts types.IngressOpts) (common.ConfigurationProvider, error) { - auth, err := RetrieveAuthConfig(ctx, opts, opts.LeaseLockNamespace) +func GetConfigurationProvider(ctx context.Context, opts types.IngressOpts, client kubernetes.Interface) (common.ConfigurationProvider, error) { + auth, err := RetrieveAuthConfig(ctx, opts, opts.LeaseLockNamespace, client) if err != nil { - klog.Fatalf("Unable to handle authentication parameters", err) + klog.Error("Unable to handle authentication parameters", err) return nil, err } return getConfProviderFromAuth(auth) @@ -71,7 +70,7 @@ func setHTTPClientTimeout( } } -func RetrieveAuthConfig(ctx context.Context, opts types.IngressOpts, namespace string) (*types.Auth, error) { +func RetrieveAuthConfig(ctx context.Context, opts types.IngressOpts, namespace string, client kubernetes.Interface) (*types.Auth, error) { authType := opts.AuthType principalType, err := types.MapToPrincipalType(authType) if err != nil { @@ -86,27 +85,27 @@ func RetrieveAuthConfig(ctx context.Context, opts types.IngressOpts, namespace s authConfigSecretName := opts.AuthSecretName // read it from k8s api - secret, err := readK8sSecret(ctx, namespace, authConfigSecretName) + secret, err := readK8sSecret(ctx, namespace, authConfigSecretName, client) if err != nil { - klog.Fatalf("Error while reading secret from k8s api", err) + klog.Error("Error while reading secret from k8s api", err) return nil, fmt.Errorf("error retrieving secret: %v", authConfigSecretName) } klog.Infof("secret is retrieved from kubernetes api: %s", authConfigSecretName) if len(secret.Data) == 0 || len(secret.Data["config"]) == 0 { - klog.Fatalf("Empty Configuration is found in the secret %s", authConfigSecretName) + klog.Error("Empty Configuration is found in the secret %s", authConfigSecretName) return nil, fmt.Errorf("auth config data is empty: %v", authConfigSecretName) } authCfg, err := ParseAuthConfig(secret, authConfigSecretName) if err != nil { - klog.Fatalf("Missing auth config data: %s", authConfigSecretName) + klog.Error("Missing auth config data: %s", authConfigSecretName) return nil, fmt.Errorf("missing auth config data: %v", err) } err = authCfg.Validate() if err != nil { - klog.Fatalf("Missing auth config data %s", authConfigSecretName) + klog.Error("Missing auth config data %s", authConfigSecretName) return nil, fmt.Errorf("missing auth config data: %v", err) } auth.Config = *authCfg @@ -117,15 +116,15 @@ func RetrieveAuthConfig(ctx context.Context, opts types.IngressOpts, namespace s func ParseAuthConfig(secret *v1.Secret, authConfigSecretName string) (*types.AuthConfig, error) { authYaml := &types.AuthConfigYaml{} err := yaml.Unmarshal(secret.Data["config"], &authYaml) - if err != nil { - klog.Fatalf("Invalid auth config data %s", authConfigSecretName) + if err != nil || authYaml.Auth == nil { + klog.Errorf("Invalid auth config data %s", authConfigSecretName) return nil, fmt.Errorf("invalid auth config data: %v", authConfigSecretName) } if len(secret.Data["private-key"]) > 0 { authYaml.Auth["privateKey"] = string(secret.Data["private-key"]) } else { - klog.Fatalf("Invalid user auth private key %s", authConfigSecretName) + klog.Errorf("Invalid user auth private key %s", authConfigSecretName) return nil, fmt.Errorf("invalid user auth config data: %v", authConfigSecretName) } @@ -133,24 +132,12 @@ func ParseAuthConfig(secret *v1.Secret, authConfigSecretName string) (*types.Aut authCfg := &types.AuthConfig{} err = yaml.Unmarshal(authCfgYaml, &authCfg) if err != nil { - klog.Fatalf("Invalid auth config data %s", authConfigSecretName) + klog.Errorf("Invalid auth config data %s", authConfigSecretName) return nil, fmt.Errorf("invalid auth config data: %v", authConfigSecretName) } return authCfg, nil } -func readK8sSecret(ctx context.Context, namespace string, - secretName string) (*v1.Secret, error) { - clusterCfg, err := rest.InClusterConfig() - if err != nil { - return &v1.Secret{}, fmt.Errorf("can not get cluster config. error: %v", err) - } - - clientSet, err := kubernetes.NewForConfig(clusterCfg) - if err != nil { - return &v1.Secret{}, fmt.Errorf("can not initialize kubernetes client. error: %v", err) - } - - k8client := clientSet.CoreV1() - return k8client.Secrets(namespace).Get(ctx, secretName, metaV1.GetOptions{}) +func readK8sSecret(ctx context.Context, namespace string, secretName string, client kubernetes.Interface) (*v1.Secret, error) { + return client.CoreV1().Secrets(namespace).Get(ctx, secretName, metaV1.GetOptions{}) } diff --git a/pkg/auth/auth_service_test.go b/pkg/auth/auth_service_test.go index 58ccbfef..9ecba0d4 100644 --- a/pkg/auth/auth_service_test.go +++ b/pkg/auth/auth_service_test.go @@ -2,14 +2,15 @@ package auth import ( "context" - "encoding/base64" "fmt" + "net/http" "testing" . "github.com/onsi/gomega" "github.com/oracle/oci-native-ingress-controller/pkg/types" + "github.com/oracle/oci-native-ingress-controller/pkg/util" v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + fakeclientset "k8s.io/client-go/kubernetes/fake" ) const ( @@ -17,12 +18,76 @@ const ( data = "IwojIE9DSSBOYXRpdmUgSW5ncmVzcyBDb250cm9sbGVyCiMKIyBDb3B5cmlnaHQgKGMpIDIwMjMgT3JhY2xlIEFtZXJpY2EsIEluYy4gYW5kIGl0cyBhZmZpbGlhdGVzLgojIExpY2Vuc2VkIHVuZGVyIHRoZSBVbml2ZXJzYWwgUGVybWlzc2l2ZSBMaWNlbnNlIHYgMS4wIGFzIHNob3duIGF0IGh0dHBzOi8vb3NzLm9yYWNsZS5jb20vbGljZW5zZXMvdXBsLwojCmF1dGg6CiAgcmVnaW9uOiB1cy1hc2hidXJuLTEKICBwYXNzcGhyYXNlOiBwYXNzCiAgdXNlcjogb2NpZDEudXNlci5vYzEuLmFhYWFhYWFhX2V4YW1wbGUKICBmaW5nZXJwcmludDogNjc6ZDk6NzQ6NGI6MjE6ZXhhbXBsZQogIHRlbmFuY3k6IG9jaWQxLnRlbmFuY3kub2MxLi5hYWFhYWFhYV9leGFtcGxl" ) +func setUp(secret *v1.Secret, setClient bool) *fakeclientset.Clientset { + client := fakeclientset.NewSimpleClientset() + if setClient { + action := "get" + resource := "secrets" + obj := secret + util.FakeClientGetCall(client, action, resource, obj) + } + return client +} + +func TestGetConfigurationProviderSuccess(t *testing.T) { + RegisterTestingT(t) + ctx := context.TODO() + opts := types.IngressOpts{ + AuthType: "user", + AuthSecretName: "oci-config", + } + configName := "config" + privateKey := "private-key" + secret := util.GetSampleSecret(configName, privateKey, data, PrivateKey) + client := setUp(secret, true) + + auth, err := GetConfigurationProvider(ctx, opts, client) + Expect(auth != nil).Should(BeTrue()) + Expect(err).Should(BeNil()) +} + +func TestGetConfigurationProviderFailSecret(t *testing.T) { + RegisterTestingT(t) + ctx := context.TODO() + opts := types.IngressOpts{ + AuthType: "user", + AuthSecretName: "oci-config", + } + secret := util.GetSampleSecret("test", "error", data, PrivateKey) + + client := setUp(secret, false) + auth, err := GetConfigurationProvider(ctx, opts, client) + Expect(auth == nil).Should(BeTrue()) + Expect(err != nil).Should(BeTrue()) + Expect(err.Error()).Should(Equal("error retrieving secret: oci-config")) + + client = setUp(secret, true) + auth, err = GetConfigurationProvider(ctx, opts, client) + Expect(auth == nil).Should(BeTrue()) + Expect(err != nil).Should(BeTrue()) + Expect(err.Error()).Should(Equal("auth config data is empty: oci-config")) + + secret = util.GetSampleSecret("config", "error", data, PrivateKey) + client = setUp(secret, true) + auth, err = GetConfigurationProvider(ctx, opts, client) + Expect(auth == nil).Should(BeTrue()) + Expect(err != nil).Should(BeTrue()) + Expect(err.Error()).Should(Equal("missing auth config data: invalid user auth config data: oci-config")) + + secret = util.GetSampleSecret("configs", "error", data, PrivateKey) + client = setUp(secret, true) + auth, err = GetConfigurationProvider(ctx, opts, client) + Expect(auth == nil).Should(BeTrue()) + Expect(err != nil).Should(BeTrue()) + Expect(err.Error()).Should(Equal("auth config data is empty: oci-config")) +} + func TestRetrieveAuthConfigInstanceAuthType(t *testing.T) { RegisterTestingT(t) opts := types.IngressOpts{ AuthType: "instance", } - cfg, err := RetrieveAuthConfig(context.TODO(), opts, "test") + cfg, err := RetrieveAuthConfig(context.TODO(), opts, "test", nil) Expect(err == nil).Should(BeTrue()) Expect(cfg.Type).Should(Equal(types.Instance)) @@ -33,7 +98,7 @@ func TestRetrieveAuthConfigInstanceAuthTypeTestRetrieveAuthConfigInvalidAuthType opts := types.IngressOpts{ AuthType: authType, } - _, err := RetrieveAuthConfig(context.TODO(), opts, "test") + _, err := RetrieveAuthConfig(context.TODO(), opts, "test", nil) Expect(err != nil).Should(BeTrue()) Expect(err.Error()).Should(Equal(fmt.Sprintf("invalid auth principal type, %s", authType))) @@ -41,7 +106,9 @@ func TestRetrieveAuthConfigInstanceAuthTypeTestRetrieveAuthConfigInvalidAuthType func TestParseAuthConfig(t *testing.T) { RegisterTestingT(t) - secret := getSampleSecret() + configName := "config" + privateKey := "private-key" + secret := util.GetSampleSecret(configName, privateKey, data, PrivateKey) authCfg, err := ParseAuthConfig(secret, "oci-config") Expect(err == nil).Should(BeTrue()) Expect(authCfg.TenancyID).Should(Equal("ocid1.tenancy.oc1..aaaaaaaa_example")) @@ -52,17 +119,30 @@ func TestParseAuthConfig(t *testing.T) { Expect(err == nil).Should(BeTrue()) } -func getSampleSecret() *v1.Secret { - dat, _ := base64.StdEncoding.DecodeString(data) - secret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: "oci-config", - }, - Data: map[string][]byte{ - "config": []byte(dat), - "private-key": []byte(PrivateKey), - }, - } - return secret +func TestParseAuthConfigWithError(t *testing.T) { + RegisterTestingT(t) + secret := util.GetSampleSecret("error", "", data, PrivateKey) + _, err := ParseAuthConfig(secret, "oci-configs") + Expect(err != nil).Should(BeTrue()) + Expect(err.Error()).Should(Equal("invalid auth config data: oci-configs")) + + secret = util.GetSampleSecret("config", "", data, PrivateKey) + _, err = ParseAuthConfig(secret, "oci-configs") + Expect(err != nil).Should(BeTrue()) + Expect(err.Error()).Should(Equal("invalid user auth config data: oci-configs")) + +} + +func TestSetHTTPClientTimeout(t *testing.T) { + RegisterTestingT(t) + timeout := setHTTPClientTimeout(httpClientTimeout) + Expect(timeout != nil).Should(Equal(true)) + dis, err := timeout(&http.Client{}) + Expect(dis).Should(Not(BeNil())) + Expect(err).Should(BeNil()) + + dis, err = timeout(nil) + Expect(dis).Should(BeNil()) + Expect(err).Should(Not(BeNil())) + Expect(err.Error()).Should(Equal("unable to modify unknown HTTP client type")) } diff --git a/pkg/certificate/certificate.go b/pkg/certificate/certificate.go index 617f7d38..6649f2aa 100644 --- a/pkg/certificate/certificate.go +++ b/pkg/certificate/certificate.go @@ -17,67 +17,58 @@ import ( "github.com/oracle/oci-go-sdk/v65/certificates" "github.com/oracle/oci-go-sdk/v65/certificatesmanagement" + . "github.com/oracle/oci-native-ingress-controller/pkg/oci/client" "github.com/oracle/oci-native-ingress-controller/pkg/util" "k8s.io/klog/v2" ) -type certCacheObj struct { - cert *certificatesmanagement.Certificate - age time.Time -} - -type caBundleCacheObj struct { - caBundle *certificatesmanagement.CaBundle - age time.Time -} - type CertificatesClient struct { - managementClient *certificatesmanagement.CertificatesManagementClient - certificatesClient *certificates.CertificatesClient + ManagementClient CertificateManagementInterface + CertificatesClient CertificateInterface certMu sync.Mutex caMu sync.Mutex - certCache map[string]*certCacheObj - caBundleCache map[string]*caBundleCacheObj + CertCache map[string]*CertCacheObj + CaBundleCache map[string]*CaBundleCacheObj } -func New(managementClient *certificatesmanagement.CertificatesManagementClient, - certificateClient *certificates.CertificatesClient) *CertificatesClient { +func New(managementClient CertificateManagementInterface, + certificateClient CertificateInterface) *CertificatesClient { return &CertificatesClient{ - managementClient: managementClient, - certificatesClient: certificateClient, - certCache: map[string]*certCacheObj{}, - caBundleCache: map[string]*caBundleCacheObj{}, + ManagementClient: managementClient, + CertificatesClient: certificateClient, + CertCache: map[string]*CertCacheObj{}, + CaBundleCache: map[string]*CaBundleCacheObj{}, } } func (certificatesClient *CertificatesClient) setCertCache(cert *certificatesmanagement.Certificate) { certificatesClient.certMu.Lock() - certificatesClient.certCache[*cert.Id] = &certCacheObj{cert, time.Now()} + certificatesClient.CertCache[*cert.Id] = &CertCacheObj{Cert: cert, Age: time.Now()} certificatesClient.certMu.Unlock() } -func (certificatesClient *CertificatesClient) getFromCertCache(certId string) *certCacheObj { +func (certificatesClient *CertificatesClient) getFromCertCache(certId string) *CertCacheObj { certificatesClient.certMu.Lock() defer certificatesClient.certMu.Unlock() - return certificatesClient.certCache[certId] + return certificatesClient.CertCache[certId] } func (certificatesClient *CertificatesClient) setCaBundleCache(caBundle *certificatesmanagement.CaBundle) { certificatesClient.caMu.Lock() - certificatesClient.caBundleCache[*caBundle.Id] = &caBundleCacheObj{caBundle, time.Now()} + certificatesClient.CaBundleCache[*caBundle.Id] = &CaBundleCacheObj{CaBundle: caBundle, Age: time.Now()} certificatesClient.caMu.Unlock() } -func (certificatesClient *CertificatesClient) getFromCaBundleCache(id string) *caBundleCacheObj { +func (certificatesClient *CertificatesClient) getFromCaBundleCache(id string) *CaBundleCacheObj { certificatesClient.caMu.Lock() defer certificatesClient.caMu.Unlock() - return certificatesClient.caBundleCache[id] + return certificatesClient.CaBundleCache[id] } func (certificatesClient *CertificatesClient) CreateCertificate(ctx context.Context, req certificatesmanagement.CreateCertificateRequest) (*certificatesmanagement.Certificate, error) { - resp, err := certificatesClient.managementClient.CreateCertificate(ctx, req) + resp, err := certificatesClient.ManagementClient.CreateCertificate(ctx, req) if err != nil { klog.Errorf("Error creating certificate %s, %s ", *req.Name, err.Error()) return nil, err @@ -88,7 +79,7 @@ func (certificatesClient *CertificatesClient) CreateCertificate(ctx context.Cont func (certificatesClient *CertificatesClient) CreateCaBundle(ctx context.Context, req certificatesmanagement.CreateCaBundleRequest) (*certificatesmanagement.CaBundle, error) { - resp, err := certificatesClient.managementClient.CreateCaBundle(ctx, req) + resp, err := certificatesClient.ManagementClient.CreateCaBundle(ctx, req) if err != nil { klog.Errorf("Error creating ca bundle %s, %s ", *req.Name, err.Error()) return nil, err @@ -100,7 +91,7 @@ func (certificatesClient *CertificatesClient) CreateCaBundle(ctx context.Context func (certificatesClient *CertificatesClient) GetCertificate(ctx context.Context, req certificatesmanagement.GetCertificateRequest) (*certificatesmanagement.Certificate, error) { klog.Infof("Getting certificate for ocid %s ", *req.CertificateId) - resp, err := certificatesClient.managementClient.GetCertificate(ctx, req) + resp, err := certificatesClient.ManagementClient.GetCertificate(ctx, req) if err != nil { klog.Errorf("Error getting certificate %s, %s ", *req.CertificateId, err.Error()) return nil, err @@ -112,7 +103,7 @@ func (certificatesClient *CertificatesClient) GetCertificate(ctx context.Context func (certificatesClient *CertificatesClient) ListCertificates(ctx context.Context, req certificatesmanagement.ListCertificatesRequest) (*certificatesmanagement.CertificateCollection, *string, error) { klog.Infof("Listing certificates with request %s", util.PrettyPrint(req)) - resp, err := certificatesClient.managementClient.ListCertificates(ctx, req) + resp, err := certificatesClient.ManagementClient.ListCertificates(ctx, req) if err != nil { klog.Errorf("Error listing certificates for request %s, %s ", util.PrettyPrint(req), err.Error()) return nil, nil, err @@ -123,7 +114,7 @@ func (certificatesClient *CertificatesClient) ListCertificates(ctx context.Conte func (certificatesClient *CertificatesClient) ScheduleCertificateDeletion(ctx context.Context, req certificatesmanagement.ScheduleCertificateDeletionRequest) error { - _, err := certificatesClient.managementClient.ScheduleCertificateDeletion(ctx, req) + _, err := certificatesClient.ManagementClient.ScheduleCertificateDeletion(ctx, req) if err != nil { klog.Errorf("Error scheduling certificate for deletion, certificateId %s, %s ", *req.CertificateId, err.Error()) return err @@ -134,7 +125,7 @@ func (certificatesClient *CertificatesClient) ScheduleCertificateDeletion(ctx co func (certificatesClient *CertificatesClient) GetCaBundle(ctx context.Context, req certificatesmanagement.GetCaBundleRequest) (*certificatesmanagement.CaBundle, error) { klog.Infof("Getting ca bundle with ocid %s ", *req.CaBundleId) - resp, err := certificatesClient.managementClient.GetCaBundle(ctx, req) + resp, err := certificatesClient.ManagementClient.GetCaBundle(ctx, req) if err != nil { klog.Errorf("Error getting certificate %s, %s ", *req.CaBundleId, err.Error()) return nil, err @@ -146,7 +137,7 @@ func (certificatesClient *CertificatesClient) GetCaBundle(ctx context.Context, func (certificatesClient *CertificatesClient) ListCaBundles(ctx context.Context, req certificatesmanagement.ListCaBundlesRequest) (*certificatesmanagement.CaBundleCollection, error) { klog.Infof("Getting ca bundles using request %s ", util.PrettyPrint(req)) - resp, err := certificatesClient.managementClient.ListCaBundles(ctx, req) + resp, err := certificatesClient.ManagementClient.ListCaBundles(ctx, req) if err != nil { klog.Errorf("Error listing ca bundles for request %s, %s ", util.PrettyPrint(req), err.Error()) return nil, err @@ -158,7 +149,7 @@ func (certificatesClient *CertificatesClient) ListCaBundles(ctx context.Context, func (certificatesClient *CertificatesClient) DeleteCaBundle(ctx context.Context, req certificatesmanagement.DeleteCaBundleRequest) (*http.Response, error) { klog.Infof("Deleting ca bundle with ocid %s ", *req.CaBundleId) - resp, err := certificatesClient.managementClient.DeleteCaBundle(ctx, req) + resp, err := certificatesClient.ManagementClient.DeleteCaBundle(ctx, req) if err != nil { klog.Errorf("Error deleting ca bundle %s, %s ", *req.CaBundleId, err.Error()) return nil, err @@ -170,7 +161,7 @@ func (certificatesClient *CertificatesClient) DeleteCaBundle(ctx context.Context func (certificatesClient *CertificatesClient) GetCertificateBundle(ctx context.Context, req certificates.GetCertificateBundleRequest) (certificates.CertificateBundle, error) { klog.Infof("Getting certificate bundle for certificate ocid %s ", *req.CertificateId) - resp, err := certificatesClient.certificatesClient.GetCertificateBundle(ctx, req) + resp, err := certificatesClient.CertificatesClient.GetCertificateBundle(ctx, req) if err != nil { klog.Errorf("Error getting certificate bundle for certificate %s, %s ", *req.CertificateId, err.Error()) return nil, err diff --git a/pkg/certificate/certificate_test.go b/pkg/certificate/certificate_test.go new file mode 100644 index 00000000..fd42bc01 --- /dev/null +++ b/pkg/certificate/certificate_test.go @@ -0,0 +1,74 @@ +package certificate + +import ( + "context" + "testing" + + . "github.com/onsi/gomega" + "github.com/oracle/oci-go-sdk/v65/certificatesmanagement" + "github.com/oracle/oci-go-sdk/v65/common" + . "github.com/oracle/oci-native-ingress-controller/pkg/oci/client" +) + +func setup() (CertificateInterface, CertificateManagementInterface) { + certClient := GetCertClient() + certManageClient := GetCertManageClient() + return certClient, certManageClient +} + +func TestNew(t *testing.T) { + RegisterTestingT(t) + certClient, certManageClient := setup() + + client := New(certManageClient, certClient) + Expect(client).Should(Not(BeNil())) +} + +func TestScheduleCertificateDeletion(t *testing.T) { + RegisterTestingT(t) + certClient, certManageClient := setup() + id := "id" + client := New(certManageClient, certClient) + request := certificatesmanagement.ScheduleCertificateDeletionRequest{ + CertificateId: &id, + } + err := client.ScheduleCertificateDeletion(context.TODO(), request) + Expect(err).Should(BeNil()) + + id = "error" + request = certificatesmanagement.ScheduleCertificateDeletionRequest{ + CertificateId: &id, + ScheduleCertificateDeletionDetails: certificatesmanagement.ScheduleCertificateDeletionDetails{}, + OpcRequestId: nil, + IfMatch: nil, + RequestMetadata: common.RequestMetadata{}, + } + err = client.ScheduleCertificateDeletion(context.TODO(), request) + Expect(err).Should(Not(BeNil())) +} + +func TestDeleteCaBundle(t *testing.T) { + RegisterTestingT(t) + certClient, certManageClient := setup() + id := "id" + client := New(certManageClient, certClient) + request := getDeleteCaBundleRequest(id) + res, err := client.DeleteCaBundle(context.TODO(), request) + + Expect(err).Should(BeNil()) + Expect(res.Status).Should(Equal("200")) + + request = getDeleteCaBundleRequest("error") + res, err = client.DeleteCaBundle(context.TODO(), request) + Expect(err).Should(Not(BeNil())) +} + +func getDeleteCaBundleRequest(id string) certificatesmanagement.DeleteCaBundleRequest { + request := certificatesmanagement.DeleteCaBundleRequest{ + CaBundleId: &id, + OpcRequestId: &id, + IfMatch: nil, + RequestMetadata: common.RequestMetadata{}, + } + return request +} diff --git a/pkg/certificate/util.go b/pkg/certificate/util.go index f797ecd3..75733b56 100644 --- a/pkg/certificate/util.go +++ b/pkg/certificate/util.go @@ -11,10 +11,16 @@ package certificate import ( "context" + "fmt" "strings" "time" + "github.com/oracle/oci-go-sdk/v65/certificates" "github.com/oracle/oci-go-sdk/v65/certificatesmanagement" + ociloadbalancer "github.com/oracle/oci-go-sdk/v65/loadbalancer" + "github.com/oracle/oci-native-ingress-controller/pkg/state" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" "k8s.io/klog/v2" "github.com/oracle/oci-native-ingress-controller/pkg/util" @@ -48,59 +54,12 @@ func CreateImportedTypeCertificate(caCertificatesChain *string, serverCertificat return createCertificate, nil } -func CreateInternalCaTypeCertificate(issuerCertificateAuthorityId *string, subject *string, - subjectAlternativeNameMap map[string]string, certificateName string, compartmentId string, - certificatesClient *CertificatesClient) (*certificatesmanagement.Certificate, error) { - - certificateSubject := &certificatesmanagement.CertificateSubject{ - CommonName: subject, - } - - var subjectAlternativeNames []certificatesmanagement.CertificateSubjectAlternativeName - for san := range subjectAlternativeNameMap { - sanType := subjectAlternativeNameMap[san] - sanValue := san - sanInstance := certificatesmanagement.CertificateSubjectAlternativeName{ - Type: certificatesmanagement.CertificateSubjectAlternativeNameTypeEnum(sanType), - Value: &sanValue, - } - subjectAlternativeNames = append(subjectAlternativeNames, sanInstance) - } - - configDetails := certificatesmanagement.CreateCertificateIssuedByInternalCaConfigDetails{ - IssuerCertificateAuthorityId: issuerCertificateAuthorityId, - Subject: certificateSubject, - SubjectAlternativeNames: subjectAlternativeNames, - CertificateProfileType: certificatesmanagement.CertificateProfileTypeTlsServerOrClient, - KeyAlgorithm: certificatesmanagement.KeyAlgorithmRsa4096, - SignatureAlgorithm: certificatesmanagement.SignatureAlgorithmSha256WithRsa, - } - - certificateDetails := certificatesmanagement.CreateCertificateDetails{ - Name: &certificateName, - CertificateConfig: configDetails, - CompartmentId: &compartmentId, - } - createCertificateRequest := certificatesmanagement.CreateCertificateRequest{ - CreateCertificateDetails: certificateDetails, - OpcRetryToken: &certificateName, - } - - createCertificate, err := certificatesClient.CreateCertificate(context.TODO(), createCertificateRequest) - if err != nil { - return nil, err - } - - klog.Infof("Created a certificate with ocid %s", *createCertificate.Id) - return createCertificate, nil -} - func GetCertificate(certificateId *string, certificatesClient *CertificatesClient) (*certificatesmanagement.Certificate, error) { certCacheObj := certificatesClient.getFromCertCache(*certificateId) if certCacheObj != nil { now := time.Now() - if now.Sub(certCacheObj.age).Minutes() < util.CertificateCacheMaxAgeInMinutes { - return certCacheObj.cert, nil + if now.Sub(certCacheObj.Age).Minutes() < util.CertificateCacheMaxAgeInMinutes { + return certCacheObj.Cert, nil } klog.Infof("Refreshing certificate %s", *certificateId) } @@ -168,7 +127,7 @@ func FindCaBundleWithName(certificateName string, compartmentId string, func GetCaBundle(caBundleId string, certificatesClient *CertificatesClient) (*certificatesmanagement.CaBundle, error) { caBundleCacheObj := certificatesClient.getFromCaBundleCache(caBundleId) if caBundleCacheObj != nil { - return caBundleCacheObj.caBundle, nil + return caBundleCacheObj.CaBundle, nil } klog.Infof("Getting ca bundle for id %s.", caBundleId) @@ -204,66 +163,203 @@ func CreateCaBundle(certificateName string, compartmentId string, certificatesCl return createCaBundle, nil } -func ScheduleCertificatesForDeletionWithNamePrefix(prefix string, compartmentId string, certificatesClient *CertificatesClient) []error { - listRequest := certificatesmanagement.ListCertificatesRequest{ - CompartmentId: &compartmentId, - LifecycleState: certificatesmanagement.ListCertificatesLifecycleStateActive, +func CreateOrGetCertificateForListener(namespace string, secretName string, compartmentId string, certificatesClient *CertificatesClient, client kubernetes.Interface) (*string, error) { + certificateName := getCertificateNameFromSecret(secretName) + certificateId, err := FindCertificateWithName(certificateName, compartmentId, certificatesClient) + if err != nil { + return nil, err } - var errorList []error - for { - certificates, nextPage, err := certificatesClient.ListCertificates(context.TODO(), listRequest) + if certificateId == nil { + tlsSecretData, err := getTlsSecretContent(namespace, secretName, client) if err != nil { - return append(errorList, err) - } - for i := range certificates.Items { - cert := certificates.Items[i] - if strings.HasPrefix(*cert.Name, prefix) { - klog.Infof("Found a certificate %s with prefix %s. This will be scheduled for deletion.", *cert.Name, prefix) - deleteRequest := certificatesmanagement.ScheduleCertificateDeletionRequest{ - CertificateId: cert.Id, - } - klog.Infof("Delete certificate request %s", util.PrettyPrint(deleteRequest)) - err := certificatesClient.ScheduleCertificateDeletion(context.TODO(), deleteRequest) - if err != nil { - klog.Error("Error deleting certificate %s, %s", *cert.Name, err.Error()) - errorList = append(errorList, err) - } - } + return nil, err } - if nextPage == nil { - break + createCertificate, err := CreateImportedTypeCertificate(tlsSecretData.CaCertificateChain, tlsSecretData.ServerCertificate, + tlsSecretData.PrivateKey, certificateName, compartmentId, certificatesClient) + if err != nil { + return nil, err } - listRequest.Page = nextPage + certificateId = createCertificate.Id } - return errorList + return certificateId, nil } -func DeleteCaBundlesWithNamePrefix(prefix string, compartmentId string, certificatesClient *CertificatesClient) error { - listRequest := certificatesmanagement.ListCaBundlesRequest{ - CompartmentId: &compartmentId, - LifecycleState: certificatesmanagement.ListCaBundlesLifecycleStateActive, +func CreateOrGetCaBundleForBackendSet(namespace string, secretName string, compartmentId string, certificatesClient *CertificatesClient, client kubernetes.Interface) (*string, error) { + certificateName := getCertificateNameFromSecret(secretName) + caBundleId, err := FindCaBundleWithName(certificateName, compartmentId, certificatesClient) + if err != nil { + return nil, err } - caBundles, err := certificatesClient.ListCaBundles(context.TODO(), listRequest) + if caBundleId == nil { + tlsSecretData, err := getTlsSecretContent(namespace, secretName, client) + if err != nil { + return nil, err + } + createCaBundle, err := CreateCaBundle(certificateName, compartmentId, certificatesClient, tlsSecretData.CaCertificateChain) + if err != nil { + return nil, err + } + caBundleId = createCaBundle.Id + } + return caBundleId, nil +} + +type TLSSecretData struct { + // This would hold server certificate and any chain of trust. + CaCertificateChain *string + ServerCertificate *string + PrivateKey *string +} + +func getTlsSecretContent(namespace string, secretName string, client kubernetes.Interface) (*TLSSecretData, error) { + secret, err := client.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) if err != nil { - return err + return nil, err } - for i := range caBundles.Items { - bundle := caBundles.Items[i] - if strings.HasPrefix(*bundle.Name, prefix) { - klog.Infof("Found a ca bundle %s with prefix %s. This will be deleted..", *bundle.Name, prefix) - deleteRequest := certificatesmanagement.DeleteCaBundleRequest{ - CaBundleId: bundle.Id, + caCertificateChain := string(secret.Data["ca.crt"]) + serverCertificate := string(secret.Data["tls.crt"]) + privateKey := string(secret.Data["tls.key"]) + return &TLSSecretData{CaCertificateChain: &caCertificateChain, ServerCertificate: &serverCertificate, PrivateKey: &privateKey}, nil +} + +func getCertificateNameFromSecret(secretName string) string { + if secretName == "" { + return "" + } + return fmt.Sprintf("ic-%s", secretName) +} + +func GetSSLConfigForBackendSet(namespace string, artifactType string, artifact string, lb *ociloadbalancer.LoadBalancer, bsName string, compartmentId string, certificatesClient *CertificatesClient, client kubernetes.Interface) (*ociloadbalancer.SslConfigurationDetails, error) { + var backendSetSslConfig *ociloadbalancer.SslConfigurationDetails + createCaBundle := false + var caBundleId *string + + bs, ok := lb.BackendSets[bsName] + + if artifactType == state.ArtifactTypeSecret && artifact != "" { + klog.Infof("Secret name for backend set %s is %s", bsName, artifact) + if ok && bs.SslConfiguration != nil && isTrustAuthorityCaBundle(bs.SslConfiguration.TrustedCertificateAuthorityIds[0]) { + newCertificateName := getCertificateNameFromSecret(artifact) + caBundle, err := GetCaBundle(bs.SslConfiguration.TrustedCertificateAuthorityIds[0], certificatesClient) + if err != nil { + return nil, err + } + + klog.Infof("Ca bundle name is %s, new certificate name is %s", *caBundle.Name, newCertificateName) + if *caBundle.Name != newCertificateName { + klog.Infof("Ca bundle for backend set %s needs update. Old name %s, New name %s", *bs.Name, *caBundle.Name, newCertificateName) + createCaBundle = true } - _, err := certificatesClient.DeleteCaBundle(context.TODO(), deleteRequest) + } else { + createCaBundle = true + } + + if createCaBundle { + cId, err := CreateOrGetCaBundleForBackendSet(namespace, artifact, compartmentId, certificatesClient, client) if err != nil { - klog.Error("Error deleting ca bundle %s, %s", *bundle.Name, err.Error()) - return err + return nil, err } + caBundleId = cId + } + + if caBundleId != nil { + caBundleIds := []string{*caBundleId} + backendSetSslConfig = &ociloadbalancer.SslConfigurationDetails{TrustedCertificateAuthorityIds: caBundleIds} } } - return nil + + if artifactType == state.ArtifactTypeCertificate && artifact != "" { + cert, err := GetCertificate(&artifact, certificatesClient) + if err != nil { + return nil, err + } + + klog.Infof("Found a certificate %s with type %s and id %s", *cert.Name, cert.ConfigType, *cert.Id) + if cert.ConfigType == certificatesmanagement.CertificateConfigTypeIssuedByInternalCa || + cert.ConfigType == certificatesmanagement.CertificateConfigTypeManagedExternallyIssuedByInternalCa { + caAuthorityIds := []string{*cert.IssuerCertificateAuthorityId} + backendSetSslConfig = &ociloadbalancer.SslConfigurationDetails{TrustedCertificateAuthorityIds: caAuthorityIds} + } + + if cert.ConfigType == certificatesmanagement.CertificateConfigTypeImported { + caBundleId, _ := FindCaBundleWithName(*cert.Name, compartmentId, certificatesClient) + if caBundleId == nil { + versionNumber := cert.CurrentVersion.VersionNumber + getCertificateBundleRequest := certificates.GetCertificateBundleRequest{ + CertificateId: &artifact, + VersionNumber: versionNumber, + } + + certificateBundle, err := certificatesClient.GetCertificateBundle(context.TODO(), getCertificateBundleRequest) + if err != nil { + return nil, err + } + + createCaBundle, err := CreateCaBundle(*cert.Name, compartmentId, certificatesClient, certificateBundle.GetCertChainPem()) + if err != nil { + return nil, err + } + caBundleId = createCaBundle.Id + } + + if caBundleId != nil { + caBundleIds := []string{*caBundleId} + backendSetSslConfig = &ociloadbalancer.SslConfigurationDetails{TrustedCertificateAuthorityIds: caBundleIds} + } + } + } + return backendSetSslConfig, nil +} + +func GetSSLConfigForListener(namespace string, listener *ociloadbalancer.Listener, artifactType string, artifact string, compartmentId string, certificatesClient *CertificatesClient, client kubernetes.Interface) (*ociloadbalancer.SslConfigurationDetails, error) { + var currentCertificateId string + var newCertificateId string + createCertificate := false + + var listenerSslConfig *ociloadbalancer.SslConfigurationDetails + + if listener != nil && listener.SslConfiguration != nil { + currentCertificateId = listener.SslConfiguration.CertificateIds[0] + if state.ArtifactTypeCertificate == artifactType && currentCertificateId != artifact { + newCertificateId = artifact + } else if state.ArtifactTypeSecret == artifactType { + cert, err := GetCertificate(¤tCertificateId, certificatesClient) + if err != nil { + return nil, err + } + certificateName := getCertificateNameFromSecret(artifact) + if certificateName != "" && *cert.Name != certificateName { + createCertificate = true + } + } + } else { + if state.ArtifactTypeSecret == artifactType { + createCertificate = true + } + if state.ArtifactTypeCertificate == artifactType { + newCertificateId = artifact + } + } + + if createCertificate { + cId, err := CreateOrGetCertificateForListener(namespace, artifact, compartmentId, certificatesClient, client) + if err != nil { + return nil, err + } + newCertificateId = *cId + } + + if newCertificateId != "" { + certificateIds := []string{newCertificateId} + listenerSslConfig = &ociloadbalancer.SslConfigurationDetails{CertificateIds: certificateIds} + } + return listenerSslConfig, nil +} + +func isTrustAuthorityCaBundle(id string) bool { + return strings.Contains(id, "cabundle") } diff --git a/pkg/certificate/util_test.go b/pkg/certificate/util_test.go new file mode 100644 index 00000000..e8f63183 --- /dev/null +++ b/pkg/certificate/util_test.go @@ -0,0 +1,477 @@ +package certificate + +import ( + "context" + "errors" + "net/http" + "testing" + "time" + + . "github.com/onsi/gomega" + "github.com/oracle/oci-go-sdk/v65/certificates" + "github.com/oracle/oci-go-sdk/v65/certificatesmanagement" + "github.com/oracle/oci-go-sdk/v65/common" + ociloadbalancer "github.com/oracle/oci-go-sdk/v65/loadbalancer" + . "github.com/oracle/oci-native-ingress-controller/pkg/oci/client" + "github.com/oracle/oci-native-ingress-controller/pkg/state" + "github.com/oracle/oci-native-ingress-controller/pkg/util" + fakeclientset "k8s.io/client-go/kubernetes/fake" +) + +const ( + errorMsg = "no cert found" + namespace = "test" + errorImportCert = "errorImportCert" +) + +func inits() (*CertificatesClient, *fakeclientset.Clientset, ociloadbalancer.LoadBalancer) { + client := fakeclientset.NewSimpleClientset() + secret := util.GetSampleCertSecret() + action := "get" + resource := "secrets" + obj := secret + util.FakeClientGetCall(client, action, resource, obj) + + certClient := GetCertClient() + certManageClient := GetCertManageClient() + certificatesClient := &CertificatesClient{ + ManagementClient: certManageClient, + CertificatesClient: certClient, + CertCache: map[string]*CertCacheObj{}, + CaBundleCache: map[string]*CaBundleCacheObj{}, + } + var trustCa []string + trustCa = append(trustCa, "cabundle") + + sslConfig := ociloadbalancer.SslConfiguration{ + VerifyDepth: nil, + VerifyPeerCertificate: nil, + TrustedCertificateAuthorityIds: trustCa, + CertificateIds: nil, + CertificateName: nil, + ServerOrderPreference: "", + CipherSuiteName: nil, + Protocols: nil, + } + bsName := "testecho1" + bs := ociloadbalancer.BackendSet{ + Name: &bsName, + Policy: nil, + Backends: nil, + HealthChecker: nil, + SslConfiguration: &sslConfig, + SessionPersistenceConfiguration: nil, + LbCookieSessionPersistenceConfiguration: nil, + } + var backendsets = map[string]ociloadbalancer.BackendSet{ + bsName: bs, + } + + lb := ociloadbalancer.LoadBalancer{ + Id: nil, + CompartmentId: nil, + DisplayName: nil, + LifecycleState: "", + TimeCreated: nil, + ShapeName: nil, + IpAddresses: nil, + ShapeDetails: nil, + IsPrivate: nil, + SubnetIds: nil, + NetworkSecurityGroupIds: nil, + Listeners: nil, + Hostnames: nil, + SslCipherSuites: nil, + Certificates: nil, + BackendSets: backendsets, + PathRouteSets: nil, + FreeformTags: nil, + DefinedTags: nil, + SystemTags: nil, + RuleSets: nil, + RoutingPolicies: nil, + } + + return certificatesClient, client, lb +} + +func TestGetSSLConfigForBackendSet(t *testing.T) { + RegisterTestingT(t) + certificatesClient, client, lb := inits() + + config, err := GetSSLConfigForBackendSet(namespace, state.ArtifactTypeSecret, "oci-config", &lb, "testecho1", "", certificatesClient, client) + Expect(err).Should(BeNil()) + Expect(config != nil).Should(BeTrue()) + + config, err = GetSSLConfigForBackendSet(namespace, state.ArtifactTypeCertificate, string(certificatesmanagement.CertificateConfigTypeIssuedByInternalCa), &lb, "testecho1", "", certificatesClient, client) + Expect(err).Should(BeNil()) + Expect(config != nil).Should(BeTrue()) + + config, err = GetSSLConfigForBackendSet(namespace, state.ArtifactTypeCertificate, string(certificatesmanagement.CertificateConfigTypeManagedExternallyIssuedByInternalCa), &lb, "testecho1", "", certificatesClient, client) + Expect(err).Should(BeNil()) + Expect(config != nil).Should(BeTrue()) + + config, err = GetSSLConfigForBackendSet(namespace, state.ArtifactTypeCertificate, string(certificatesmanagement.CertificateConfigTypeImported), &lb, "testecho1", "", certificatesClient, client) + Expect(err).Should(BeNil()) + Expect(config != nil).Should(BeTrue()) + + // No ca bundle scenario + config, err = GetSSLConfigForBackendSet(namespace, state.ArtifactTypeCertificate, errorImportCert, &lb, "testecho1", "", certificatesClient, client) + Expect(err).Should(BeNil()) + + _, err = GetSSLConfigForBackendSet(namespace, state.ArtifactTypeCertificate, "error", &lb, "testecho1", "", certificatesClient, client) + Expect(err).Should(Not(BeNil())) + Expect(err.Error()).Should(Equal(errorMsg)) + +} + +func TestGetSSLConfigForListener(t *testing.T) { + RegisterTestingT(t) + certificatesClient, client, _ := inits() + + //no listener for cert + sslConfig, err := GetSSLConfigForListener(namespace, nil, state.ArtifactTypeCertificate, "certificate", "", certificatesClient, client) + Expect(err).Should(BeNil()) + Expect(sslConfig != nil).Should(BeTrue()) + Expect(len(sslConfig.CertificateIds)).Should(Equal(1)) + Expect(sslConfig.CertificateIds[0]).Should(Equal("certificate")) + + //no listener for secret + sslConfig, err = GetSSLConfigForListener(namespace, nil, state.ArtifactTypeSecret, "secret", "", certificatesClient, client) + Expect(err).Should(BeNil()) + Expect(sslConfig != nil).Should(BeTrue()) + Expect(len(sslConfig.CertificateIds)).Should(Equal(1)) + Expect(sslConfig.CertificateIds[0]).Should(Equal("id")) + + // Listener + certificate + var certIds []string + certIds = append(certIds, "secret-cert", "cabundle") + customSslConfig := ociloadbalancer.SslConfiguration{ + CertificateIds: certIds, + } + listener := ociloadbalancer.Listener{ + SslConfiguration: &customSslConfig, + } + sslConfig, err = GetSSLConfigForListener(namespace, &listener, state.ArtifactTypeCertificate, "certificate", "", certificatesClient, client) + Expect(err).Should(BeNil()) + Expect(sslConfig != nil).Should(BeTrue()) + Expect(len(sslConfig.CertificateIds)).Should(Equal(1)) + Expect(sslConfig.CertificateIds[0]).Should(Equal("certificate")) + + // Listener + secret + sslConfig, err = GetSSLConfigForListener(namespace, &listener, state.ArtifactTypeSecret, "secret-cert", "", certificatesClient, client) + Expect(err).Should(BeNil()) + Expect(sslConfig != nil).Should(BeTrue()) + Expect(len(sslConfig.CertificateIds)).Should(Equal(1)) + Expect(sslConfig.CertificateIds[0]).Should(Equal("id")) + +} + +func TestGetCertificate(t *testing.T) { + RegisterTestingT(t) + certificatesClient, _, _ := inits() + + certId := "id" + certId2 := "id2" + + certificate, err := GetCertificate(&certId, certificatesClient) + Expect(certificate != nil).Should(BeTrue()) + Expect(err).Should(BeNil()) + + // cache fetch + certificate, err = GetCertificate(&certId, certificatesClient) + Expect(certificate != nil).Should(BeTrue()) + Expect(err).Should(BeNil()) + + certificate, err = GetCertificate(&certId2, certificatesClient) + Expect(certificate != nil).Should(BeTrue()) + Expect(err).Should(BeNil()) +} + +func GetCertManageClient() CertificateManagementInterface { + return &MockCertificateManagerClient{} +} + +type MockCertificateManagerClient struct { +} + +func (m MockCertificateManagerClient) CreateCertificate(ctx context.Context, request certificatesmanagement.CreateCertificateRequest) (certificatesmanagement.CreateCertificateResponse, error) { + id := "id" + return certificatesmanagement.CreateCertificateResponse{ + RawResponse: nil, + Certificate: certificatesmanagement.Certificate{ + Id: &id, + }, + Etag: nil, + OpcRequestId: &id, + }, nil +} + +func (m MockCertificateManagerClient) GetCertificate(ctx context.Context, request certificatesmanagement.GetCertificateRequest) (certificatesmanagement.GetCertificateResponse, error) { + + if *request.CertificateId == "error" { + return certificatesmanagement.GetCertificateResponse{}, errors.New(errorMsg) + } + id := "id" + name := "cert" + authorityId := "authId" + var confType certificatesmanagement.CertificateConfigTypeEnum + if *request.CertificateId == errorImportCert { + name = "error" + confType = certificatesmanagement.CertificateConfigTypeImported + } else { + confType, _ = certificatesmanagement.GetMappingCertificateConfigTypeEnum(*request.CertificateId) + } + var number int64 + number = 234 + certVersionSummary := certificatesmanagement.CertificateVersionSummary{ + VersionNumber: &number, + } + return certificatesmanagement.GetCertificateResponse{ + RawResponse: nil, + Certificate: certificatesmanagement.Certificate{ + Id: &id, + Name: &name, + ConfigType: confType, + IssuerCertificateAuthorityId: &authorityId, + CurrentVersion: &certVersionSummary, + }, + Etag: nil, + OpcRequestId: nil, + }, nil +} + +func (m MockCertificateManagerClient) ListCertificates(ctx context.Context, request certificatesmanagement.ListCertificatesRequest) (certificatesmanagement.ListCertificatesResponse, error) { + id := "id" + return certificatesmanagement.ListCertificatesResponse{ + RawResponse: nil, + CertificateCollection: certificatesmanagement.CertificateCollection{}, + OpcRequestId: &id, + OpcNextPage: &id, + }, nil +} + +func (m MockCertificateManagerClient) ScheduleCertificateDeletion(ctx context.Context, request certificatesmanagement.ScheduleCertificateDeletionRequest) (certificatesmanagement.ScheduleCertificateDeletionResponse, error) { + var err error + if *request.CertificateId == "error" { + err = errors.New("cert error deletion") + } + return certificatesmanagement.ScheduleCertificateDeletionResponse{}, err +} + +func (m MockCertificateManagerClient) CreateCaBundle(ctx context.Context, request certificatesmanagement.CreateCaBundleRequest) (certificatesmanagement.CreateCaBundleResponse, error) { + id := "id" + return certificatesmanagement.CreateCaBundleResponse{ + RawResponse: nil, + CaBundle: certificatesmanagement.CaBundle{ + Id: &id, + }, + Etag: nil, + OpcRequestId: nil, + }, nil +} + +func (m MockCertificateManagerClient) GetCaBundle(ctx context.Context, request certificatesmanagement.GetCaBundleRequest) (certificatesmanagement.GetCaBundleResponse, error) { + id := "id" + name := "cabundle" + return certificatesmanagement.GetCaBundleResponse{ + RawResponse: nil, + CaBundle: certificatesmanagement.CaBundle{ + Id: &id, + Name: &name, + }, + OpcRequestId: &id, + }, nil +} + +func (m MockCertificateManagerClient) ListCaBundles(ctx context.Context, request certificatesmanagement.ListCaBundlesRequest) (certificatesmanagement.ListCaBundlesResponse, error) { + if *request.Name == "error" { + return certificatesmanagement.ListCaBundlesResponse{}, nil + } + + var items []certificatesmanagement.CaBundleSummary + name := "ic-oci-config" + id := "id" + item := certificatesmanagement.CaBundleSummary{ + Id: &id, + Name: &name, + } + items = append(items, item) + + return certificatesmanagement.ListCaBundlesResponse{ + RawResponse: nil, + CaBundleCollection: certificatesmanagement.CaBundleCollection{ + Items: items, + }, + OpcRequestId: nil, + OpcNextPage: nil, + }, nil +} + +func (m MockCertificateManagerClient) DeleteCaBundle(ctx context.Context, request certificatesmanagement.DeleteCaBundleRequest) (certificatesmanagement.DeleteCaBundleResponse, error) { + res := http.Response{ + Status: "200", + } + var err error + if *request.CaBundleId == "error" { + err = errors.New("error deleting cabundle") + } + return certificatesmanagement.DeleteCaBundleResponse{ + RawResponse: &res, + OpcRequestId: nil, + }, err +} + +func GetCertClient() CertificateInterface { + return &MockCertificateClient{} +} + +type MockCertificateClient struct { +} + +func (m MockCertificateClient) GetCertificateBundle(ctx context.Context, request certificates.GetCertificateBundleRequest) (certificates.GetCertificateBundleResponse, error) { + + var bundle certificates.CertificateBundle + bundle = getMockBundle() + + return certificates.GetCertificateBundleResponse{ + RawResponse: nil, + CertificateBundle: bundle, + Etag: nil, + OpcRequestId: nil, + }, nil +} + +func getMockBundle() certificates.CertificateBundle { + return &MockCertificateBundle{} +} + +type MockCertificateBundle struct { +} + +func (m MockCertificateBundle) GetCertificateId() *string { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateBundle) GetCertificateName() *string { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateBundle) GetVersionNumber() *int64 { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateBundle) GetSerialNumber() *string { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateBundle) GetTimeCreated() *common.SDKTime { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateBundle) GetValidity() *certificates.Validity { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateBundle) GetStages() []certificates.VersionStageEnum { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateBundle) GetCertificatePem() *string { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateBundle) GetCertChainPem() *string { + data := "chain" + return &data +} + +func (m MockCertificateBundle) GetVersionName() *string { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateBundle) GetRevocationStatus() *certificates.RevocationStatus { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateClient) SetCertCache(cert *certificatesmanagement.Certificate) { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateClient) GetFromCertCache(certId string) *CertCacheObj { + cert := certificatesmanagement.Certificate{} + var now time.Time + if certId == "id" { + now = time.Now() + } else { + now = time.Now() + now.Add(time.Minute * 15) + } + return &CertCacheObj{ + Cert: &cert, + Age: now, + } +} + +func (m MockCertificateClient) SetCaBundleCache(caBundle *certificatesmanagement.CaBundle) { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateClient) GetFromCaBundleCache(id string) *CaBundleCacheObj { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateClient) CreateCertificate(ctx context.Context, req certificatesmanagement.CreateCertificateRequest) (*certificatesmanagement.Certificate, error) { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateClient) CreateCaBundle(ctx context.Context, req certificatesmanagement.CreateCaBundleRequest) (*certificatesmanagement.CaBundle, error) { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateClient) GetCertificate(ctx context.Context, req certificatesmanagement.GetCertificateRequest) (*certificatesmanagement.Certificate, error) { + id := "id" + return &certificatesmanagement.Certificate{ + Id: &id, + }, nil +} + +func (m MockCertificateClient) ListCertificates(ctx context.Context, req certificatesmanagement.ListCertificatesRequest) (*certificatesmanagement.CertificateCollection, *string, error) { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateClient) ScheduleCertificateDeletion(ctx context.Context, req certificatesmanagement.ScheduleCertificateDeletionRequest) error { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateClient) GetCaBundle(ctx context.Context, req certificatesmanagement.GetCaBundleRequest) (*certificatesmanagement.CaBundle, error) { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateClient) ListCaBundles(ctx context.Context, req certificatesmanagement.ListCaBundlesRequest) (*certificatesmanagement.CaBundleCollection, error) { + //TODO implement me + panic("implement me") +} + +func (m MockCertificateClient) DeleteCaBundle(ctx context.Context, req certificatesmanagement.DeleteCaBundleRequest) (*http.Response, error) { + //TODO implement me + panic("implement me") +} diff --git a/pkg/controllers/backend/backendPath.yaml b/pkg/controllers/backend/backendPath.yaml new file mode 100644 index 00000000..f4e3710a --- /dev/null +++ b/pkg/controllers/backend/backendPath.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-readiness + namespace: default +spec: + rules: + - host: "foo.bar.com" + http: + paths: + - pathType: Exact + path: "/testecho1" + backend: + service: + name: testecho1 + port: + number: 80 \ No newline at end of file diff --git a/pkg/controllers/backend/backendPathWithDefaultBackend.yaml b/pkg/controllers/backend/backendPathWithDefaultBackend.yaml new file mode 100644 index 00000000..37ff07c6 --- /dev/null +++ b/pkg/controllers/backend/backendPathWithDefaultBackend.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-readiness + namespace: default +spec: + defaultBackend: + service: + name: host-es + port: + number: 8080 + rules: + - host: "foo.bar.com" + http: + paths: + - pathType: Exact + path: "/testecho1" + backend: + service: + name: testecho1 + port: + number: 80 \ No newline at end of file diff --git a/pkg/controllers/backend/backend_test.go b/pkg/controllers/backend/backend_test.go index 1a73ddab..922a98b7 100644 --- a/pkg/controllers/backend/backend_test.go +++ b/pkg/controllers/backend/backend_test.go @@ -3,52 +3,42 @@ package backend import ( "bytes" "context" + "sync" "testing" + "time" . "github.com/onsi/gomega" + "github.com/oracle/oci-go-sdk/v65/common" + ociloadbalancer "github.com/oracle/oci-go-sdk/v65/loadbalancer" + lb "github.com/oracle/oci-native-ingress-controller/pkg/loadbalancer" + "github.com/oracle/oci-native-ingress-controller/pkg/oci/client" + "github.com/oracle/oci-native-ingress-controller/pkg/util" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/informers" networkinginformers "k8s.io/client-go/informers/networking/v1" fakeclientset "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/kubernetes/typed/core/v1/fake" - fake2 "k8s.io/client-go/kubernetes/typed/networking/v1/fake" corelisters "k8s.io/client-go/listers/core/v1" - k8stesting "k8s.io/client-go/testing" "k8s.io/client-go/tools/cache" ) -func setUp(ctx context.Context, ingressClassList *networkingv1.IngressClassList, ingressList *networkingv1.IngressList, testService *corev1.Service, - endpoints *corev1.Endpoints, pod *corev1.Pod) (networkinginformers.IngressClassInformer, networkinginformers.IngressInformer, - corelisters.ServiceLister, corelisters.EndpointsLister, corelisters.PodLister, *fakeclientset.Clientset) { +const ( + backendPath = "backendPath.yaml" + backendPathWithDefaultBackend = "backendPathWithDefaultBackend.yaml" + namespace = "default" +) + +func setUp(ctx context.Context, ingressClassList *networkingv1.IngressClassList, ingressList *networkingv1.IngressList, testService *corev1.ServiceList, endpoints *corev1.EndpointsList, pod *corev1.PodList) (networkinginformers.IngressClassInformer, networkinginformers.IngressInformer, corelisters.ServiceLister, corelisters.EndpointsLister, corelisters.PodLister, *fakeclientset.Clientset) { client := fakeclientset.NewSimpleClientset() - client.NetworkingV1().(*fake2.FakeNetworkingV1). - PrependReactor("list", "ingressclasses", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { - return true, ingressClassList, nil - }) - - client.NetworkingV1().(*fake2.FakeNetworkingV1). - PrependReactor("list", "ingresses", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { - return true, ingressList, nil - }) - - client.CoreV1().Services("").(*fake.FakeServices).Fake. - PrependReactor("get", "services", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { - return true, testService, nil - }) - - client.CoreV1().(*fake.FakeCoreV1). - PrependReactor("get", "endpoints", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { - return true, endpoints, nil - }) - - client.CoreV1().(*fake.FakeCoreV1). - PrependReactor("get", "pods", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { - return true, pod, nil - }) + + action := "list" + util.UpdateFakeClientCall(client, action, "ingressclasses", ingressClassList) + util.UpdateFakeClientCall(client, action, "ingresses", ingressList) + util.UpdateFakeClientCall(client, action, "services", testService) + util.UpdateFakeClientCall(client, action, "endpoints", endpoints) + util.UpdateFakeClientCall(client, action, "pods", pod) informerFactory := informers.NewSharedInformerFactory(client, 0) ingressClassInformer := informerFactory.Networking().V1().IngressClasses() @@ -75,6 +65,154 @@ func setUp(ctx context.Context, ingressClassList *networkingv1.IngressClassList, return ingressClassInformer, ingressInformer, serviceLister, endpointLister, podLister, client } +func TestEnsureBackend(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList, backendPath) + + err := c.ensureBackends(&ingressClassList.Items[0], "id") + Expect(err == nil).Should(Equal(true)) +} + +func TestRunPusher(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList, backendPath) + + c.runPusher() + Expect(c.queue.Len()).Should(Equal(1)) +} + +func TestProcessNextItem(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList, backendPath) + + c.queue.Add("default-ingress-class") + res := c.processNextItem() + Expect(res).Should(BeTrue()) + time.Sleep(11 * time.Second) // since we get "ingress class not ready" error, and re-enqueue. + Expect(c.queue.Len()).Should(Equal(1)) +} + +func TestProcessNextItemWithNginx(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassListWithNginx() + c := inits(ctx, ingressClassList, backendPath) + + c.queue.Add("nginx-ingress-class") + res := c.processNextItem() + Expect(res).Should(BeTrue()) + Expect(c.queue.Len()).Should(Equal(0)) +} + +func TestNoDefaultBackends(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList, backendPath) + ingresses, _ := c.getIngressesForClass(&ingressClassList.Items[0]) + backends, err := c.getDefaultBackends(ingresses) + Expect(err == nil).Should(Equal(true)) + Expect(len(backends)).Should(Equal(0)) +} +func TestDefaultBackends(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList, backendPathWithDefaultBackend) + ingresses, _ := c.getIngressesForClass(&ingressClassList.Items[0]) + backends, err := c.getDefaultBackends(ingresses) + Expect(err == nil).Should(Equal(true)) + Expect(len(backends)).Should(Equal(1)) +} + +func TestEnsurePodReadinessConditionWithExistingReadiness(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList, backendPathWithDefaultBackend) + ingresses, _ := c.getIngressesForClass(&ingressClassList.Items[0]) + ingress := ingresses[0] + var readinessCondition corev1.PodConditionType + for _, rule := range ingress.Spec.Rules { + for _, path := range rule.HTTP.Paths { + readinessCondition = util.GetPodReadinessCondition(ingress.Name, rule.Host, path) + break + } + break + } + + backendHealth := ociloadbalancer.BackendSetHealth{ + Status: ociloadbalancer.BackendSetHealthStatusOk, + WarningStateBackendNames: nil, + CriticalStateBackendNames: nil, + UnknownStateBackendNames: nil, + TotalBackendCount: nil, + } + var condition []corev1.PodCondition + condition = append(condition, corev1.PodCondition{ + Type: readinessCondition, + Status: corev1.ConditionTrue, + Reason: "backend is healthy", + }) + + err := c.ensurePodReadinessCondition(util.GetPodResourceWithReadiness("testecho1", "echoserver", "ingress-readiness", "foo.bar.com", condition), readinessCondition, &backendHealth, "testecho1") + + Expect(err == nil).Should(Equal(true)) +} + +func inits(ctx context.Context, ingressClassList *networkingv1.IngressClassList, yamlPath string) *Controller { + + ingressList := util.ReadResourceAsIngressList(yamlPath) + testService := util.GetServiceListResource(namespace, "testecho1", 80) + endpoints := util.GetEndpointsResourceList("testecho1", namespace) + pod := util.GetPodResourceList("testpod", "echoserver") + lbClient := getLoadBalancerClient() + + loadBalancerClient := &lb.LoadBalancerClient{ + LbClient: lbClient, + Mu: sync.Mutex{}, + Cache: map[string]*lb.LbCacheObj{}, + } + + ingressClassInformer, ingressInformer, serviceLister, endpointLister, podLister, client := setUp(ctx, ingressClassList, ingressList, testService, endpoints, pod) + c := NewController("oci.oraclecloud.com/native-ingress-controller", ingressClassInformer, ingressInformer, serviceLister, endpointLister, podLister, client, loadBalancerClient) + return c +} + +func TestGetIngressesForClass(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList, backendPath) + ic, err := c.getIngressesForClass(&ingressClassList.Items[0]) + Expect(err == nil).Should(Equal(true)) + Expect(len(ic)).Should(Equal(1)) + count := 0 + for _, ingress := range ic { + for _, rule := range ingress.Spec.Rules { + for range rule.HTTP.Paths { + count++ + } + } + } + Expect(count).Should(Equal(1)) + +} + func TestBuildPodConditionPatch(t *testing.T) { RegisterTestingT(t) pod := &corev1.Pod{ @@ -99,3 +237,99 @@ func TestBuildPodConditionPatch(t *testing.T) { Expect(err == nil).Should(Equal(true)) Expect(bytes.Equal(patch, []byte("{\"status\":{\"conditions\":[{\"lastProbeTime\":null,\"lastTransitionTime\":null,\"status\":\"True\",\"type\":\"ContainersReady\"}]}}"))).Should(Equal(true)) } + +func getLoadBalancerClient() client.LoadBalancerInterface { + return &MockLoadBalancerClient{} +} + +type MockLoadBalancerClient struct { +} + +func (m MockLoadBalancerClient) GetLoadBalancer(ctx context.Context, request ociloadbalancer.GetLoadBalancerRequest) (ociloadbalancer.GetLoadBalancerResponse, error) { + res := util.SampleLoadBalancerResponse() + return res, nil +} + +func (m MockLoadBalancerClient) CreateLoadBalancer(ctx context.Context, request ociloadbalancer.CreateLoadBalancerRequest) (ociloadbalancer.CreateLoadBalancerResponse, error) { + return ociloadbalancer.CreateLoadBalancerResponse{}, nil +} + +func (m MockLoadBalancerClient) DeleteLoadBalancer(ctx context.Context, request ociloadbalancer.DeleteLoadBalancerRequest) (ociloadbalancer.DeleteLoadBalancerResponse, error) { + return ociloadbalancer.DeleteLoadBalancerResponse{ + OpcRequestId: common.String("OpcRequestId"), + OpcWorkRequestId: common.String("OpcWorkRequestId"), + }, nil +} + +func (m MockLoadBalancerClient) GetWorkRequest(ctx context.Context, request ociloadbalancer.GetWorkRequestRequest) (ociloadbalancer.GetWorkRequestResponse, error) { + id := "id" + requestId := "opcrequestid" + return ociloadbalancer.GetWorkRequestResponse{ + RawResponse: nil, + WorkRequest: ociloadbalancer.WorkRequest{ + Id: &id, + LoadBalancerId: &id, + Type: nil, + LifecycleState: ociloadbalancer.WorkRequestLifecycleStateSucceeded, + }, + OpcRequestId: &requestId, + }, nil +} + +func (m MockLoadBalancerClient) CreateBackendSet(ctx context.Context, request ociloadbalancer.CreateBackendSetRequest) (ociloadbalancer.CreateBackendSetResponse, error) { + return ociloadbalancer.CreateBackendSetResponse{}, nil +} + +func (m MockLoadBalancerClient) UpdateBackendSet(ctx context.Context, request ociloadbalancer.UpdateBackendSetRequest) (ociloadbalancer.UpdateBackendSetResponse, error) { + reqId := "opcrequestid" + res := ociloadbalancer.UpdateBackendSetResponse{ + RawResponse: nil, + OpcWorkRequestId: &reqId, + OpcRequestId: &reqId, + } + return res, nil +} + +func (m MockLoadBalancerClient) DeleteBackendSet(ctx context.Context, request ociloadbalancer.DeleteBackendSetRequest) (ociloadbalancer.DeleteBackendSetResponse, error) { + return ociloadbalancer.DeleteBackendSetResponse{}, nil +} + +func (m MockLoadBalancerClient) GetBackendSetHealth(ctx context.Context, request ociloadbalancer.GetBackendSetHealthRequest) (ociloadbalancer.GetBackendSetHealthResponse, error) { + backendCount := 1 + return ociloadbalancer.GetBackendSetHealthResponse{ + RawResponse: nil, + BackendSetHealth: ociloadbalancer.BackendSetHealth{ + Status: ociloadbalancer.BackendSetHealthStatusOk, + WarningStateBackendNames: nil, + CriticalStateBackendNames: nil, + UnknownStateBackendNames: nil, + TotalBackendCount: &backendCount, + }, + OpcRequestId: nil, + ETag: nil, + }, nil +} + +func (m MockLoadBalancerClient) CreateRoutingPolicy(ctx context.Context, request ociloadbalancer.CreateRoutingPolicyRequest) (ociloadbalancer.CreateRoutingPolicyResponse, error) { + return ociloadbalancer.CreateRoutingPolicyResponse{}, nil +} + +func (m MockLoadBalancerClient) UpdateRoutingPolicy(ctx context.Context, request ociloadbalancer.UpdateRoutingPolicyRequest) (ociloadbalancer.UpdateRoutingPolicyResponse, error) { + return ociloadbalancer.UpdateRoutingPolicyResponse{}, nil +} + +func (m MockLoadBalancerClient) DeleteRoutingPolicy(ctx context.Context, request ociloadbalancer.DeleteRoutingPolicyRequest) (ociloadbalancer.DeleteRoutingPolicyResponse, error) { + return ociloadbalancer.DeleteRoutingPolicyResponse{}, nil +} + +func (m MockLoadBalancerClient) CreateListener(ctx context.Context, request ociloadbalancer.CreateListenerRequest) (ociloadbalancer.CreateListenerResponse, error) { + return ociloadbalancer.CreateListenerResponse{}, nil +} + +func (m MockLoadBalancerClient) UpdateListener(ctx context.Context, request ociloadbalancer.UpdateListenerRequest) (ociloadbalancer.UpdateListenerResponse, error) { + return ociloadbalancer.UpdateListenerResponse{}, nil +} + +func (m MockLoadBalancerClient) DeleteListener(ctx context.Context, request ociloadbalancer.DeleteListenerRequest) (ociloadbalancer.DeleteListenerResponse, error) { + return ociloadbalancer.DeleteListenerResponse{}, nil +} diff --git a/pkg/controllers/ingress/ingress.go b/pkg/controllers/ingress/ingress.go index bbf01336..4d44240d 100644 --- a/pkg/controllers/ingress/ingress.go +++ b/pkg/controllers/ingress/ingress.go @@ -13,12 +13,11 @@ import ( "context" "encoding/json" "fmt" - "github.com/prometheus/client_golang/prometheus" "reflect" "time" - "github.com/oracle/oci-go-sdk/v65/certificates" - "github.com/oracle/oci-go-sdk/v65/certificatesmanagement" + "github.com/prometheus/client_golang/prometheus" + "github.com/pkg/errors" "k8s.io/klog/v2" @@ -68,6 +67,7 @@ type Controller struct { // NewController creates a new Controller. func NewController(controllerClass string, defaultCompartmentId string, ingressClassInformer networkinginformers.IngressClassInformer, ingressInformer networkinginformers.IngressInformer, + serviceLister corelisters.ServiceLister, client kubernetes.Interface, lbClient *loadbalancer.LoadBalancerClient, certificatesClient *certificate.CertificatesClient, reg *prometheus.Registry) *Controller { @@ -77,12 +77,12 @@ func NewController(controllerClass string, defaultCompartmentId string, ingressClassLister: ingressClassInformer.Lister(), ingressLister: ingressInformer.Lister(), informer: ingressInformer, - - client: client, - lbClient: lbClient, - certificatesClient: certificatesClient, - queue: workqueue.NewRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(10*time.Second, 5*time.Minute)), - metricsCollector: metric.NewIngressCollector(controllerClass, reg), + serviceLister: serviceLister, + client: client, + lbClient: lbClient, + certificatesClient: certificatesClient, + queue: workqueue.NewRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(10*time.Second, 5*time.Minute)), + metricsCollector: metric.NewIngressCollector(controllerClass, reg), } ingressInformer.Informer().AddEventHandler( @@ -106,14 +106,18 @@ func (c *Controller) enqueueIngress(ingress *networkingv1.Ingress) { } func (c *Controller) ingressAdd(obj interface{}) { - c.metricsCollector.IncrementIngressAddOperation() + if c.metricsCollector != nil { + c.metricsCollector.IncrementIngressAddOperation() + } ic := obj.(*networkingv1.Ingress) klog.V(4).InfoS("Adding ingress", "ingress", klog.KObj(ic)) c.enqueueIngress(ic) } func (c *Controller) ingressUpdate(old, new interface{}) { - c.metricsCollector.IncrementIngressUpdateOperation() + if c.metricsCollector != nil { + c.metricsCollector.IncrementIngressUpdateOperation() + } oldIngress := old.(*networkingv1.Ingress) newIngress := new.(*networkingv1.Ingress) @@ -122,7 +126,9 @@ func (c *Controller) ingressUpdate(old, new interface{}) { } func (c *Controller) ingressDelete(obj interface{}) { - c.metricsCollector.IncrementIngressDeleteOperation() + if c.metricsCollector != nil { + c.metricsCollector.IncrementIngressDeleteOperation() + } ic, ok := obj.(*networkingv1.Ingress) if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) @@ -159,7 +165,9 @@ func (c *Controller) processNextItem() bool { err := c.sync(key.(string)) endBuildTime := util.GetCurrentTimeInUnixMillis() - c.metricsCollector.AddIngressSyncTime(util.GetTimeDifferenceInSeconds(startBuildTime, endBuildTime)) + if c.metricsCollector != nil { + c.metricsCollector.AddIngressSyncTime(util.GetTimeDifferenceInSeconds(startBuildTime, endBuildTime)) + } // Handle the error if something went wrong during the execution of the business logic c.handleErr(err, key) @@ -168,7 +176,9 @@ func (c *Controller) processNextItem() bool { // sync is the business logic of the controller. func (c *Controller) sync(key string) error { - c.metricsCollector.IncrementSyncCount() + if c.metricsCollector != nil { + c.metricsCollector.IncrementSyncCount() + } namespace, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { klog.ErrorS(err, "Failed to split meta namespace cache key", "cacheKey", key) @@ -327,7 +337,7 @@ func (c *Controller) ensureIngress(ingress *networkingv1.Ingress, ingressClass * startBuildTime := util.GetCurrentTimeInUnixMillis() klog.V(2).InfoS("creating backend set for ingress", "ingress", klog.KObj(ingress), "backendSetName", bsName) artifact, artifactType := stateStore.GetTLSConfigForBackendSet(bsName) - backendSetSslConfig, err := getSSLConfigForBackendSet(ingress.Namespace, artifactType, artifact, lb, bsName, c) + backendSetSslConfig, err := certificate.GetSSLConfigForBackendSet(ingress.Namespace, artifactType, artifact, lb, bsName, c.defaultCompartmentId, c.certificatesClient, c.client) if err != nil { return err } @@ -339,7 +349,9 @@ func (c *Controller) ensureIngress(ingress *networkingv1.Ingress, ingressClass * return err } endBuildTime := util.GetCurrentTimeInUnixMillis() - c.metricsCollector.AddBackendCreationTime(util.GetTimeDifferenceInSeconds(startBuildTime, endBuildTime)) + if c.metricsCollector != nil { + c.metricsCollector.AddBackendCreationTime(util.GetTimeDifferenceInSeconds(startBuildTime, endBuildTime)) + } } // Determine listeners... This is based off path ports. @@ -360,7 +372,7 @@ func (c *Controller) ensureIngress(ingress *networkingv1.Ingress, ingressClass * var listenerSslConfig *ociloadbalancer.SslConfigurationDetails artifact, artifactType := stateStore.GetTLSConfigForListener(port) - listenerSslConfig, err := getSSLConfigForListener(ingress.Namespace, nil, artifactType, artifact, c) + listenerSslConfig, err := certificate.GetSSLConfigForListener(ingress.Namespace, nil, artifactType, artifact, c.defaultCompartmentId, c.certificatesClient, c.client) if err != nil { return err } @@ -460,133 +472,6 @@ func deleteListeners(actualListeners sets.Int32, desiredListeners sets.Int32, lb return nil } -func getSSLConfigForBackendSet(namespace string, artifactType string, artifact string, lb *ociloadbalancer.LoadBalancer, bsName string, c *Controller) (*ociloadbalancer.SslConfigurationDetails, error) { - var backendSetSslConfig *ociloadbalancer.SslConfigurationDetails - createCaBundle := false - var caBundleId *string - - bs, ok := lb.BackendSets[bsName] - - if artifactType == state.ArtifactTypeSecret && artifact != "" { - klog.Infof("Secret name for backend set %s is %s", bsName, artifact) - if ok && bs.SslConfiguration != nil && isTrustAuthorityCaBundle(bs.SslConfiguration.TrustedCertificateAuthorityIds[0]) { - newCertificateName := getCertificateNameFromSecret(artifact) - caBundle, err := certificate.GetCaBundle(bs.SslConfiguration.TrustedCertificateAuthorityIds[0], c.certificatesClient) - if err != nil { - return nil, err - } - - klog.Infof("Ca bundle name is %s, new certificate name is %s", *caBundle.Name, newCertificateName) - if *caBundle.Name != newCertificateName { - klog.Infof("Ca bundle for backend set %s needs update. Old name %s, New name %s", *bs.Name, *caBundle.Name, newCertificateName) - createCaBundle = true - } - } else { - createCaBundle = true - } - - if createCaBundle { - cId, err := createOrGetCaBundleForBackendSet(namespace, artifact, c) - if err != nil { - return nil, err - } - caBundleId = cId - } - - if caBundleId != nil { - caBundleIds := []string{*caBundleId} - backendSetSslConfig = &ociloadbalancer.SslConfigurationDetails{TrustedCertificateAuthorityIds: caBundleIds} - } - } - - if artifactType == state.ArtifactTypeCertificate && artifact != "" { - cert, err := certificate.GetCertificate(&artifact, c.certificatesClient) - if err != nil { - return nil, err - } - - klog.Infof("Found a certificate %s with type %s and id %s", *cert.Name, cert.ConfigType, *cert.Id) - if cert.ConfigType == certificatesmanagement.CertificateConfigTypeIssuedByInternalCa || - cert.ConfigType == certificatesmanagement.CertificateConfigTypeManagedExternallyIssuedByInternalCa { - caAuthorityIds := []string{*cert.IssuerCertificateAuthorityId} - backendSetSslConfig = &ociloadbalancer.SslConfigurationDetails{TrustedCertificateAuthorityIds: caAuthorityIds} - } - - if cert.ConfigType == certificatesmanagement.CertificateConfigTypeImported { - caBundleId, _ := certificate.FindCaBundleWithName(*cert.Name, c.defaultCompartmentId, c.certificatesClient) - if caBundleId == nil { - versionNumber := cert.CurrentVersion.VersionNumber - getCertificateBundleRequest := certificates.GetCertificateBundleRequest{ - CertificateId: &artifact, - VersionNumber: versionNumber, - } - - certificateBundle, err := c.certificatesClient.GetCertificateBundle(context.TODO(), getCertificateBundleRequest) - if err != nil { - return nil, err - } - - createCaBundle, err := certificate.CreateCaBundle(*cert.Name, c.defaultCompartmentId, c.certificatesClient, certificateBundle.GetCertChainPem()) - if err != nil { - return nil, err - } - caBundleId = createCaBundle.Id - } - - if caBundleId != nil { - caBundleIds := []string{*caBundleId} - backendSetSslConfig = &ociloadbalancer.SslConfigurationDetails{TrustedCertificateAuthorityIds: caBundleIds} - } - } - } - return backendSetSslConfig, nil -} - -func getSSLConfigForListener(namespace string, listener *ociloadbalancer.Listener, artifactType string, artifact string, c *Controller) (*ociloadbalancer.SslConfigurationDetails, error) { - var currentCertificateId string - var newCertificateId string - createCertificate := false - - var listenerSslConfig *ociloadbalancer.SslConfigurationDetails - - if listener != nil && listener.SslConfiguration != nil { - currentCertificateId = listener.SslConfiguration.CertificateIds[0] - if state.ArtifactTypeCertificate == artifactType && currentCertificateId != artifact { - newCertificateId = artifact - } else if state.ArtifactTypeSecret == artifactType { - cert, err := certificate.GetCertificate(¤tCertificateId, c.certificatesClient) - if err != nil { - return nil, err - } - certificateName := getCertificateNameFromSecret(artifact) - if certificateName != "" && *cert.Name != certificateName { - createCertificate = true - } - } - } else { - if state.ArtifactTypeSecret == artifactType { - createCertificate = true - } - if state.ArtifactTypeCertificate == artifactType { - newCertificateId = artifact - } - } - - if createCertificate { - cId, err := createOrGetCertificateForListener(namespace, artifact, c) - if err != nil { - return nil, err - } - newCertificateId = *cId - } - - if newCertificateId != "" { - certificateIds := []string{newCertificateId} - listenerSslConfig = &ociloadbalancer.SslConfigurationDetails{CertificateIds: certificateIds} - } - return listenerSslConfig, nil -} - func syncListener(namespace string, stateStore *state.StateStore, lbId *string, listenerName string, c *Controller) error { startTime := util.GetCurrentTimeInUnixMillis() lb, etag, err := c.lbClient.GetLoadBalancer(context.TODO(), *lbId) @@ -603,7 +488,7 @@ func syncListener(namespace string, stateStore *state.StateStore, lbId *string, artifact, artifactType := stateStore.GetTLSConfigForListener(int32(*listener.Port)) var sslConfig *ociloadbalancer.SslConfigurationDetails if artifact != "" { - sslConfig, err = getSSLConfigForListener(namespace, &listener, artifactType, artifact, c) + sslConfig, err = certificate.GetSSLConfigForListener(namespace, &listener, artifactType, artifact, c.defaultCompartmentId, c.certificatesClient, c.client) if err != nil { return err } @@ -630,7 +515,9 @@ func syncListener(namespace string, stateStore *state.StateStore, lbId *string, } } endTime := util.GetCurrentTimeInUnixMillis() - c.metricsCollector.AddIngressListenerSyncTime(util.GetTimeDifferenceInSeconds(startTime, endTime)) + if c.metricsCollector != nil { + c.metricsCollector.AddIngressListenerSyncTime(util.GetTimeDifferenceInSeconds(startTime, endTime)) + } return nil } @@ -649,7 +536,7 @@ func syncBackendSet(ingress *networkingv1.Ingress, lbID string, backendSetName s needsUpdate := false artifact, artifactType := stateStore.GetTLSConfigForBackendSet(*bs.Name) - sslConfig, err := getSSLConfigForBackendSet(ingress.Namespace, artifactType, artifact, lb, *bs.Name, c) + sslConfig, err := certificate.GetSSLConfigForBackendSet(ingress.Namespace, artifactType, artifact, lb, *bs.Name, c.defaultCompartmentId, c.certificatesClient, c.client) if err != nil { return err } @@ -682,53 +569,10 @@ func syncBackendSet(ingress *networkingv1.Ingress, lbID string, backendSetName s } endTime := util.GetCurrentTimeInUnixMillis() - c.metricsCollector.AddIngressBackendSyncTime(util.GetTimeDifferenceInSeconds(startTime, endTime)) - return nil -} - -func createOrGetCertificateForListener(namespace string, secretName string, c *Controller) (*string, error) { - certificateName := getCertificateNameFromSecret(secretName) - certificateId, err := certificate.FindCertificateWithName(certificateName, c.defaultCompartmentId, c.certificatesClient) - if err != nil { - return nil, err + if c.metricsCollector != nil { + c.metricsCollector.AddIngressBackendSyncTime(util.GetTimeDifferenceInSeconds(startTime, endTime)) } - - if certificateId == nil { - tlsSecretData, err := getTlsSecretContent(namespace, secretName, c.client) - if err != nil { - return nil, err - } - - createCertificate, err := certificate.CreateImportedTypeCertificate(tlsSecretData.CaCertificateChain, tlsSecretData.ServerCertificate, - tlsSecretData.PrivateKey, certificateName, c.defaultCompartmentId, c.certificatesClient) - if err != nil { - return nil, err - } - - certificateId = createCertificate.Id - } - return certificateId, nil -} - -func createOrGetCaBundleForBackendSet(namespace string, secretName string, c *Controller) (*string, error) { - certificateName := getCertificateNameFromSecret(secretName) - caBundleId, err := certificate.FindCaBundleWithName(certificateName, c.defaultCompartmentId, c.certificatesClient) - if err != nil { - return nil, err - } - - if caBundleId == nil { - tlsSecretData, err := getTlsSecretContent(namespace, secretName, c.client) - if err != nil { - return nil, err - } - createCaBundle, err := certificate.CreateCaBundle(certificateName, c.defaultCompartmentId, c.certificatesClient, tlsSecretData.CaCertificateChain) - if err != nil { - return nil, err - } - caBundleId = createCaBundle.Id - } - return caBundleId, nil + return nil } func (c *Controller) deleteIngress(i *networkingv1.Ingress) error { diff --git a/pkg/controllers/ingress/ingressPath.yaml b/pkg/controllers/ingress/ingressPath.yaml new file mode 100644 index 00000000..f4e3710a --- /dev/null +++ b/pkg/controllers/ingress/ingressPath.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-readiness + namespace: default +spec: + rules: + - host: "foo.bar.com" + http: + paths: + - pathType: Exact + path: "/testecho1" + backend: + service: + name: testecho1 + port: + number: 80 \ No newline at end of file diff --git a/pkg/controllers/ingress/ingressPathWithFinalizer.yaml b/pkg/controllers/ingress/ingressPathWithFinalizer.yaml new file mode 100644 index 00000000..8779d38d --- /dev/null +++ b/pkg/controllers/ingress/ingressPathWithFinalizer.yaml @@ -0,0 +1,38 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-readiness-1 + namespace: default +spec: + rules: + - host: "foo.bar.com" + http: + paths: + - pathType: Exact + path: "/testecho1" + backend: + service: + name: testecho1 + port: + number: 80 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-readiness-2 + namespace: default + finalizers: + - oci.oraclecloud.com/ingress-controller-protection +spec: + rules: + - host: "foo.bar.com" + http: + paths: + - pathType: Exact + path: "/testecho1" + backend: + service: + name: testecho1 + port: + number: 80 \ No newline at end of file diff --git a/pkg/controllers/ingress/ingress_test.go b/pkg/controllers/ingress/ingress_test.go new file mode 100644 index 00000000..48c858d9 --- /dev/null +++ b/pkg/controllers/ingress/ingress_test.go @@ -0,0 +1,374 @@ +package ingress + +import ( + "context" + "net/http" + "sync" + "testing" + + . "github.com/onsi/gomega" + "github.com/oracle/oci-go-sdk/v65/certificates" + "github.com/oracle/oci-go-sdk/v65/certificatesmanagement" + "github.com/oracle/oci-go-sdk/v65/common" + ociloadbalancer "github.com/oracle/oci-go-sdk/v65/loadbalancer" + "github.com/oracle/oci-native-ingress-controller/pkg/certificate" + lb "github.com/oracle/oci-native-ingress-controller/pkg/loadbalancer" + . "github.com/oracle/oci-native-ingress-controller/pkg/oci/client" + "github.com/oracle/oci-native-ingress-controller/pkg/util" + "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/client-go/informers" + networkinginformers "k8s.io/client-go/informers/networking/v1" + fakeclientset "k8s.io/client-go/kubernetes/fake" + corelisters "k8s.io/client-go/listers/core/v1" + + "k8s.io/client-go/tools/cache" +) + +const ( + ingressPath = "ingressPath.yaml" + ingressPathWithFinalizer = "ingressPathWithFinalizer.yaml" + namespace = "default" +) + +func setUp(ctx context.Context, ingressClassList *networkingv1.IngressClassList, ingressList *networkingv1.IngressList, testService *v1.ServiceList) (networkinginformers.IngressClassInformer, networkinginformers.IngressInformer, corelisters.ServiceLister, *fakeclientset.Clientset) { + client := fakeclientset.NewSimpleClientset() + action := "list" + + util.UpdateFakeClientCall(client, action, "ingressclasses", ingressClassList) + util.UpdateFakeClientCall(client, action, "ingresses", ingressList) + util.UpdateFakeClientCall(client, "get", "ingresses", &ingressList.Items[0]) + util.UpdateFakeClientCall(client, "update", "ingresses", &ingressList.Items[0]) + util.UpdateFakeClientCall(client, "patch", "ingresses", &ingressList.Items[0]) + util.UpdateFakeClientCall(client, action, "services", testService) + + informerFactory := informers.NewSharedInformerFactory(client, 0) + ingressClassInformer := informerFactory.Networking().V1().IngressClasses() + ingressClassInformer.Lister() + + ingressInformer := informerFactory.Networking().V1().Ingresses() + ingressInformer.Lister() + + serviceInformer := informerFactory.Core().V1().Services() + serviceLister := serviceInformer.Lister() + + informerFactory.Start(ctx.Done()) + cache.WaitForCacheSync(ctx.Done(), ingressClassInformer.Informer().HasSynced) + cache.WaitForCacheSync(ctx.Done(), ingressInformer.Informer().HasSynced) + return ingressClassInformer, ingressInformer, serviceLister, client +} + +func inits(ctx context.Context, ingressClassList *networkingv1.IngressClassList, ingressList *networkingv1.IngressList) *Controller { + + testService := util.GetServiceListResource(namespace, "testecho1", 80) + lbClient := GetLoadBalancerClient() + certClient := GetCertClient() + certManageClient := GetCertManageClient() + + loadBalancerClient := &lb.LoadBalancerClient{ + LbClient: lbClient, + Mu: sync.Mutex{}, + Cache: map[string]*lb.LbCacheObj{}, + } + + certificatesClient := &certificate.CertificatesClient{ + ManagementClient: certManageClient, + CertificatesClient: certClient, + CertCache: map[string]*CertCacheObj{}, + CaBundleCache: map[string]*CaBundleCacheObj{}, + } + + ingressClassInformer, ingressInformer, serviceLister, client := setUp(ctx, ingressClassList, ingressList, testService) + c := NewController("oci.oraclecloud.com/native-ingress-controller", "", ingressClassInformer, + ingressInformer, serviceLister, client, loadBalancerClient, certificatesClient, nil) + return c +} + +func TestEnsureIngressSuccess(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + ingressList := util.ReadResourceAsIngressList(ingressPath) + c := inits(ctx, ingressClassList, ingressList) + err := c.ensureIngress(&ingressList.Items[0], &ingressClassList.Items[0]) + + Expect(err == nil).Should(Equal(true)) +} +func TestEnsureLoadBalancerIP(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + ingressList := util.ReadResourceAsIngressList(ingressPath) + c := inits(ctx, ingressClassList, ingressList) + err := c.ensureLoadBalancerIP("ip", &ingressList.Items[0]) + Expect(err == nil).Should(Equal(true)) +} + +func TestEnsureFinalizer(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + ingressList := util.ReadResourceAsIngressList(ingressPathWithFinalizer) + c := inits(ctx, ingressClassList, ingressList) + err := c.ensureFinalizer(&ingressList.Items[0]) + Expect(err == nil).Should(Equal(true)) + err = c.ensureFinalizer(&ingressList.Items[1]) + Expect(err == nil).Should(Equal(true)) +} + +func TestDeleteIngress(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + ingressList := util.ReadResourceAsIngressList(ingressPathWithFinalizer) + c := inits(ctx, ingressClassList, ingressList) + err := c.deleteIngress(&ingressList.Items[0]) + Expect(err == nil).Should(Equal(true)) + err = c.deleteIngress(&ingressList.Items[1]) + Expect(err == nil).Should(Equal(true)) +} + +func TestIngressAdd(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + ingressList := util.ReadResourceAsIngressList(ingressPath) + c := inits(ctx, ingressClassList, ingressList) + queueSize := c.queue.Len() + c.ingressAdd(&ingressList.Items[0]) + Expect(c.queue.Len()).Should(Equal(queueSize + 1)) +} + +func TestIngressUpdate(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + ingressList := util.ReadResourceAsIngressList(ingressPathWithFinalizer) + c := inits(ctx, ingressClassList, ingressList) + queueSize := c.queue.Len() + c.ingressUpdate(&ingressList.Items[0], &ingressList.Items[1]) + Expect(c.queue.Len()).Should(Equal(queueSize + 1)) +} +func TestIngressDelete(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + ingressList := util.ReadResourceAsIngressList(ingressPathWithFinalizer) + c := inits(ctx, ingressClassList, ingressList) + queueSize := c.queue.Len() + c.ingressDelete(&ingressList.Items[0]) + Expect(c.queue.Len()).Should(Equal(queueSize + 1)) +} + +func TestProcessNextItem(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassListWithLBSet("id") + ingressList := util.ReadResourceAsIngressList(ingressPathWithFinalizer) + c := inits(ctx, ingressClassList, ingressList) + + c.queue.Add("default-ingress-class") + res := c.processNextItem() + Expect(res).Should(BeTrue()) +} + +func GetCertManageClient() CertificateManagementInterface { + return &MockCertificateManagerClient{} +} + +type MockCertificateManagerClient struct { +} + +func (m MockCertificateManagerClient) CreateCertificate(ctx context.Context, request certificatesmanagement.CreateCertificateRequest) (certificatesmanagement.CreateCertificateResponse, error) { + return certificatesmanagement.CreateCertificateResponse{}, nil +} + +func (m MockCertificateManagerClient) GetCertificate(ctx context.Context, request certificatesmanagement.GetCertificateRequest) (certificatesmanagement.GetCertificateResponse, error) { + return certificatesmanagement.GetCertificateResponse{}, nil +} + +func (m MockCertificateManagerClient) ListCertificates(ctx context.Context, request certificatesmanagement.ListCertificatesRequest) (certificatesmanagement.ListCertificatesResponse, error) { + return certificatesmanagement.ListCertificatesResponse{}, nil +} + +func (m MockCertificateManagerClient) ScheduleCertificateDeletion(ctx context.Context, request certificatesmanagement.ScheduleCertificateDeletionRequest) (certificatesmanagement.ScheduleCertificateDeletionResponse, error) { + return certificatesmanagement.ScheduleCertificateDeletionResponse{}, nil +} + +func (m MockCertificateManagerClient) CreateCaBundle(ctx context.Context, request certificatesmanagement.CreateCaBundleRequest) (certificatesmanagement.CreateCaBundleResponse, error) { + return certificatesmanagement.CreateCaBundleResponse{}, nil +} + +func (m MockCertificateManagerClient) GetCaBundle(ctx context.Context, request certificatesmanagement.GetCaBundleRequest) (certificatesmanagement.GetCaBundleResponse, error) { + return certificatesmanagement.GetCaBundleResponse{}, nil +} + +func (m MockCertificateManagerClient) ListCaBundles(ctx context.Context, request certificatesmanagement.ListCaBundlesRequest) (certificatesmanagement.ListCaBundlesResponse, error) { + return certificatesmanagement.ListCaBundlesResponse{}, nil +} + +func (m MockCertificateManagerClient) DeleteCaBundle(ctx context.Context, request certificatesmanagement.DeleteCaBundleRequest) (certificatesmanagement.DeleteCaBundleResponse, error) { + return certificatesmanagement.DeleteCaBundleResponse{}, nil +} + +func GetCertClient() CertificateInterface { + return &MockCertificateClient{} +} + +type MockCertificateClient struct { +} + +func (m MockCertificateClient) SetCertCache(cert *certificatesmanagement.Certificate) { + +} + +func (m MockCertificateClient) GetFromCertCache(certId string) *CertCacheObj { + return nil +} + +func (m MockCertificateClient) SetCaBundleCache(caBundle *certificatesmanagement.CaBundle) { + +} + +func (m MockCertificateClient) GetFromCaBundleCache(id string) *CaBundleCacheObj { + return nil +} + +func (m MockCertificateClient) CreateCertificate(ctx context.Context, req certificatesmanagement.CreateCertificateRequest) (*certificatesmanagement.Certificate, error) { + return &certificatesmanagement.Certificate{}, nil +} + +func (m MockCertificateClient) CreateCaBundle(ctx context.Context, req certificatesmanagement.CreateCaBundleRequest) (*certificatesmanagement.CaBundle, error) { + return &certificatesmanagement.CaBundle{}, nil +} + +func (m MockCertificateClient) GetCertificate(ctx context.Context, req certificatesmanagement.GetCertificateRequest) (*certificatesmanagement.Certificate, error) { + return &certificatesmanagement.Certificate{}, nil +} + +func (m MockCertificateClient) ListCertificates(ctx context.Context, req certificatesmanagement.ListCertificatesRequest) (*certificatesmanagement.CertificateCollection, *string, error) { + return &certificatesmanagement.CertificateCollection{}, nil, nil +} + +func (m MockCertificateClient) ScheduleCertificateDeletion(ctx context.Context, req certificatesmanagement.ScheduleCertificateDeletionRequest) error { + return nil +} + +func (m MockCertificateClient) GetCaBundle(ctx context.Context, req certificatesmanagement.GetCaBundleRequest) (*certificatesmanagement.CaBundle, error) { + return &certificatesmanagement.CaBundle{}, nil +} + +func (m MockCertificateClient) ListCaBundles(ctx context.Context, req certificatesmanagement.ListCaBundlesRequest) (*certificatesmanagement.CaBundleCollection, error) { + return &certificatesmanagement.CaBundleCollection{}, nil +} + +func (m MockCertificateClient) DeleteCaBundle(ctx context.Context, req certificatesmanagement.DeleteCaBundleRequest) (*http.Response, error) { + return &http.Response{}, nil +} + +func (m MockCertificateClient) GetCertificateBundle(ctx context.Context, request certificates.GetCertificateBundleRequest) (certificates.GetCertificateBundleResponse, error) { + return certificates.GetCertificateBundleResponse{}, nil +} + +func GetLoadBalancerClient() LoadBalancerInterface { + return &MockLoadBalancerClient{} +} + +type MockLoadBalancerClient struct { +} + +func (m MockLoadBalancerClient) GetLoadBalancer(ctx context.Context, request ociloadbalancer.GetLoadBalancerRequest) (ociloadbalancer.GetLoadBalancerResponse, error) { + res := util.SampleLoadBalancerResponse() + return res, nil +} + +func (m MockLoadBalancerClient) CreateLoadBalancer(ctx context.Context, request ociloadbalancer.CreateLoadBalancerRequest) (ociloadbalancer.CreateLoadBalancerResponse, error) { + return ociloadbalancer.CreateLoadBalancerResponse{}, nil +} + +func (m MockLoadBalancerClient) DeleteLoadBalancer(ctx context.Context, request ociloadbalancer.DeleteLoadBalancerRequest) (ociloadbalancer.DeleteLoadBalancerResponse, error) { + return ociloadbalancer.DeleteLoadBalancerResponse{ + OpcRequestId: common.String("OpcRequestId"), + OpcWorkRequestId: common.String("OpcWorkRequestId"), + }, nil +} + +func (m MockLoadBalancerClient) GetWorkRequest(ctx context.Context, request ociloadbalancer.GetWorkRequestRequest) (ociloadbalancer.GetWorkRequestResponse, error) { + id := "id" + requestId := "opcrequestid" + return ociloadbalancer.GetWorkRequestResponse{ + RawResponse: nil, + WorkRequest: ociloadbalancer.WorkRequest{ + Id: &id, + LoadBalancerId: &id, + Type: nil, + LifecycleState: ociloadbalancer.WorkRequestLifecycleStateSucceeded, + }, + OpcRequestId: &requestId, + }, nil +} + +func (m MockLoadBalancerClient) CreateBackendSet(ctx context.Context, request ociloadbalancer.CreateBackendSetRequest) (ociloadbalancer.CreateBackendSetResponse, error) { + reqId := "opcrequestid" + return ociloadbalancer.CreateBackendSetResponse{ + RawResponse: nil, + OpcWorkRequestId: &reqId, + OpcRequestId: &reqId, + }, nil +} + +func (m MockLoadBalancerClient) UpdateBackendSet(ctx context.Context, request ociloadbalancer.UpdateBackendSetRequest) (ociloadbalancer.UpdateBackendSetResponse, error) { + reqId := "opcrequestid" + res := ociloadbalancer.UpdateBackendSetResponse{ + RawResponse: nil, + OpcWorkRequestId: &reqId, + OpcRequestId: &reqId, + } + return res, nil +} + +func (m MockLoadBalancerClient) DeleteBackendSet(ctx context.Context, request ociloadbalancer.DeleteBackendSetRequest) (ociloadbalancer.DeleteBackendSetResponse, error) { + return ociloadbalancer.DeleteBackendSetResponse{}, nil +} + +func (m MockLoadBalancerClient) GetBackendSetHealth(ctx context.Context, request ociloadbalancer.GetBackendSetHealthRequest) (ociloadbalancer.GetBackendSetHealthResponse, error) { + return ociloadbalancer.GetBackendSetHealthResponse{}, nil +} + +func (m MockLoadBalancerClient) CreateRoutingPolicy(ctx context.Context, request ociloadbalancer.CreateRoutingPolicyRequest) (ociloadbalancer.CreateRoutingPolicyResponse, error) { + return ociloadbalancer.CreateRoutingPolicyResponse{}, nil +} + +func (m MockLoadBalancerClient) UpdateRoutingPolicy(ctx context.Context, request ociloadbalancer.UpdateRoutingPolicyRequest) (ociloadbalancer.UpdateRoutingPolicyResponse, error) { + return ociloadbalancer.UpdateRoutingPolicyResponse{}, nil +} + +func (m MockLoadBalancerClient) DeleteRoutingPolicy(ctx context.Context, request ociloadbalancer.DeleteRoutingPolicyRequest) (ociloadbalancer.DeleteRoutingPolicyResponse, error) { + return ociloadbalancer.DeleteRoutingPolicyResponse{}, nil +} + +func (m MockLoadBalancerClient) CreateListener(ctx context.Context, request ociloadbalancer.CreateListenerRequest) (ociloadbalancer.CreateListenerResponse, error) { + reqId := "opcrequestid" + res := ociloadbalancer.CreateListenerResponse{ + RawResponse: nil, + OpcWorkRequestId: &reqId, + OpcRequestId: &reqId, + } + return res, nil +} + +func (m MockLoadBalancerClient) UpdateListener(ctx context.Context, request ociloadbalancer.UpdateListenerRequest) (ociloadbalancer.UpdateListenerResponse, error) { + return ociloadbalancer.UpdateListenerResponse{}, nil +} + +func (m MockLoadBalancerClient) DeleteListener(ctx context.Context, request ociloadbalancer.DeleteListenerRequest) (ociloadbalancer.DeleteListenerResponse, error) { + return ociloadbalancer.DeleteListenerResponse{}, nil +} diff --git a/pkg/controllers/ingress/util.go b/pkg/controllers/ingress/util.go index c6bdc8c7..1116e47b 100644 --- a/pkg/controllers/ingress/util.go +++ b/pkg/controllers/ingress/util.go @@ -10,42 +10,12 @@ package ingress import ( - "context" - "fmt" "reflect" - "strings" ociloadbalancer "github.com/oracle/oci-go-sdk/v65/loadbalancer" "github.com/oracle/oci-native-ingress-controller/pkg/util" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" ) -type TLSSecretData struct { - // This would hold server certificate and any chain of trust. - CaCertificateChain *string - ServerCertificate *string - PrivateKey *string -} - -func getTlsSecretContent(namespace string, secretName string, client kubernetes.Interface) (*TLSSecretData, error) { - secret, err := client.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) - if err != nil { - return nil, err - } - caCertificateChain := string(secret.Data["ca.crt"]) - serverCertificate := string(secret.Data["tls.crt"]) - privateKey := string(secret.Data["tls.key"]) - return &TLSSecretData{CaCertificateChain: &caCertificateChain, ServerCertificate: &serverCertificate, PrivateKey: &privateKey}, nil -} - -func getCertificateNameFromSecret(secretName string) string { - if secretName == "" { - return "" - } - return fmt.Sprintf("ic-%s", secretName) -} - func compareHealthCheckers(healthCheckerDetails *ociloadbalancer.HealthCheckerDetails, healthChecker *ociloadbalancer.HealthChecker) bool { if reflect.DeepEqual(healthCheckerDetails.Protocol, healthChecker.Protocol) { if *healthChecker.Protocol == util.ProtocolTCP { @@ -71,7 +41,3 @@ func compareHttpHealthCheckerAttributes(healthCheckerDetails *ociloadbalancer.He reflect.DeepEqual(healthCheckerDetails.ResponseBodyRegex, healthChecker.ResponseBodyRegex) && reflect.DeepEqual(healthCheckerDetails.IsForcePlainText, healthChecker.IsForcePlainText) } - -func isTrustAuthorityCaBundle(id string) bool { - return strings.Contains(id, "cabundle") -} diff --git a/pkg/controllers/ingressclass/ingressclass.go b/pkg/controllers/ingressclass/ingressclass.go index b497688e..781bd3ca 100644 --- a/pkg/controllers/ingressclass/ingressclass.go +++ b/pkg/controllers/ingressclass/ingressclass.go @@ -15,6 +15,7 @@ import ( "fmt" "time" + "github.com/oracle/oci-native-ingress-controller/pkg/exception" "k8s.io/klog/v2" ctrcache "sigs.k8s.io/controller-runtime/pkg/cache" @@ -208,7 +209,7 @@ func (c *Controller) sync(key string) error { func (c *Controller) getLoadBalancer(ic *networkingv1.IngressClass) (*ociloadbalancer.LoadBalancer, error) { lbID := util.GetIngressClassLoadBalancerId(ic) if lbID == "" { - return nil, ¬FoundServiceError{} + return nil, &exception.NotFoundServiceError{} } lb, _, err := c.lbClient.GetLoadBalancer(context.TODO(), lbID) @@ -472,34 +473,3 @@ OUTER: } return } - -// helper struct since the oci go sdk doesn't let you create errors directly -// since the struct is private... -type notFoundServiceError struct { - common.ServiceError -} - -func (e *notFoundServiceError) GetHTTPStatusCode() int { - return 404 -} - -// The human-readable error string as sent by the service -func (e *notFoundServiceError) GetMessage() string { - return "NotFound" -} - -// A short error code that defines the error, meant for programmatic parsing. -// See https://docs.cloud.oracle.com/Content/API/References/apierrors.htm -func (e *notFoundServiceError) GetCode() string { - return "NotFound" -} - -// Unique Oracle-assigned identifier for the request. -// If you need to contact Oracle about a particular request, please provide the request ID. -func (e *notFoundServiceError) GetOpcRequestID() string { - return "fakeopcrequestid" -} - -func (e *notFoundServiceError) Error() string { - return "NotFound" -} diff --git a/pkg/controllers/ingressclass/ingressclass_test.go b/pkg/controllers/ingressclass/ingressclass_test.go new file mode 100644 index 00000000..02954a45 --- /dev/null +++ b/pkg/controllers/ingressclass/ingressclass_test.go @@ -0,0 +1,252 @@ +package ingressclass + +import ( + "context" + "fmt" + "sync" + "testing" + + . "github.com/onsi/gomega" + "github.com/oracle/oci-go-sdk/v65/common" + ociloadbalancer "github.com/oracle/oci-go-sdk/v65/loadbalancer" + lb "github.com/oracle/oci-native-ingress-controller/pkg/loadbalancer" + "github.com/oracle/oci-native-ingress-controller/pkg/oci/client" + "github.com/oracle/oci-native-ingress-controller/pkg/util" + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + networkinginformers "k8s.io/client-go/informers/networking/v1" + fakeclientset "k8s.io/client-go/kubernetes/fake" + + "k8s.io/client-go/tools/cache" +) + +func TestEnsureLoadBalancer(t *testing.T) { + RegisterTestingT(t) + ctx := context.TODO() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList) + + err := c.ensureLoadBalancer(&ingressClassList.Items[0]) + Expect(err).Should(BeNil()) +} + +func TestIngressClassAdd(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList) + queueSize := c.queue.Len() + c.ingressClassAdd(&ingressClassList.Items[0]) + Expect(c.queue.Len()).Should(Equal(queueSize + 1)) +} + +func TestIngressUpdate(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList) + queueSize := c.queue.Len() + c.ingressClassUpdate(&ingressClassList.Items[0], &ingressClassList.Items[0]) + Expect(c.queue.Len()).Should(Equal(queueSize + 1)) +} +func TestIngressClassDelete(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList) + queueSize := c.queue.Len() + c.ingressClassDelete(&ingressClassList.Items[0]) + Expect(c.queue.Len()).Should(Equal(queueSize + 1)) +} + +func TestDeleteIngressClass(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList) + err := c.deleteIngressClass(&ingressClassList.Items[0]) + Expect(err).Should(BeNil()) +} + +func TestDeleteLoadBalancer(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList) + err := c.deleteLoadBalancer(&ingressClassList.Items[0]) + Expect(err).Should(BeNil()) +} + +func TestEnsureFinalizer(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList) + err := c.ensureFinalizer(&ingressClassList.Items[0]) + Expect(err).Should(BeNil()) +} + +func TestDeleteFinalizer(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList) + var finalizers []string + finalizer := "oci.oraclecloud.com/ingress-controller-protection" + finalizers = append(finalizers, finalizer) + ingressClass := &networkingv1.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Annotations: map[string]string{"ingressclass.kubernetes.io/is-default-class": fmt.Sprint("isDefault")}, + Finalizers: finalizers, + }, + Spec: networkingv1.IngressClassSpec{ + Controller: "controller", + }, + } + err := c.deleteFinalizer(ingressClass) // with finalizer + Expect(err).Should(BeNil()) + err = c.deleteFinalizer(&ingressClassList.Items[0]) + Expect(err).Should(BeNil()) +} + +func inits(ctx context.Context, ingressClassList *networkingv1.IngressClassList) *Controller { + + lbClient := getLoadBalancerClient() + + loadBalancerClient := &lb.LoadBalancerClient{ + LbClient: lbClient, + Mu: sync.Mutex{}, + Cache: map[string]*lb.LbCacheObj{}, + } + + ingressClassInformer, client := setUp(ctx, ingressClassList) + c := NewController("", "", + "oci.oraclecloud.com/native-ingress-controller", ingressClassInformer, client, loadBalancerClient, nil) + return c +} + +func setUp(ctx context.Context, ingressClassList *networkingv1.IngressClassList) (networkinginformers.IngressClassInformer, *fakeclientset.Clientset) { + client := fakeclientset.NewSimpleClientset() + + util.UpdateFakeClientCall(client, "list", "ingressclasses", ingressClassList) + util.UpdateFakeClientCall(client, "patch", "ingressclasses", &ingressClassList.Items[0]) + + informerFactory := informers.NewSharedInformerFactory(client, 0) + ingressClassInformer := informerFactory.Networking().V1().IngressClasses() + ingressClassInformer.Lister() + + informerFactory.Start(ctx.Done()) + cache.WaitForCacheSync(ctx.Done(), ingressClassInformer.Informer().HasSynced) + + return ingressClassInformer, client +} + +func getLoadBalancerClient() client.LoadBalancerInterface { + return &MockLoadBalancerClient{} +} + +type MockLoadBalancerClient struct { +} + +func (m MockLoadBalancerClient) GetLoadBalancer(ctx context.Context, request ociloadbalancer.GetLoadBalancerRequest) (ociloadbalancer.GetLoadBalancerResponse, error) { + res := util.SampleLoadBalancerResponse() + return res, nil +} + +func (m MockLoadBalancerClient) CreateLoadBalancer(ctx context.Context, request ociloadbalancer.CreateLoadBalancerRequest) (ociloadbalancer.CreateLoadBalancerResponse, error) { + id := "id" + return ociloadbalancer.CreateLoadBalancerResponse{ + RawResponse: nil, + OpcWorkRequestId: &id, + OpcRequestId: &id, + }, nil +} + +func (m MockLoadBalancerClient) DeleteLoadBalancer(ctx context.Context, request ociloadbalancer.DeleteLoadBalancerRequest) (ociloadbalancer.DeleteLoadBalancerResponse, error) { + return ociloadbalancer.DeleteLoadBalancerResponse{ + OpcRequestId: common.String("OpcRequestId"), + OpcWorkRequestId: common.String("OpcWorkRequestId"), + }, nil +} + +func (m MockLoadBalancerClient) GetWorkRequest(ctx context.Context, request ociloadbalancer.GetWorkRequestRequest) (ociloadbalancer.GetWorkRequestResponse, error) { + id := "id" + requestId := "opcrequestid" + return ociloadbalancer.GetWorkRequestResponse{ + RawResponse: nil, + WorkRequest: ociloadbalancer.WorkRequest{ + Id: &id, + LoadBalancerId: &id, + Type: nil, + LifecycleState: ociloadbalancer.WorkRequestLifecycleStateSucceeded, + }, + OpcRequestId: &requestId, + }, nil +} + +func (m MockLoadBalancerClient) CreateBackendSet(ctx context.Context, request ociloadbalancer.CreateBackendSetRequest) (ociloadbalancer.CreateBackendSetResponse, error) { + return ociloadbalancer.CreateBackendSetResponse{}, nil +} + +func (m MockLoadBalancerClient) UpdateBackendSet(ctx context.Context, request ociloadbalancer.UpdateBackendSetRequest) (ociloadbalancer.UpdateBackendSetResponse, error) { + reqId := "opcrequestid" + res := ociloadbalancer.UpdateBackendSetResponse{ + RawResponse: nil, + OpcWorkRequestId: &reqId, + OpcRequestId: &reqId, + } + return res, nil +} + +func (m MockLoadBalancerClient) DeleteBackendSet(ctx context.Context, request ociloadbalancer.DeleteBackendSetRequest) (ociloadbalancer.DeleteBackendSetResponse, error) { + return ociloadbalancer.DeleteBackendSetResponse{}, nil +} + +func (m MockLoadBalancerClient) GetBackendSetHealth(ctx context.Context, request ociloadbalancer.GetBackendSetHealthRequest) (ociloadbalancer.GetBackendSetHealthResponse, error) { + backendCount := 1 + return ociloadbalancer.GetBackendSetHealthResponse{ + RawResponse: nil, + BackendSetHealth: ociloadbalancer.BackendSetHealth{ + Status: ociloadbalancer.BackendSetHealthStatusOk, + WarningStateBackendNames: nil, + CriticalStateBackendNames: nil, + UnknownStateBackendNames: nil, + TotalBackendCount: &backendCount, + }, + OpcRequestId: nil, + ETag: nil, + }, nil +} + +func (m MockLoadBalancerClient) CreateRoutingPolicy(ctx context.Context, request ociloadbalancer.CreateRoutingPolicyRequest) (ociloadbalancer.CreateRoutingPolicyResponse, error) { + return ociloadbalancer.CreateRoutingPolicyResponse{}, nil +} + +func (m MockLoadBalancerClient) UpdateRoutingPolicy(ctx context.Context, request ociloadbalancer.UpdateRoutingPolicyRequest) (ociloadbalancer.UpdateRoutingPolicyResponse, error) { + return ociloadbalancer.UpdateRoutingPolicyResponse{}, nil +} + +func (m MockLoadBalancerClient) DeleteRoutingPolicy(ctx context.Context, request ociloadbalancer.DeleteRoutingPolicyRequest) (ociloadbalancer.DeleteRoutingPolicyResponse, error) { + return ociloadbalancer.DeleteRoutingPolicyResponse{}, nil +} + +func (m MockLoadBalancerClient) CreateListener(ctx context.Context, request ociloadbalancer.CreateListenerRequest) (ociloadbalancer.CreateListenerResponse, error) { + return ociloadbalancer.CreateListenerResponse{}, nil +} + +func (m MockLoadBalancerClient) UpdateListener(ctx context.Context, request ociloadbalancer.UpdateListenerRequest) (ociloadbalancer.UpdateListenerResponse, error) { + return ociloadbalancer.UpdateListenerResponse{}, nil +} + +func (m MockLoadBalancerClient) DeleteListener(ctx context.Context, request ociloadbalancer.DeleteListenerRequest) (ociloadbalancer.DeleteListenerResponse, error) { + return ociloadbalancer.DeleteListenerResponse{}, nil +} diff --git a/pkg/controllers/routingpolicy/routePath.yaml b/pkg/controllers/routingpolicy/routePath.yaml new file mode 100644 index 00000000..f4e3710a --- /dev/null +++ b/pkg/controllers/routingpolicy/routePath.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-readiness + namespace: default +spec: + rules: + - host: "foo.bar.com" + http: + paths: + - pathType: Exact + path: "/testecho1" + backend: + service: + name: testecho1 + port: + number: 80 \ No newline at end of file diff --git a/pkg/controllers/routingpolicy/routingpolicy_test.go b/pkg/controllers/routingpolicy/routingpolicy_test.go new file mode 100644 index 00000000..b44a491d --- /dev/null +++ b/pkg/controllers/routingpolicy/routingpolicy_test.go @@ -0,0 +1,171 @@ +package routingpolicy + +import ( + "context" + "sync" + "testing" + + . "github.com/onsi/gomega" + ociloadbalancer "github.com/oracle/oci-go-sdk/v65/loadbalancer" + lb "github.com/oracle/oci-native-ingress-controller/pkg/loadbalancer" + "github.com/oracle/oci-native-ingress-controller/pkg/oci/client" + "github.com/oracle/oci-native-ingress-controller/pkg/util" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/client-go/informers" + networkinginformers "k8s.io/client-go/informers/networking/v1" + fakeclientset "k8s.io/client-go/kubernetes/fake" + corelisters "k8s.io/client-go/listers/core/v1" + + "k8s.io/client-go/tools/cache" +) + +func TestEnsureRoutingRules(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList, "routePath.yaml") + + err := c.ensureRoutingRules(&ingressClassList.Items[0]) + Expect(err == nil).Should(Equal(true)) +} + +func TestRunPusher(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ingressClassList := util.GetIngressClassList() + c := inits(ctx, ingressClassList, "routePath.yaml") + + c.runPusher() + Expect(c.queue.Len()).Should(Equal(1)) +} + +func inits(ctx context.Context, ingressClassList *networkingv1.IngressClassList, yamlPath string) *Controller { + + ingressList := util.ReadResourceAsIngressList(yamlPath) + testService := util.GetServiceListResource("test", "testecho1", 80) + lbClient := getLoadBalancerClient() + + loadBalancerClient := &lb.LoadBalancerClient{ + LbClient: lbClient, + Mu: sync.Mutex{}, + Cache: map[string]*lb.LbCacheObj{}, + } + + ingressClassInformer, ingressInformer, serviceLister, client := setUp(ctx, ingressClassList, ingressList, testService) + c := NewController("oci.oraclecloud.com/native-ingress-controller", + ingressClassInformer, ingressInformer, serviceLister, client, loadBalancerClient) + return c +} + +func setUp(ctx context.Context, ingressClassList *networkingv1.IngressClassList, ingressList *networkingv1.IngressList, testService *corev1.ServiceList) (networkinginformers.IngressClassInformer, networkinginformers.IngressInformer, corelisters.ServiceLister, *fakeclientset.Clientset) { + client := fakeclientset.NewSimpleClientset() + + action := "list" + util.UpdateFakeClientCall(client, action, "ingressclasses", ingressClassList) + util.UpdateFakeClientCall(client, action, "ingresses", ingressList) + util.UpdateFakeClientCall(client, action, "services", testService) + + informerFactory := informers.NewSharedInformerFactory(client, 0) + ingressClassInformer := informerFactory.Networking().V1().IngressClasses() + ingressClassInformer.Lister() + + ingressInformer := informerFactory.Networking().V1().Ingresses() + ingressInformer.Lister() + + serviceInformer := informerFactory.Core().V1().Services() + serviceLister := serviceInformer.Lister() + + informerFactory.Start(ctx.Done()) + cache.WaitForCacheSync(ctx.Done(), ingressClassInformer.Informer().HasSynced) + cache.WaitForCacheSync(ctx.Done(), ingressInformer.Informer().HasSynced) + cache.WaitForCacheSync(ctx.Done(), serviceInformer.Informer().HasSynced) + return ingressClassInformer, ingressInformer, serviceLister, client +} + +func getLoadBalancerClient() client.LoadBalancerInterface { + return &MockLoadBalancerClient{} +} + +type MockLoadBalancerClient struct { +} + +func (m MockLoadBalancerClient) GetLoadBalancer(ctx context.Context, request ociloadbalancer.GetLoadBalancerRequest) (ociloadbalancer.GetLoadBalancerResponse, error) { + res := util.SampleLoadBalancerResponse() + return res, nil +} + +func (m MockLoadBalancerClient) CreateLoadBalancer(ctx context.Context, request ociloadbalancer.CreateLoadBalancerRequest) (ociloadbalancer.CreateLoadBalancerResponse, error) { + return ociloadbalancer.CreateLoadBalancerResponse{}, nil +} + +func (m MockLoadBalancerClient) DeleteLoadBalancer(ctx context.Context, request ociloadbalancer.DeleteLoadBalancerRequest) (ociloadbalancer.DeleteLoadBalancerResponse, error) { + return ociloadbalancer.DeleteLoadBalancerResponse{}, nil +} + +func (m MockLoadBalancerClient) GetWorkRequest(ctx context.Context, request ociloadbalancer.GetWorkRequestRequest) (ociloadbalancer.GetWorkRequestResponse, error) { + id := "id" + requestId := "opcrequestid" + return ociloadbalancer.GetWorkRequestResponse{ + RawResponse: nil, + WorkRequest: ociloadbalancer.WorkRequest{ + Id: &id, + LoadBalancerId: &id, + Type: nil, + LifecycleState: ociloadbalancer.WorkRequestLifecycleStateSucceeded, + }, + OpcRequestId: &requestId, + }, nil +} + +func (m MockLoadBalancerClient) CreateBackendSet(ctx context.Context, request ociloadbalancer.CreateBackendSetRequest) (ociloadbalancer.CreateBackendSetResponse, error) { + return ociloadbalancer.CreateBackendSetResponse{}, nil +} + +func (m MockLoadBalancerClient) UpdateBackendSet(ctx context.Context, request ociloadbalancer.UpdateBackendSetRequest) (ociloadbalancer.UpdateBackendSetResponse, error) { + return ociloadbalancer.UpdateBackendSetResponse{}, nil +} + +func (m MockLoadBalancerClient) DeleteBackendSet(ctx context.Context, request ociloadbalancer.DeleteBackendSetRequest) (ociloadbalancer.DeleteBackendSetResponse, error) { + return ociloadbalancer.DeleteBackendSetResponse{}, nil +} + +func (m MockLoadBalancerClient) GetBackendSetHealth(ctx context.Context, request ociloadbalancer.GetBackendSetHealthRequest) (ociloadbalancer.GetBackendSetHealthResponse, error) { + return ociloadbalancer.GetBackendSetHealthResponse{}, nil +} + +func (m MockLoadBalancerClient) CreateRoutingPolicy(ctx context.Context, request ociloadbalancer.CreateRoutingPolicyRequest) (ociloadbalancer.CreateRoutingPolicyResponse, error) { + id := "id" + return ociloadbalancer.CreateRoutingPolicyResponse{ + RawResponse: nil, + OpcWorkRequestId: &id, + OpcRequestId: &id, + }, nil +} + +func (m MockLoadBalancerClient) UpdateRoutingPolicy(ctx context.Context, request ociloadbalancer.UpdateRoutingPolicyRequest) (ociloadbalancer.UpdateRoutingPolicyResponse, error) { + id := "id" + return ociloadbalancer.UpdateRoutingPolicyResponse{ + RawResponse: nil, + OpcWorkRequestId: &id, + OpcRequestId: &id, + }, nil +} + +func (m MockLoadBalancerClient) DeleteRoutingPolicy(ctx context.Context, request ociloadbalancer.DeleteRoutingPolicyRequest) (ociloadbalancer.DeleteRoutingPolicyResponse, error) { + return ociloadbalancer.DeleteRoutingPolicyResponse{}, nil +} + +func (m MockLoadBalancerClient) CreateListener(ctx context.Context, request ociloadbalancer.CreateListenerRequest) (ociloadbalancer.CreateListenerResponse, error) { + return ociloadbalancer.CreateListenerResponse{}, nil +} + +func (m MockLoadBalancerClient) UpdateListener(ctx context.Context, request ociloadbalancer.UpdateListenerRequest) (ociloadbalancer.UpdateListenerResponse, error) { + return ociloadbalancer.UpdateListenerResponse{}, nil +} + +func (m MockLoadBalancerClient) DeleteListener(ctx context.Context, request ociloadbalancer.DeleteListenerRequest) (ociloadbalancer.DeleteListenerResponse, error) { + return ociloadbalancer.DeleteListenerResponse{}, nil +} diff --git a/pkg/exception/util.go b/pkg/exception/util.go new file mode 100644 index 00000000..5ce3616d --- /dev/null +++ b/pkg/exception/util.go @@ -0,0 +1,36 @@ +package exception + +import ( + "github.com/oracle/oci-go-sdk/v65/common" +) + +// helper struct since the oci go sdk doesn't let you create errors directly +// since the struct is private... +type NotFoundServiceError struct { + common.ServiceError +} + +func (e *NotFoundServiceError) GetHTTPStatusCode() int { + return 404 +} + +// The human-readable error string as sent by the service +func (e *NotFoundServiceError) GetMessage() string { + return "NotFound" +} + +// A short error code that defines the error, meant for programmatic parsing. +// See https://docs.cloud.oracle.com/Content/API/References/apierrors.htm +func (e *NotFoundServiceError) GetCode() string { + return "NotFound" +} + +// Unique Oracle-assigned identifier for the request. +// If you need to contact Oracle about a particular request, please provide the request ID. +func (e *NotFoundServiceError) GetOpcRequestID() string { + return "fakeopcrequestid" +} + +func (e *NotFoundServiceError) Error() string { + return "NotFound" +} diff --git a/pkg/loadbalancer/loadbalancer.go b/pkg/loadbalancer/loadbalancer.go index 8dad4ecd..6b3884b6 100644 --- a/pkg/loadbalancer/loadbalancer.go +++ b/pkg/loadbalancer/loadbalancer.go @@ -18,51 +18,52 @@ import ( "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/loadbalancer" + "github.com/oracle/oci-native-ingress-controller/pkg/oci/client" "github.com/oracle/oci-native-ingress-controller/pkg/util" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" ) -type lbCacheObj struct { +type LbCacheObj struct { LB *loadbalancer.LoadBalancer Age time.Time ETag string } type LoadBalancerClient struct { - lbClient *loadbalancer.LoadBalancerClient + LbClient client.LoadBalancerInterface - mu sync.Mutex - cache map[string]*lbCacheObj + Mu sync.Mutex + Cache map[string]*LbCacheObj } func New(lbClient *loadbalancer.LoadBalancerClient) *LoadBalancerClient { return &LoadBalancerClient{ - lbClient: lbClient, - cache: map[string]*lbCacheObj{}, + LbClient: client.NewLoadBalancerClient(lbClient), + Cache: map[string]*LbCacheObj{}, } } func (lbc *LoadBalancerClient) setCache(lb *loadbalancer.LoadBalancer, etag string) { - lbc.mu.Lock() - lbc.cache[*lb.Id] = &lbCacheObj{lb, time.Now(), etag} - lbc.mu.Unlock() + lbc.Mu.Lock() + lbc.Cache[*lb.Id] = &LbCacheObj{lb, time.Now(), etag} + lbc.Mu.Unlock() } -func (lbc *LoadBalancerClient) getFromCache(lbID string) *lbCacheObj { - lbc.mu.Lock() - defer lbc.mu.Unlock() - return lbc.cache[lbID] +func (lbc *LoadBalancerClient) getFromCache(lbID string) *LbCacheObj { + lbc.Mu.Lock() + defer lbc.Mu.Unlock() + return lbc.Cache[lbID] } -func (lbc *LoadBalancerClient) removeFromCache(lbID string) *lbCacheObj { - lbc.mu.Lock() - defer lbc.mu.Unlock() - return lbc.cache[lbID] +func (lbc *LoadBalancerClient) removeFromCache(lbID string) *LbCacheObj { + lbc.Mu.Lock() + defer lbc.Mu.Unlock() + return lbc.Cache[lbID] } func (lbc *LoadBalancerClient) getLoadBalancerBustCache(ctx context.Context, lbID string) (*loadbalancer.LoadBalancer, string, error) { - resp, err := lbc.lbClient.GetLoadBalancer(ctx, loadbalancer.GetLoadBalancerRequest{ + resp, err := lbc.LbClient.GetLoadBalancer(ctx, loadbalancer.GetLoadBalancerRequest{ LoadBalancerId: common.String(lbID), }) if err != nil { @@ -74,7 +75,7 @@ func (lbc *LoadBalancerClient) getLoadBalancerBustCache(ctx context.Context, lbI } func (lbc *LoadBalancerClient) GetBackendSetHealth(ctx context.Context, lbID string, backendSetName string) (*loadbalancer.BackendSetHealth, error) { - resp, err := lbc.lbClient.GetBackendSetHealth(ctx, loadbalancer.GetBackendSetHealthRequest{ + resp, err := lbc.LbClient.GetBackendSetHealth(ctx, loadbalancer.GetBackendSetHealthRequest{ LoadBalancerId: common.String(lbID), BackendSetName: common.String(backendSetName), }) @@ -86,7 +87,7 @@ func (lbc *LoadBalancerClient) GetBackendSetHealth(ctx context.Context, lbID str } func (lbc *LoadBalancerClient) CreateLoadBalancer(ctx context.Context, req loadbalancer.CreateLoadBalancerRequest) (*loadbalancer.LoadBalancer, error) { - resp, err := lbc.lbClient.CreateLoadBalancer(ctx, req) + resp, err := lbc.LbClient.CreateLoadBalancer(ctx, req) if err != nil { return nil, err } @@ -122,7 +123,7 @@ func (lbc *LoadBalancerClient) DeleteLoadBalancer(ctx context.Context, lbID stri } klog.Infof("Deleting load balancer with request %s", util.PrettyPrint(deleteLoadBalancerRequest)) - resp, err := lbc.lbClient.DeleteLoadBalancer(ctx, deleteLoadBalancerRequest) + resp, err := lbc.LbClient.DeleteLoadBalancer(ctx, deleteLoadBalancerRequest) svcErr, ok := common.IsServiceError(err) if ok && (svcErr.GetHTTPStatusCode() == 409 || svcErr.GetHTTPStatusCode() == 404) { lbc.removeFromCache(lbID) @@ -169,7 +170,7 @@ func (lbc *LoadBalancerClient) createRoutingPolicy( } klog.Infof("Creating routing policy with request: %s", util.PrettyPrint(createPolicyRequest)) - resp, err := lbc.lbClient.CreateRoutingPolicy(ctx, createPolicyRequest) + resp, err := lbc.LbClient.CreateRoutingPolicy(ctx, createPolicyRequest) if isServiceError(err, 409) { klog.Infof("Create routing policy operation returned code %d for load balancer %s. Routing policy %s may be already present.", 409, lbID, policyName) @@ -196,7 +197,7 @@ func (lbc *LoadBalancerClient) DeleteRoutingPolicy( } klog.Infof("Delete routing policy with request %s ", util.PrettyPrint(deleteRoutingPolicyRequest)) - resp, err := lbc.lbClient.DeleteRoutingPolicy(ctx, deleteRoutingPolicyRequest) + resp, err := lbc.LbClient.DeleteRoutingPolicy(ctx, deleteRoutingPolicyRequest) if isServiceError(err, 404) { klog.Infof("Delete routing policy operation returned code %d for load balancer %s. Routing policy %s may be already deleted.", 404, lbID, policyName) @@ -232,7 +233,7 @@ func (lbc *LoadBalancerClient) DeleteBackendSet(ctx context.Context, lbID string } klog.Infof("Deleting backend set with request %s", util.PrettyPrint(backendSetDeleteRequest)) - resp, err := lbc.lbClient.DeleteBackendSet(ctx, backendSetDeleteRequest) + resp, err := lbc.LbClient.DeleteBackendSet(ctx, backendSetDeleteRequest) if isServiceError(err, 404) { // it was already deleted so nothing to do. klog.Infof("Delete backend set operation returned code %d for load balancer %s. Backend set %s may be already deleted.", 404, lbID, backendSetName) @@ -268,7 +269,7 @@ func (lbc *LoadBalancerClient) DeleteListener(ctx context.Context, lbID string, } klog.Infof("Deleting listener with request %s", util.PrettyPrint(deleteListenerRequest)) - resp, err := lbc.lbClient.DeleteListener(ctx, deleteListenerRequest) + resp, err := lbc.LbClient.DeleteListener(ctx, deleteListenerRequest) if isServiceError(err, 404) { // it was already deleted so nothing to do. klog.Infof("Delete listener operation returned code %d for load balancer %s. Listener %s may be already deleted.", 404, lbID, listenerName) @@ -314,7 +315,7 @@ func (lbc *LoadBalancerClient) CreateBackendSet( } klog.Infof("Creating backend set with request: %s", util.PrettyPrint(createBackendSetRequest)) - resp, err := lbc.lbClient.CreateBackendSet(ctx, createBackendSetRequest) + resp, err := lbc.LbClient.CreateBackendSet(ctx, createBackendSetRequest) if isServiceError(err, 409) { klog.Infof("Create backend set operation returned code %d for load balancer %s. Backend set %s may be already present.", 409, lbID, backendSetName) @@ -410,7 +411,7 @@ func (lbc *LoadBalancerClient) updateRoutingPolicyRules(ctx context.Context, lbI } klog.Infof("Updating routing policy with request: %s", util.PrettyPrint(updateRoutingPolicyRequest)) - resp, err := lbc.lbClient.UpdateRoutingPolicy(ctx, updateRoutingPolicyRequest) + resp, err := lbc.LbClient.UpdateRoutingPolicy(ctx, updateRoutingPolicyRequest) if err != nil { return err } @@ -503,7 +504,7 @@ func (lbc *LoadBalancerClient) UpdateBackendSet(ctx context.Context, lbID *strin } klog.Infof("Updating backend set with request: %s", util.PrettyPrint(updateBackendSetRequest)) - resp, err := lbc.lbClient.UpdateBackendSet(ctx, updateBackendSetRequest) + resp, err := lbc.LbClient.UpdateBackendSet(ctx, updateBackendSetRequest) if err != nil { return err } @@ -578,7 +579,7 @@ func (lbc *LoadBalancerClient) UpdateListener(ctx context.Context, lbId *string, } klog.Infof("Updating listener with request: %s", util.PrettyPrint(updateListenerRequest)) - resp, err := lbc.lbClient.UpdateListener(ctx, updateListenerRequest) + resp, err := lbc.LbClient.UpdateListener(ctx, updateListenerRequest) if err != nil { return err } @@ -620,7 +621,7 @@ func (lbc *LoadBalancerClient) CreateListener(ctx context.Context, lbID string, } klog.Infof("Creating listener with request %s", util.PrettyPrint(createListenerRequest)) - resp, err := lbc.lbClient.CreateListener(ctx, createListenerRequest) + resp, err := lbc.LbClient.CreateListener(ctx, createListenerRequest) if isServiceError(err, 409) { klog.Infof("Create listener operation returned code %d for load balancer %s. Listener %s may be already present.", 409, lbID, listenerName) @@ -641,7 +642,7 @@ func (lbc *LoadBalancerClient) waitForWorkRequest(ctx context.Context, workReque defer cancel() for { - resp, err := lbc.lbClient.GetWorkRequest(ctx, loadbalancer.GetWorkRequestRequest{ + resp, err := lbc.LbClient.GetWorkRequest(ctx, loadbalancer.GetWorkRequestRequest{ WorkRequestId: common.String(workRequestID), }) if err != nil { diff --git a/pkg/loadbalancer/loadbalancer_test.go b/pkg/loadbalancer/loadbalancer_test.go new file mode 100644 index 00000000..acd1e09f --- /dev/null +++ b/pkg/loadbalancer/loadbalancer_test.go @@ -0,0 +1,339 @@ +package loadbalancer + +import ( + "context" + "errors" + "sync" + "testing" + + . "github.com/onsi/gomega" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-native-ingress-controller/pkg/util" + + ociloadbalancer "github.com/oracle/oci-go-sdk/v65/loadbalancer" + + "github.com/oracle/oci-native-ingress-controller/pkg/oci/client" +) + +func TestLoadBalancerClient_DeleteLoadBalancer(t *testing.T) { + RegisterTestingT(t) + loadBalancerClient := setupLBClient() + + err := loadBalancerClient.DeleteLoadBalancer(context.TODO(), "lbId") + Expect(err).To(BeNil()) +} + +func TestLoadBalancerClient_CreateLoadBalancer(t *testing.T) { + RegisterTestingT(t) + loadBalancerClient := setupLBClient() + id := "id" + request := ociloadbalancer.CreateLoadBalancerRequest{ + OpcRequestId: &id, + } + _, err := loadBalancerClient.CreateLoadBalancer(context.TODO(), request) + Expect(err).To(BeNil()) + id = "error" + request = ociloadbalancer.CreateLoadBalancerRequest{ + OpcRequestId: &id, + } + _, err = loadBalancerClient.CreateLoadBalancer(context.TODO(), request) + Expect(err).To(Not(BeNil())) +} + +func TestLoadBalancerClient_EnsureRoutingPolicy(t *testing.T) { + RegisterTestingT(t) + loadBalancerClient := setupLBClient() + + var rules []ociloadbalancer.RoutingRule + name := "route_80" + condition := "cond" + rule := ociloadbalancer.RoutingRule{ + Name: &name, + Condition: &condition, + Actions: nil, + } + rules = append(rules, rule) + err := loadBalancerClient.EnsureRoutingPolicy(context.TODO(), "id", name, rules) + Expect(err).To(BeNil()) + err = loadBalancerClient.EnsureRoutingPolicy(context.TODO(), "id", "listener", rules) + Expect(err).To(Not(BeNil())) + Expect(err.Error()).Should(Equal("listener listener not found")) +} + +func TestLoadBalancerClient_CreateListener(t *testing.T) { + RegisterTestingT(t) + loadBalancerClient := setupLBClient() + id := "id" + + sslConfigDetail := getSslConfigurationDetails(id) + + err := loadBalancerClient.CreateListener(context.TODO(), "id", 8080, util.ProtocolHTTP, &sslConfigDetail) + Expect(err).To(BeNil()) + err = loadBalancerClient.CreateListener(context.TODO(), "id", 8080, util.ProtocolHTTP2, &sslConfigDetail) + Expect(err).To(BeNil()) + +} + +func getSslConfigurationDetails(id string) ociloadbalancer.SslConfigurationDetails { + var certIds []string + certIds = append(certIds, "secret-cert", "cabundle") + sslConfigDetail := ociloadbalancer.SslConfigurationDetails{ + VerifyDepth: nil, + VerifyPeerCertificate: nil, + TrustedCertificateAuthorityIds: nil, + CertificateIds: certIds, + CertificateName: &id, + Protocols: nil, + CipherSuiteName: nil, + ServerOrderPreference: "", + } + return sslConfigDetail +} + +func TestLoadBalancerClient_UpdateBackends(t *testing.T) { + RegisterTestingT(t) + loadBalancerClient := setupLBClient() + + ip := "127.0.0.29" + port := 8080 + var backendsets []ociloadbalancer.BackendDetails + details := ociloadbalancer.BackendDetails{ + IpAddress: &ip, + Port: &port, + } + backendsets = append(backendsets, details) + + err := loadBalancerClient.UpdateBackends(context.TODO(), "id", "testecho1", backendsets) + Expect(err).To(Not(BeNil())) + Expect(err.Error()).Should(Equal("backendset testecho1 was not found")) + err = loadBalancerClient.UpdateBackends(context.TODO(), "id", "bs_f151df96ee98ff0", backendsets) + Expect(err).To(BeNil()) + +} + +func TestLoadBalancerClient_DeleteBackendSet(t *testing.T) { + RegisterTestingT(t) + loadBalancerClient := setupLBClient() + + err := loadBalancerClient.DeleteBackendSet(context.TODO(), "id", "bs_f151df96ee98ff0") + Expect(err).To(BeNil()) + + err = loadBalancerClient.DeleteBackendSet(context.TODO(), "id", "random") + Expect(err).To(BeNil()) + +} + +func TestLoadBalancerClient_DeleteRoutingPolicy(t *testing.T) { + RegisterTestingT(t) + loadBalancerClient := setupLBClient() + + err := loadBalancerClient.DeleteRoutingPolicy(context.TODO(), "id", "route_80") + Expect(err).To(BeNil()) + err = loadBalancerClient.DeleteRoutingPolicy(context.TODO(), "id", "random") + Expect(err).To(Not(BeNil())) + +} + +func TestLoadBalancerClient_DeleteListener(t *testing.T) { + RegisterTestingT(t) + loadBalancerClient := setupLBClient() + + err := loadBalancerClient.DeleteListener(context.TODO(), "id", "route_80") + Expect(err).To(BeNil()) + err = loadBalancerClient.DeleteListener(context.TODO(), "id", "random") + Expect(err).To(BeNil()) +} + +func TestLoadBalancerClient_CreateBackendSet(t *testing.T) { + RegisterTestingT(t) + loadBalancerClient := setupLBClient() + sslConfig := getSslConfigurationDetails("id") + err := loadBalancerClient.CreateBackendSet(context.TODO(), "id", "bs_f151df96ee98ff0", "", nil, &sslConfig) + Expect(err).To(BeNil()) + err = loadBalancerClient.CreateBackendSet(context.TODO(), "id", "bs_f151df96ee98778", "", nil, &sslConfig) + Expect(err).To(BeNil()) + err = loadBalancerClient.CreateBackendSet(context.TODO(), "id", "error", "", nil, &sslConfig) + Expect(err).To(Not(BeNil())) +} + +func TestLoadBalancerClient_UpdateListener(t *testing.T) { + RegisterTestingT(t) + loadBalancerClient := setupLBClient() + id := "id" + pname := "route_80" + proto := util.ProtocolHTTP + proto2 := util.ProtocolHTTP2 + port := 8080 + listener := ociloadbalancer.Listener{ + Name: &pname, + Port: &port, + Protocol: &proto, + RoutingPolicyName: &pname, + } + ssConfig := getSslConfigurationDetails(id) + err := loadBalancerClient.UpdateListener(context.TODO(), &id, "", listener, &pname, &ssConfig, &proto) + Expect(err).To(BeNil()) + err = loadBalancerClient.UpdateListener(context.TODO(), &id, "", listener, &pname, &ssConfig, &proto2) + Expect(err).To(BeNil()) +} + +func setupLBClient() *LoadBalancerClient { + lbClient := GetLoadBalancerClient() + + loadBalancerClient := &LoadBalancerClient{ + LbClient: lbClient, + Mu: sync.Mutex{}, + Cache: map[string]*LbCacheObj{}, + } + return loadBalancerClient +} + +func GetLoadBalancerClient() client.LoadBalancerInterface { + return &MockLoadBalancerClient{} +} + +type MockLoadBalancerClient struct { +} + +func (m MockLoadBalancerClient) GetLoadBalancer(ctx context.Context, request ociloadbalancer.GetLoadBalancerRequest) (ociloadbalancer.GetLoadBalancerResponse, error) { + res := util.SampleLoadBalancerResponse() + return res, nil +} + +func (m MockLoadBalancerClient) CreateLoadBalancer(ctx context.Context, request ociloadbalancer.CreateLoadBalancerRequest) (ociloadbalancer.CreateLoadBalancerResponse, error) { + id := "id" + var err error + if *request.OpcRequestId == "error" { + err = errors.New("error creating lb") + } + return ociloadbalancer.CreateLoadBalancerResponse{ + RawResponse: nil, + OpcWorkRequestId: &id, + OpcRequestId: &id, + }, err +} + +func (m MockLoadBalancerClient) DeleteLoadBalancer(ctx context.Context, request ociloadbalancer.DeleteLoadBalancerRequest) (ociloadbalancer.DeleteLoadBalancerResponse, error) { + return ociloadbalancer.DeleteLoadBalancerResponse{ + OpcRequestId: common.String("OpcRequestId"), + OpcWorkRequestId: common.String("OpcWorkRequestId"), + }, nil +} + +func (m MockLoadBalancerClient) GetWorkRequest(ctx context.Context, request ociloadbalancer.GetWorkRequestRequest) (ociloadbalancer.GetWorkRequestResponse, error) { + id := "id" + requestId := "opcrequestid" + return ociloadbalancer.GetWorkRequestResponse{ + RawResponse: nil, + WorkRequest: ociloadbalancer.WorkRequest{ + Id: &id, + LoadBalancerId: &id, + Type: nil, + LifecycleState: ociloadbalancer.WorkRequestLifecycleStateSucceeded, + }, + OpcRequestId: &requestId, + }, nil +} + +func (m MockLoadBalancerClient) CreateBackendSet(ctx context.Context, request ociloadbalancer.CreateBackendSetRequest) (ociloadbalancer.CreateBackendSetResponse, error) { + id := "id" + var err error + if *request.Name == "error" { + err = errors.New("backend creation error") + } + return ociloadbalancer.CreateBackendSetResponse{ + RawResponse: nil, + OpcWorkRequestId: &id, + OpcRequestId: &id, + }, err +} + +func (m MockLoadBalancerClient) UpdateBackendSet(ctx context.Context, request ociloadbalancer.UpdateBackendSetRequest) (ociloadbalancer.UpdateBackendSetResponse, error) { + id := "id" + return ociloadbalancer.UpdateBackendSetResponse{ + RawResponse: nil, + OpcWorkRequestId: &id, + OpcRequestId: &id, + }, nil +} + +func (m MockLoadBalancerClient) DeleteBackendSet(ctx context.Context, request ociloadbalancer.DeleteBackendSetRequest) (ociloadbalancer.DeleteBackendSetResponse, error) { + id := "id" + var err error + if *request.BackendSetName == "testecho1" { + err = errors.New("Error backend") + } + + return ociloadbalancer.DeleteBackendSetResponse{ + RawResponse: nil, + OpcWorkRequestId: &id, + OpcRequestId: &id, + }, err +} + +func (m MockLoadBalancerClient) GetBackendSetHealth(ctx context.Context, request ociloadbalancer.GetBackendSetHealthRequest) (ociloadbalancer.GetBackendSetHealthResponse, error) { + //TODO implement me + panic("implement me") +} + +func (m MockLoadBalancerClient) CreateRoutingPolicy(ctx context.Context, request ociloadbalancer.CreateRoutingPolicyRequest) (ociloadbalancer.CreateRoutingPolicyResponse, error) { + id := "id" + return ociloadbalancer.CreateRoutingPolicyResponse{ + RawResponse: nil, + OpcWorkRequestId: &id, + OpcRequestId: &id, + }, nil +} + +func (m MockLoadBalancerClient) UpdateRoutingPolicy(ctx context.Context, request ociloadbalancer.UpdateRoutingPolicyRequest) (ociloadbalancer.UpdateRoutingPolicyResponse, error) { + //TODO implement me + panic("implement me") +} + +func (m MockLoadBalancerClient) DeleteRoutingPolicy(ctx context.Context, request ociloadbalancer.DeleteRoutingPolicyRequest) (ociloadbalancer.DeleteRoutingPolicyResponse, error) { + id := "id" + var err error + if *request.RoutingPolicyName == "random" { + err = errors.New("route not found") + } + return ociloadbalancer.DeleteRoutingPolicyResponse{ + RawResponse: nil, + OpcWorkRequestId: &id, + OpcRequestId: &id, + }, err +} + +func (m MockLoadBalancerClient) CreateListener(ctx context.Context, request ociloadbalancer.CreateListenerRequest) (ociloadbalancer.CreateListenerResponse, error) { + id := "id" + return ociloadbalancer.CreateListenerResponse{ + RawResponse: nil, + OpcWorkRequestId: &id, + OpcRequestId: &id, + }, nil +} + +func (m MockLoadBalancerClient) UpdateListener(ctx context.Context, request ociloadbalancer.UpdateListenerRequest) (ociloadbalancer.UpdateListenerResponse, error) { + id := "id" + var err error + if *request.ListenerName == "error" { + err = errors.New("listener error") + } + return ociloadbalancer.UpdateListenerResponse{ + RawResponse: nil, + OpcWorkRequestId: &id, + OpcRequestId: &id, + }, err +} + +func (m MockLoadBalancerClient) DeleteListener(ctx context.Context, request ociloadbalancer.DeleteListenerRequest) (ociloadbalancer.DeleteListenerResponse, error) { + id := "id" + var err error + if *request.ListenerName == "error" { + err = errors.New("listener error") + } + return ociloadbalancer.DeleteListenerResponse{ + RawResponse: nil, + OpcWorkRequestId: &id, + OpcRequestId: &id, + }, err +} diff --git a/pkg/metric/IngressCollector.go b/pkg/metric/IngressCollector.go index 67b9fda5..97c8832d 100644 --- a/pkg/metric/IngressCollector.go +++ b/pkg/metric/IngressCollector.go @@ -32,6 +32,9 @@ type IngressCollector struct { } func NewIngressCollector(controllerClass string, reg *prometheus.Registry) *IngressCollector { + if reg == nil { + return nil + } constLabels := prometheus.Labels{ "controller_class": controllerClass, } diff --git a/pkg/oci/client/certificate.go b/pkg/oci/client/certificate.go new file mode 100644 index 00000000..170da389 --- /dev/null +++ b/pkg/oci/client/certificate.go @@ -0,0 +1,124 @@ +package client + +import ( + "context" + "net/http" + "time" + + "github.com/oracle/oci-go-sdk/v65/certificates" + "github.com/oracle/oci-go-sdk/v65/certificatesmanagement" +) + +type CertificateInterface interface { + GetCertificateBundle(ctx context.Context, + request certificates.GetCertificateBundleRequest) (certificates.GetCertificateBundleResponse, error) + SetCertCache(cert *certificatesmanagement.Certificate) + GetFromCertCache(certId string) *CertCacheObj + SetCaBundleCache(caBundle *certificatesmanagement.CaBundle) + GetFromCaBundleCache(id string) *CaBundleCacheObj + CreateCertificate(ctx context.Context, + req certificatesmanagement.CreateCertificateRequest) (*certificatesmanagement.Certificate, error) + CreateCaBundle(ctx context.Context, + req certificatesmanagement.CreateCaBundleRequest) (*certificatesmanagement.CaBundle, error) + GetCertificate(ctx context.Context, + req certificatesmanagement.GetCertificateRequest) (*certificatesmanagement.Certificate, error) + ListCertificates(ctx context.Context, + req certificatesmanagement.ListCertificatesRequest) (*certificatesmanagement.CertificateCollection, *string, error) + ScheduleCertificateDeletion(ctx context.Context, + req certificatesmanagement.ScheduleCertificateDeletionRequest) error + GetCaBundle(ctx context.Context, + req certificatesmanagement.GetCaBundleRequest) (*certificatesmanagement.CaBundle, error) + ListCaBundles(ctx context.Context, + req certificatesmanagement.ListCaBundlesRequest) (*certificatesmanagement.CaBundleCollection, error) + DeleteCaBundle(ctx context.Context, + req certificatesmanagement.DeleteCaBundleRequest) (*http.Response, error) +} + +type CertCacheObj struct { + Cert *certificatesmanagement.Certificate + Age time.Time +} + +type CaBundleCacheObj struct { + CaBundle *certificatesmanagement.CaBundle + Age time.Time +} + +type CertificateClient struct { + certificatesClient *certificates.CertificatesClient +} + +func (client CertificateClient) SetCertCache(cert *certificatesmanagement.Certificate) { + client.setCertCache(cert) +} + +func (client CertificateClient) GetFromCertCache(certId string) *CertCacheObj { + return client.getFromCertCache(certId) +} + +func (client CertificateClient) SetCaBundleCache(caBundle *certificatesmanagement.CaBundle) { + client.setCaBundleCache(caBundle) +} + +func (client CertificateClient) GetFromCaBundleCache(id string) *CaBundleCacheObj { + return client.GetFromCaBundleCache(id) +} + +func (client CertificateClient) setCertCache(cert *certificatesmanagement.Certificate) { + client.setCertCache(cert) +} + +func (client CertificateClient) getFromCertCache(certId string) *CertCacheObj { + return client.getFromCertCache(certId) +} + +func (client CertificateClient) setCaBundleCache(caBundle *certificatesmanagement.CaBundle) { + client.setCaBundleCache(caBundle) +} + +func (client CertificateClient) getFromCaBundleCache(id string) *CaBundleCacheObj { + return client.getFromCaBundleCache(id) +} + +func (client CertificateClient) CreateCertificate(ctx context.Context, req certificatesmanagement.CreateCertificateRequest) (*certificatesmanagement.Certificate, error) { + return client.CreateCertificate(ctx, req) +} + +func (client CertificateClient) CreateCaBundle(ctx context.Context, req certificatesmanagement.CreateCaBundleRequest) (*certificatesmanagement.CaBundle, error) { + return client.CreateCaBundle(ctx, req) +} + +func (client CertificateClient) GetCertificate(ctx context.Context, req certificatesmanagement.GetCertificateRequest) (*certificatesmanagement.Certificate, error) { + return client.GetCertificate(ctx, req) +} + +func (client CertificateClient) ListCertificates(ctx context.Context, req certificatesmanagement.ListCertificatesRequest) (*certificatesmanagement.CertificateCollection, *string, error) { + return client.ListCertificates(ctx, req) +} + +func (client CertificateClient) ScheduleCertificateDeletion(ctx context.Context, req certificatesmanagement.ScheduleCertificateDeletionRequest) error { + return client.ScheduleCertificateDeletion(ctx, req) +} + +func (client CertificateClient) GetCaBundle(ctx context.Context, req certificatesmanagement.GetCaBundleRequest) (*certificatesmanagement.CaBundle, error) { + return client.GetCaBundle(ctx, req) +} + +func (client CertificateClient) ListCaBundles(ctx context.Context, req certificatesmanagement.ListCaBundlesRequest) (*certificatesmanagement.CaBundleCollection, error) { + return client.ListCaBundles(ctx, req) +} + +func (client CertificateClient) DeleteCaBundle(ctx context.Context, req certificatesmanagement.DeleteCaBundleRequest) (*http.Response, error) { + return client.DeleteCaBundle(ctx, req) +} + +func NewCertificateClient(certificatesClient *certificates.CertificatesClient) CertificateClient { + return CertificateClient{ + certificatesClient: certificatesClient, + } +} + +func (client CertificateClient) GetCertificateBundle(ctx context.Context, + request certificates.GetCertificateBundleRequest) (certificates.GetCertificateBundleResponse, error) { + return client.certificatesClient.GetCertificateBundle(ctx, request) +} diff --git a/pkg/oci/client/certificatemanagement.go b/pkg/oci/client/certificatemanagement.go new file mode 100644 index 00000000..d3f33964 --- /dev/null +++ b/pkg/oci/client/certificatemanagement.go @@ -0,0 +1,68 @@ +package client + +import ( + "context" + + "github.com/oracle/oci-go-sdk/v65/certificatesmanagement" +) + +type CertificateManagementInterface interface { + CreateCertificate(ctx context.Context, request certificatesmanagement.CreateCertificateRequest) (certificatesmanagement.CreateCertificateResponse, error) + GetCertificate(ctx context.Context, request certificatesmanagement.GetCertificateRequest) (certificatesmanagement.GetCertificateResponse, error) + ListCertificates(ctx context.Context, request certificatesmanagement.ListCertificatesRequest) (certificatesmanagement.ListCertificatesResponse, error) + ScheduleCertificateDeletion(ctx context.Context, request certificatesmanagement.ScheduleCertificateDeletionRequest) (certificatesmanagement.ScheduleCertificateDeletionResponse, error) + CreateCaBundle(ctx context.Context, request certificatesmanagement.CreateCaBundleRequest) (certificatesmanagement.CreateCaBundleResponse, error) + GetCaBundle(ctx context.Context, request certificatesmanagement.GetCaBundleRequest) (certificatesmanagement.GetCaBundleResponse, error) + ListCaBundles(ctx context.Context, request certificatesmanagement.ListCaBundlesRequest) (certificatesmanagement.ListCaBundlesResponse, error) + DeleteCaBundle(ctx context.Context, request certificatesmanagement.DeleteCaBundleRequest) (certificatesmanagement.DeleteCaBundleResponse, error) +} + +type CertificateManagementClient struct { + managementClient *certificatesmanagement.CertificatesManagementClient +} + +func NewCertificateManagementClient(managementClient *certificatesmanagement.CertificatesManagementClient) CertificateManagementClient { + return CertificateManagementClient{ + managementClient: managementClient, + } +} + +func (client CertificateManagementClient) CreateCertificate(ctx context.Context, + request certificatesmanagement.CreateCertificateRequest) (certificatesmanagement.CreateCertificateResponse, error) { + return client.managementClient.CreateCertificate(ctx, request) +} + +func (client CertificateManagementClient) GetCertificate(ctx context.Context, + request certificatesmanagement.GetCertificateRequest) (certificatesmanagement.GetCertificateResponse, error) { + return client.managementClient.GetCertificate(ctx, request) +} + +func (client CertificateManagementClient) ListCertificates(ctx context.Context, + request certificatesmanagement.ListCertificatesRequest) (certificatesmanagement.ListCertificatesResponse, error) { + return client.managementClient.ListCertificates(ctx, request) +} + +func (client CertificateManagementClient) ScheduleCertificateDeletion(ctx context.Context, + request certificatesmanagement.ScheduleCertificateDeletionRequest) (certificatesmanagement.ScheduleCertificateDeletionResponse, error) { + return client.managementClient.ScheduleCertificateDeletion(ctx, request) +} + +func (client CertificateManagementClient) CreateCaBundle(ctx context.Context, + request certificatesmanagement.CreateCaBundleRequest) (certificatesmanagement.CreateCaBundleResponse, error) { + return client.managementClient.CreateCaBundle(ctx, request) +} + +func (client CertificateManagementClient) GetCaBundle(ctx context.Context, + request certificatesmanagement.GetCaBundleRequest) (certificatesmanagement.GetCaBundleResponse, error) { + return client.managementClient.GetCaBundle(ctx, request) +} + +func (client CertificateManagementClient) ListCaBundles(ctx context.Context, + request certificatesmanagement.ListCaBundlesRequest) (certificatesmanagement.ListCaBundlesResponse, error) { + return client.managementClient.ListCaBundles(ctx, request) +} + +func (client CertificateManagementClient) DeleteCaBundle(ctx context.Context, + request certificatesmanagement.DeleteCaBundleRequest) (certificatesmanagement.DeleteCaBundleResponse, error) { + return client.managementClient.DeleteCaBundle(ctx, request) +} diff --git a/pkg/oci/client/loadbalancer.go b/pkg/oci/client/loadbalancer.go new file mode 100644 index 00000000..4b3ae809 --- /dev/null +++ b/pkg/oci/client/loadbalancer.go @@ -0,0 +1,109 @@ +package client + +import ( + "context" + + "github.com/oracle/oci-go-sdk/v65/loadbalancer" +) + +type LoadBalancerInterface interface { + GetLoadBalancer(ctx context.Context, request loadbalancer.GetLoadBalancerRequest) (loadbalancer.GetLoadBalancerResponse, error) + CreateLoadBalancer(ctx context.Context, request loadbalancer.CreateLoadBalancerRequest) (loadbalancer.CreateLoadBalancerResponse, error) + DeleteLoadBalancer(ctx context.Context, request loadbalancer.DeleteLoadBalancerRequest) (loadbalancer.DeleteLoadBalancerResponse, error) + + GetWorkRequest(ctx context.Context, request loadbalancer.GetWorkRequestRequest) (loadbalancer.GetWorkRequestResponse, error) + + CreateBackendSet(ctx context.Context, request loadbalancer.CreateBackendSetRequest) (loadbalancer.CreateBackendSetResponse, error) + UpdateBackendSet(ctx context.Context, request loadbalancer.UpdateBackendSetRequest) (loadbalancer.UpdateBackendSetResponse, error) + DeleteBackendSet(ctx context.Context, request loadbalancer.DeleteBackendSetRequest) (loadbalancer.DeleteBackendSetResponse, error) + + GetBackendSetHealth(ctx context.Context, request loadbalancer.GetBackendSetHealthRequest) (loadbalancer.GetBackendSetHealthResponse, error) + + CreateRoutingPolicy(ctx context.Context, request loadbalancer.CreateRoutingPolicyRequest) (loadbalancer.CreateRoutingPolicyResponse, error) + UpdateRoutingPolicy(ctx context.Context, request loadbalancer.UpdateRoutingPolicyRequest) (loadbalancer.UpdateRoutingPolicyResponse, error) + DeleteRoutingPolicy(ctx context.Context, request loadbalancer.DeleteRoutingPolicyRequest) (loadbalancer.DeleteRoutingPolicyResponse, error) + + CreateListener(ctx context.Context, request loadbalancer.CreateListenerRequest) (loadbalancer.CreateListenerResponse, error) + UpdateListener(ctx context.Context, request loadbalancer.UpdateListenerRequest) (loadbalancer.UpdateListenerResponse, error) + DeleteListener(ctx context.Context, request loadbalancer.DeleteListenerRequest) (loadbalancer.DeleteListenerResponse, error) +} + +type LBClient struct { + lbClient *loadbalancer.LoadBalancerClient +} + +func NewLoadBalancerClient(lbClient *loadbalancer.LoadBalancerClient) LBClient { + return LBClient{ + lbClient: lbClient, + } +} + +func (client LBClient) GetLoadBalancer(ctx context.Context, + request loadbalancer.GetLoadBalancerRequest) (loadbalancer.GetLoadBalancerResponse, error) { + return client.lbClient.GetLoadBalancer(ctx, request) +} + +func (client LBClient) CreateLoadBalancer(ctx context.Context, + request loadbalancer.CreateLoadBalancerRequest) (loadbalancer.CreateLoadBalancerResponse, error) { + return client.lbClient.CreateLoadBalancer(ctx, request) +} + +func (client LBClient) DeleteLoadBalancer(ctx context.Context, + request loadbalancer.DeleteLoadBalancerRequest) (loadbalancer.DeleteLoadBalancerResponse, error) { + return client.lbClient.DeleteLoadBalancer(ctx, request) +} + +func (client LBClient) GetWorkRequest(ctx context.Context, + request loadbalancer.GetWorkRequestRequest) (loadbalancer.GetWorkRequestResponse, error) { + return client.lbClient.GetWorkRequest(ctx, request) +} + +func (client LBClient) CreateBackendSet(ctx context.Context, + request loadbalancer.CreateBackendSetRequest) (loadbalancer.CreateBackendSetResponse, error) { + return client.lbClient.CreateBackendSet(ctx, request) +} + +func (client LBClient) UpdateBackendSet(ctx context.Context, + request loadbalancer.UpdateBackendSetRequest) (loadbalancer.UpdateBackendSetResponse, error) { + return client.lbClient.UpdateBackendSet(ctx, request) +} + +func (client LBClient) DeleteBackendSet(ctx context.Context, + request loadbalancer.DeleteBackendSetRequest) (loadbalancer.DeleteBackendSetResponse, error) { + return client.lbClient.DeleteBackendSet(ctx, request) +} + +func (client LBClient) GetBackendSetHealth(ctx context.Context, + request loadbalancer.GetBackendSetHealthRequest) (loadbalancer.GetBackendSetHealthResponse, error) { + return client.lbClient.GetBackendSetHealth(ctx, request) +} + +func (client LBClient) CreateRoutingPolicy(ctx context.Context, + request loadbalancer.CreateRoutingPolicyRequest) (loadbalancer.CreateRoutingPolicyResponse, error) { + return client.lbClient.CreateRoutingPolicy(ctx, request) +} + +func (client LBClient) UpdateRoutingPolicy(ctx context.Context, + request loadbalancer.UpdateRoutingPolicyRequest) (loadbalancer.UpdateRoutingPolicyResponse, error) { + return client.lbClient.UpdateRoutingPolicy(ctx, request) +} + +func (client LBClient) DeleteRoutingPolicy(ctx context.Context, + request loadbalancer.DeleteRoutingPolicyRequest) (loadbalancer.DeleteRoutingPolicyResponse, error) { + return client.lbClient.DeleteRoutingPolicy(ctx, request) +} + +func (client LBClient) CreateListener(ctx context.Context, + request loadbalancer.CreateListenerRequest) (loadbalancer.CreateListenerResponse, error) { + return client.lbClient.CreateListener(ctx, request) +} + +func (client LBClient) UpdateListener(ctx context.Context, + request loadbalancer.UpdateListenerRequest) (loadbalancer.UpdateListenerResponse, error) { + return client.lbClient.UpdateListener(ctx, request) +} + +func (client LBClient) DeleteListener(ctx context.Context, + request loadbalancer.DeleteListenerRequest) (loadbalancer.DeleteListenerResponse, error) { + return client.lbClient.DeleteListener(ctx, request) +} diff --git a/pkg/podreadiness/webhook_test.go b/pkg/podreadiness/webhook_test.go index 2e9b2c7c..40c42d75 100644 --- a/pkg/podreadiness/webhook_test.go +++ b/pkg/podreadiness/webhook_test.go @@ -175,7 +175,7 @@ func TestHandle(t *testing.T) { Image: "echoserver", }, }, - ReadinessGates: getPodReadinessGates(), + ReadinessGates: util.GetPodReadinessGates("ingress-readiness", "foo.bar.com"), }, }, op: admissionv1.Create, @@ -206,31 +206,6 @@ func TestHandle(t *testing.T) { } } -func getPodReadinessGates() []v1.PodReadinessGate { - var gates []v1.PodReadinessGate - - cond := util.GetPodReadinessCondition("ingress-readiness", "foo.bar.com", getHTTPPath()) - gates = append(gates, corev1.PodReadinessGate{ - ConditionType: cond, - }) - return gates -} - -func getHTTPPath() networkingv1.HTTPIngressPath { - pathType := networkingv1.PathType("Exact") - return networkingv1.HTTPIngressPath{ - Path: "/testecho1", - PathType: &pathType, - Backend: networkingv1.IngressBackend{ - Service: &networkingv1.IngressServiceBackend{ - Name: "testecho1", - Port: networkingv1.ServiceBackendPort{}, - }, - }, - } - -} - func getRawBytes(bytes []byte) runtime.RawExtension { if len(bytes) == 0 { return runtime.RawExtension{} diff --git a/pkg/server/server.go b/pkg/server/server.go index 35f04ef8..5566f948 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -17,6 +17,8 @@ import ( ctrcache "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/webhook" + "k8s.io/client-go/kubernetes" + "github.com/oracle/oci-go-sdk/v65/certificates" "github.com/oracle/oci-go-sdk/v65/certificatesmanagement" ociloadbalancer "github.com/oracle/oci-go-sdk/v65/loadbalancer" @@ -30,6 +32,7 @@ import ( "github.com/oracle/oci-native-ingress-controller/pkg/controllers/routingpolicy" "github.com/oracle/oci-native-ingress-controller/pkg/loadbalancer" "github.com/oracle/oci-native-ingress-controller/pkg/metric" + . "github.com/oracle/oci-native-ingress-controller/pkg/oci/client" "github.com/oracle/oci-native-ingress-controller/pkg/types" "github.com/prometheus/client_golang/prometheus" @@ -59,13 +62,13 @@ func BuildConfig(kubeconfig string) (*rest.Config, error) { } func SetUpControllers(opts types.IngressOpts, ingressClassInformer networkinginformers.IngressClassInformer, - ingressInformer networkinginformers.IngressInformer, client *clientset.Clientset, + ingressInformer networkinginformers.IngressInformer, client kubernetes.Interface, serviceInformer v1.ServiceInformer, endpointInformer v1.EndpointsInformer, podInformer v1.PodInformer, c ctrcache.Cache, reg *prometheus.Registry) func(ctx context.Context) { return func(ctx context.Context) { klog.Info("Controller loop...") - configProvider, err := auth.GetConfigurationProvider(ctx, opts) + configProvider, err := auth.GetConfigurationProvider(ctx, opts, client) if err != nil { klog.Fatalf("failed to load authentication configuration provider: %v", err) } @@ -87,13 +90,14 @@ func SetUpControllers(opts types.IngressOpts, ingressClassInformer networkinginf lbClient := loadbalancer.New(&ociLBClient) - certificatesClient := certificate.New(&ociCertificatesMgmtClient, &ociCertificatesClient) + certificatesClient := certificate.New(&ociCertificatesMgmtClient, NewCertificateClient(&ociCertificatesClient)) ingressController := ingress.NewController( opts.ControllerClass, opts.CompartmentId, ingressClassInformer, ingressInformer, + serviceInformer.Lister(), client, lbClient, certificatesClient, diff --git a/pkg/state/ingressstate_test.go b/pkg/state/ingressstate_test.go index d77165cf..bc1c8a98 100644 --- a/pkg/state/ingressstate_test.go +++ b/pkg/state/ingressstate_test.go @@ -19,14 +19,10 @@ import ( "github.com/oracle/oci-native-ingress-controller/pkg/util" v1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/informers" fakeclientset "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/kubernetes/typed/core/v1/fake" - fake2 "k8s.io/client-go/kubernetes/typed/networking/v1/fake" corelisters "k8s.io/client-go/listers/core/v1" networkinglisters "k8s.io/client-go/listers/networking/v1" - k8stesting "k8s.io/client-go/testing" "k8s.io/client-go/tools/cache" ) @@ -36,24 +32,16 @@ const ( BackendSetPolicyConfigValidationsFilePath = "validate-bs-policy-config.yaml" ListenerProtocolConfigValidationsFilePath = "validate-listener-protocol-config.yaml" TestIngressStateFilePath = "test-ingress-state.yaml" + TestIngressStateWithPortNameFilePath = "test-ingress-state_withportname.yaml" ) -func setUp(ctx context.Context, ingressClassList *networkingv1.IngressClassList, ingressList *networkingv1.IngressList, testService *v1.Service) (networkinglisters.IngressClassLister, networkinglisters.IngressLister, corelisters.ServiceLister) { +func setUp(ctx context.Context, ingressClassList *networkingv1.IngressClassList, ingressList *networkingv1.IngressList, testService *v1.ServiceList) (networkinglisters.IngressClassLister, networkinglisters.IngressLister, corelisters.ServiceLister) { client := fakeclientset.NewSimpleClientset() - client.NetworkingV1().(*fake2.FakeNetworkingV1). - PrependReactor("list", "ingressclasses", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { - return true, ingressClassList, nil - }) - client.NetworkingV1().(*fake2.FakeNetworkingV1). - PrependReactor("list", "ingresses", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { - return true, ingressList, nil - }) - - client.CoreV1().(*fake.FakeCoreV1). - PrependReactor("get", "services", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { - return true, testService, nil - }) + action := "list" + util.UpdateFakeClientCall(client, action, "ingressclasses", ingressClassList) + util.UpdateFakeClientCall(client, action, "ingresses", ingressList) + util.UpdateFakeClientCall(client, action, "services", testService) informerFactory := informers.NewSharedInformerFactory(client, 0) ingressClassInformer := informerFactory.Networking().V1().IngressClasses() @@ -80,7 +68,7 @@ func TestListenerWithDifferentSecrets(t *testing.T) { ingressClassList := util.GetIngressClassList() ingressList := util.ReadResourceAsIngressList(TlsConfigValidationsFilePath) - testService := util.GetServiceResource("default", "tls-test", 943) + testService := util.GetServiceListResource("default", "tls-test", 943) ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) @@ -102,7 +90,7 @@ func TestListenerWithSameSecrets(t *testing.T) { ingressList.Items[0].Spec.TLS[0].SecretName = secretName ingressList.Items[1].Spec.TLS[0].SecretName = secretName - testService := util.GetServiceResource("default", "tls-test", 943) + testService := util.GetServiceListResource("default", "tls-test", 943) ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) @@ -133,7 +121,7 @@ func TestListenerWithSecretAndCertificate(t *testing.T) { ingressList.Items[1].Spec.TLS = []networkingv1.IngressTLS{} ingressList.Items[1].Annotations = map[string]string{util.IngressListenerTlsCertificateAnnotation: "certificateId"} - testService := util.GetServiceResource("default", "tls-test", 943) + testService := util.GetServiceListResource("default", "tls-test", 943) ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) @@ -157,7 +145,7 @@ func TestListenerWithDifferentCertificates(t *testing.T) { ingressList.Items[1].Spec.TLS = []networkingv1.IngressTLS{} ingressList.Items[1].Annotations = map[string]string{util.IngressListenerTlsCertificateAnnotation: "differentCertificateId"} - testService := util.GetServiceResource("default", "tls-test", 943) + testService := util.GetServiceListResource("default", "tls-test", 943) ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) @@ -182,7 +170,7 @@ func TestListenerWithSameCertificate(t *testing.T) { ingressList.Items[1].Spec.TLS = []networkingv1.IngressTLS{} ingressList.Items[1].Annotations = map[string]string{util.IngressListenerTlsCertificateAnnotation: certificateId} - testService := util.GetServiceResource("default", "tls-test", 943) + testService := util.GetServiceListResource("default", "tls-test", 943) ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) @@ -208,13 +196,36 @@ func TestIngressState(t *testing.T) { ingressList := util.ReadResourceAsIngressList(TestIngressStateFilePath) - testService := util.GetServiceResource("default", "tls-test", 943) + testService := util.GetServiceListResource("default", "tls-test", 943) + ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) + + stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) + err := stateStore.BuildState(&ingressClassList.Items[0]) + Expect(err).NotTo(HaveOccurred()) + + assertCases(stateStore) +} + +func TestIngressStateWithPortName(t *testing.T) { + RegisterTestingT(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ingressClassList := util.GetIngressClassList() + + ingressList := util.ReadResourceAsIngressList(TestIngressStateWithPortNameFilePath) + + testService := util.GetServiceListResourceWithPortName("default", "tls-test", 80, "tls-port") ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) err := stateStore.BuildState(&ingressClassList.Items[0]) Expect(err).NotTo(HaveOccurred()) + assertCases(stateStore) +} + +func assertCases(stateStore *StateStore) { ingressName := "ingress-state" allBs := stateStore.GetAllBackendSetForIngressClass() // 4 including default_ingress @@ -255,7 +266,7 @@ func TestValidateHealthCheckerConfig(t *testing.T) { ingressList := util.ReadResourceAsIngressList(HealthCheckerConfigValidationsFilePath) - testService := util.GetServiceResource("default", "test-health-checker-annotation", 800) + testService := util.GetServiceListResource("default", "test-health-checker-annotation", 800) ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) @@ -293,7 +304,7 @@ func TestValidateHealthCheckerConfigWithConflict(t *testing.T) { ingressList.Items[1].Annotations[util.IngressHealthCheckPortAnnotation] = "9090" - testService := util.GetServiceResource("default", "test-health-checker-annotation", 800) + testService := util.GetServiceListResource("default", "test-health-checker-annotation", 800) ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) @@ -312,7 +323,7 @@ func TestValidatePolicyConfig(t *testing.T) { ingressList := util.ReadResourceAsIngressList(BackendSetPolicyConfigValidationsFilePath) - testService := util.GetServiceResource("default", "test-policy-annotation", 900) + testService := util.GetServiceListResource("default", "test-policy-annotation", 900) ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) @@ -337,7 +348,7 @@ func TestValidatePolicyConfigWithConflict(t *testing.T) { ingressList.Items[1].Annotations[util.IngressPolicyAnnotation] = "LEAST_CONNECTIONS" - testService := util.GetServiceResource("default", "test-policy-annotation", 900) + testService := util.GetServiceListResource("default", "test-policy-annotation", 900) ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) @@ -356,7 +367,7 @@ func TestValidateProtocolConfig(t *testing.T) { ingressList := util.ReadResourceAsIngressList(ListenerProtocolConfigValidationsFilePath) - testService := util.GetServiceResource("default", "test-protocol-annotation", 900) + testService := util.GetServiceListResource("default", "test-protocol-annotation", 900) ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) @@ -377,7 +388,7 @@ func TestValidateProtocolConfigWithConflict(t *testing.T) { ingressList.Items[1].Annotations[util.IngressProtocolAnnotation] = "HTTP" - testService := util.GetServiceResource("default", "test-protocol-annotation", 900) + testService := util.GetServiceListResource("default", "test-protocol-annotation", 900) ingressClassLister, ingressLister, serviceLister := setUp(ctx, ingressClassList, ingressList, testService) stateStore := NewStateStore(ingressClassLister, ingressLister, serviceLister, nil) diff --git a/pkg/state/test-ingress-state_withportname.yaml b/pkg/state/test-ingress-state_withportname.yaml new file mode 100644 index 00000000..1987da37 --- /dev/null +++ b/pkg/state/test-ingress-state_withportname.yaml @@ -0,0 +1,78 @@ +# +# OCI Native Ingress Controller +# +# Copyright (c) 2023 Oracle America, Inc. and its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +# +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-state + namespace: default +spec: + tls: + - hosts: + - foo.bar.com + secretName: secret_name + rules: + - host: "foo.bar.com" + http: + paths: + - pathType: Prefix + path: "/PrefixEcho1" + backend: + service: + name: tls-test + port: + name: "tls-port" + - host: "foo.bar.com" + http: + paths: + - pathType: Prefix + path: "/ExactEcho1" + backend: + service: + name: tls-test + port: + number: 70 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-state-new + namespace: default +spec: + tls: + - hosts: + - foo.bar.com + secretName: secret_name + rules: + - host: "foo.bar.com" + http: + paths: + - pathType: Prefix + path: "/PrefixEcho1/aa" + backend: + service: + name: tls-test + port: + name: "tls-port" + - host: "foo.bar.com" + http: + paths: + - pathType: Prefix + path: "/ExactEcho1" + backend: + service: + name: tls-test + port: + number: 90 + - http: + paths: + - pathType: Prefix + path: "/PrefixEcho1" + backend: + service: + name: tls-test + port: + number: 100 diff --git a/pkg/util/testutil.go b/pkg/util/testutil.go index 7230562f..8794bfcf 100644 --- a/pkg/util/testutil.go +++ b/pkg/util/testutil.go @@ -1,15 +1,23 @@ package util import ( + "encoding/base64" "fmt" "log" "os" "strings" + "github.com/oracle/oci-go-sdk/v65/common" + ociloadbalancer "github.com/oracle/oci-go-sdk/v65/loadbalancer" v1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + fakeclientset "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/kubernetes/typed/core/v1/fake" + fake2 "k8s.io/client-go/kubernetes/typed/networking/v1/fake" + k8stesting "k8s.io/client-go/testing" ) func ReadResourceAsIngressList(fileName string) *networkingv1.IngressList { @@ -79,6 +87,28 @@ func GetServiceListResource(namespace string, name string, port int32) *v1.Servi Items: services, } } +func GetServiceListResourceWithPortName(namespace string, name string, port int32, portName string) *v1.ServiceList { + testService := v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: v1.ServiceSpec{ + Selector: map[string]string{"app": name}, + Ports: []v1.ServicePort{{ + Protocol: v1.ProtocolTCP, + Name: portName, + Port: port, + }}, + }, + } + var services []v1.Service + services = append(services, testService) + + return &v1.ServiceList{ + Items: services, + } +} func GetIngressClassList() *networkingv1.IngressClassList { ingressClass := GetIngressClassResource("default-ingress-class", true, "oci.oraclecloud.com/native-ingress-controller") @@ -88,6 +118,22 @@ func GetIngressClassList() *networkingv1.IngressClassList { return ingressClassList } +func GetIngressClassListWithLBSet(lbId string) *networkingv1.IngressClassList { + ingressClass := GetIngressClassResourceWithLbId("default-ingress-class", true, "oci.oraclecloud.com/native-ingress-controller", lbId) + ingressClassList := &networkingv1.IngressClassList{ + Items: []networkingv1.IngressClass{*ingressClass}, + } + return ingressClassList +} + +func GetIngressClassListWithNginx() *networkingv1.IngressClassList { + ingressClass := GetIngressClassResource("nginx-ingress-class", true, "nginx-ingress-controller") + ingressClassList := &networkingv1.IngressClassList{ + Items: []networkingv1.IngressClass{*ingressClass}, + } + return ingressClassList +} + func GetIngressClassResource(name string, isDefault bool, controller string) *networkingv1.IngressClass { return &networkingv1.IngressClass{ ObjectMeta: metav1.ObjectMeta{ @@ -100,6 +146,73 @@ func GetIngressClassResource(name string, isDefault bool, controller string) *ne } } +func GetIngressClassResourceWithLbId(name string, isDefault bool, controller string, lbid string) *networkingv1.IngressClass { + return &networkingv1.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Annotations: map[string]string{"ingressclass.kubernetes.io/is-default-class": fmt.Sprint(isDefault), IngressClassLoadBalancerIdAnnotation: lbid}, + }, + Spec: networkingv1.IngressClassSpec{ + Controller: controller, + }, + } +} + +func GetEndpointsResourceList(name string, namespace string) *v1.EndpointsList { + var emptyNodeName string + endpoint := v1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + ResourceVersion: "1", + }, + Subsets: []v1.EndpointSubset{{ + Addresses: []v1.EndpointAddress{{ + IP: "6.7.8.9", + Hostname: "", + NodeName: &emptyNodeName, + TargetRef: &v1.ObjectReference{ + Kind: "Pod", + Namespace: "default", + Name: "testpod", + UID: "999", + }, + }}, + Ports: []v1.EndpointPort{{Port: 1000}}, + }}, + } + endpoint2 := v1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "host-es", + Namespace: namespace, + ResourceVersion: "1", + }, + Subsets: []v1.EndpointSubset{{ + Addresses: []v1.EndpointAddress{{ + IP: "6.7.8.9", + Hostname: "", + NodeName: &emptyNodeName, + TargetRef: &v1.ObjectReference{ + Kind: "Pod", + Namespace: "default", + Name: "testpod", + UID: "999", + }, + }}, + Ports: []v1.EndpointPort{{Port: 1000}}, + }}, + } + + var endpoints []v1.Endpoints + endpoints = append(endpoints, endpoint) + endpoints = append(endpoints, endpoint2) + + return &v1.EndpointsList{ + Items: endpoints, + } + +} + func GetEndpointsResource(name string, namespace string) *v1.Endpoints { var emptyNodeName string return &v1.Endpoints{ @@ -132,3 +245,236 @@ func GetPodResource(name string, image string) *v1.Pod { } return pod } +func GetPodResourceWithReadiness(name string, image string, ingressName string, host string, condition []v1.PodCondition) *v1.Pod { + pod := &v1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Pod", + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: name, + Image: image, + }, + }, + ReadinessGates: GetPodReadinessGates(ingressName, host), + }, + Status: v1.PodStatus{ + Phase: "", + Conditions: condition, + Message: "", + Reason: "", + }, + } + return pod +} + +func GetPodReadinessGates(name string, host string) []v1.PodReadinessGate { + var gates []v1.PodReadinessGate + + cond := GetPodReadinessCondition(name, host, GetHTTPPath()) + gates = append(gates, v1.PodReadinessGate{ + ConditionType: cond, + }) + return gates +} + +func GetHTTPPath() networkingv1.HTTPIngressPath { + pathType := networkingv1.PathType("Exact") + return networkingv1.HTTPIngressPath{ + Path: "/testecho1", + PathType: &pathType, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "testecho1", + Port: networkingv1.ServiceBackendPort{}, + }, + }, + } + +} + +func GetPodResourceList(name string, image string) *v1.PodList { + pod := v1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: name, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: name, + Image: image, + }, + }, + }, + } + + var pods []v1.Pod + pods = append(pods, pod) + + return &v1.PodList{ + TypeMeta: metav1.TypeMeta{ + Kind: "PodList", + APIVersion: "v1", + }, + Items: pods, + } +} + +func UpdateFakeClientCall(client *fakeclientset.Clientset, action string, resource string, object runtime.Object) { + client.NetworkingV1().(*fake2.FakeNetworkingV1). + PrependReactor(action, resource, func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { + return true, object, nil + }) +} + +func SampleLoadBalancerResponse() ociloadbalancer.GetLoadBalancerResponse { + etag := "testTag" + lbId := "id" + backendSetName := GenerateBackendSetName("default", "testecho1", 80) + name := "testecho1-999" + port := 80 + ip := "127.89.90.90" + backend := ociloadbalancer.Backend{ + Name: &name, + IpAddress: &ip, + Port: &port, + Weight: nil, + Drain: nil, + Backup: nil, + Offline: nil, + } + var backends []ociloadbalancer.Backend + backends = append(backends, backend) + + healthChecker := &ociloadbalancer.HealthChecker{ + Protocol: common.String(ProtocolHTTP), + UrlPath: common.String("/health"), + Port: common.Int(8080), + ReturnCode: common.Int(200), + Retries: common.Int(3), + TimeoutInMillis: common.Int(3000), + IntervalInMillis: common.Int(10000), + ResponseBodyRegex: common.String("*"), + IsForcePlainText: common.Bool(true), + } + policy := "LEAST_CONNECTIONS" + var ipAddresses []ociloadbalancer.IpAddress + ipAddress := ociloadbalancer.IpAddress{ + IpAddress: &ip, + IsPublic: nil, + ReservedIp: nil, + } + ipAddresses = append(ipAddresses, ipAddress) + + var rules []ociloadbalancer.RoutingRule + routeN := "route_80" + cond := "cond" + routeName := routeN + rule := ociloadbalancer.RoutingRule{ + Name: &routeName, + Condition: &cond, + Actions: nil, + } + rules = append(rules, rule) + plcy := ociloadbalancer.RoutingPolicy{ + Name: &routeN, + ConditionLanguageVersion: "", + Rules: rules, + } + policies := map[string]ociloadbalancer.RoutingPolicy{ + routeN: plcy, + } + proto := ProtocolHTTP + listener := ociloadbalancer.Listener{ + Name: &routeN, + DefaultBackendSetName: nil, + Port: &port, + Protocol: &proto, + HostnameNames: nil, + PathRouteSetName: nil, + SslConfiguration: nil, + ConnectionConfiguration: nil, + RuleSetNames: nil, + RoutingPolicyName: &routeN, + } + var res = ociloadbalancer.GetLoadBalancerResponse{ + RawResponse: nil, + LoadBalancer: ociloadbalancer.LoadBalancer{ + Id: &lbId, + IpAddresses: ipAddresses, + Listeners: map[string]ociloadbalancer.Listener{ + routeN: listener, + }, + BackendSets: map[string]ociloadbalancer.BackendSet{ + backendSetName: { + Name: &backendSetName, + Policy: &policy, + Backends: backends, + HealthChecker: healthChecker, + SslConfiguration: nil, + SessionPersistenceConfiguration: nil, + LbCookieSessionPersistenceConfiguration: nil, + }, + }, + PathRouteSets: nil, + FreeformTags: nil, + DefinedTags: nil, + SystemTags: nil, + RuleSets: nil, + RoutingPolicies: policies, + }, + OpcRequestId: nil, + ETag: &etag, + } + return res +} + +func GetSampleSecret(configName string, privateKey string, data string, privateKeyData string) *v1.Secret { + dat, _ := base64.StdEncoding.DecodeString(data) + + namespace := "test" + name := "oci-config" + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Data: map[string][]byte{ + configName: dat, + privateKey: []byte(privateKeyData), + }, + } + return secret +} + +func GetSampleCertSecret() *v1.Secret { + namespace := "test" + name := "oci-cert" + s := "some-random" + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Data: map[string][]byte{ + "ca.crt": []byte(s), + "tls.crt": []byte(s), + "tls.key": []byte(s), + }, + } + return secret +} + +func FakeClientGetCall(client *fakeclientset.Clientset, action string, resource string, obj runtime.Object) { + client.CoreV1().(*fake.FakeCoreV1). + PrependReactor(action, resource, func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { + return true, obj, nil + }) +}