diff --git a/tests/operator/function/create.go b/tests/operator/function/create.go new file mode 100644 index 000000000..8a30278e0 --- /dev/null +++ b/tests/operator/function/create.go @@ -0,0 +1,39 @@ +package function + +import ( + "fmt" + "github.com/kyma-project/serverless/tests/operator/utils" + + serverlessv1alpha2 "github.com/kyma-project/serverless/components/serverless/pkg/apis/serverless/v1alpha2" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func Create(utils *utils.TestUtils) error { + function := getFunction(utils) + if err := utils.Client.Create(utils.Ctx, function); err != nil { + return fmt.Errorf("failed to create function: %w", err) + } + + return nil +} + +func getFunction(utils *utils.TestUtils) *serverlessv1alpha2.Function { + return &serverlessv1alpha2.Function{ + ObjectMeta: metav1.ObjectMeta{ + Name: utils.FunctionName, + Namespace: utils.Namespace, + }, + Spec: serverlessv1alpha2.FunctionSpec{ + Runtime: serverlessv1alpha2.NodeJs20, + Source: serverlessv1alpha2.Source{ + Inline: &serverlessv1alpha2.InlineSource{ + Source: `module.exports = { + main: function(event, context) { + return "Hello World"; + } + }`, + }, + }, + }, + } +} diff --git a/tests/operator/function/delete.go b/tests/operator/function/delete.go new file mode 100644 index 000000000..f09e19b30 --- /dev/null +++ b/tests/operator/function/delete.go @@ -0,0 +1,8 @@ +package function + +import "github.com/kyma-project/serverless/tests/operator/utils" + +func Delete(utils *utils.TestUtils) error { + function := getFunction(utils) + return utils.Client.Delete(utils.Ctx, function) +} diff --git a/tests/operator/main.go b/tests/operator/main.go index 68cbf5fdc..4ff9b1c50 100644 --- a/tests/operator/main.go +++ b/tests/operator/main.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "github.com/kyma-project/serverless/tests/operator/function" "os" "time" @@ -44,6 +45,9 @@ func main() { Logger: log, ServerlessName: "legacy-test", + SecondServerlessName: "default-test-two", + FunctionName: "function-name", + ServerlessConfigMapName: "serverless-configuration", ServerlessCtrlDeployName: "serverless-ctrl-mngr", ServerlessRegistryName: "serverless-docker-registry", ServerlessUpdateSpec: v1alpha1.ServerlessSpec{ @@ -81,6 +85,9 @@ func main() { Logger: log, ServerlessName: "default-test", + SecondServerlessName: "default-test-two", + FunctionName: "function-name", + ServerlessConfigMapName: "serverless-config", ServerlessCtrlDeployName: "serverless-ctrl-mngr", ServerlessConfigName: "serverless-config", ServerlessUpdateSpec: v1alpha1.ServerlessSpec{ @@ -118,31 +125,84 @@ func runScenario(testutil *utils.TestUtils) error { // verify Serverless testutil.Logger.Infof("Verifying serverless '%s'", testutil.ServerlessName) - if err := utils.WithRetry(testutil, serverless.Verify); err != nil { + if err := utils.WithRetry(testutil, serverless.VerifyOld); err != nil { + return err + } + + // verify Serverless config map + testutil.Logger.Infof("Verifying serverless '%s' config map '%s'", testutil.ServerlessName, testutil.ServerlessConfigMapName) + if err := utils.WithRetry(testutil, serverless.VerifyConfig); err != nil { + return err + } + + // create second Serverless + testutil.Logger.Infof("Creating second serverless '%s'", testutil.SecondServerlessName) + if err := serverless.CreateSecond(testutil); err != nil { + return err + } + + // verify second Serverless won't create + testutil.Logger.Infof("Verifying second serverless '%s' won't create", testutil.SecondServerlessName) + if err := utils.WithRetry(testutil, serverless.VerifyStuck); err != nil { + return err + } + + // delete served Serverless + testutil.Logger.Infof("Deleting served serverless '%s'", testutil.ServerlessName) + if err := serverless.DeleteOld(testutil); err != nil { + return err + } + testutil.Logger.Infof("Verifying serverless '%s' deletion", testutil.ServerlessName) + if err := utils.WithRetry(testutil, serverless.VerifyDeletionOld); err != nil { + return err + } + + // verify second Serverless becomes served + testutil.Logger.Infof("Verifying second serverless '%s' becomes served", testutil.SecondServerlessName) + if err := utils.WithRetry(testutil, serverless.VerifyNew); err != nil { + return err + } + + // create function + testutil.Logger.Infof("Creating function in namespace '%s'", testutil.Namespace) + if err := function.Create(testutil); err != nil { return err } // update serverless with other spec - testutil.Logger.Infof("Updating serverless '%s'", testutil.ServerlessName) + testutil.Logger.Infof("Updating serverless '%s'", testutil.SecondServerlessName) if err := serverless.Update(testutil); err != nil { return err } // verify Serverless - testutil.Logger.Infof("Verifying serverless '%s'", testutil.ServerlessName) - if err := utils.WithRetry(testutil, serverless.Verify); err != nil { + testutil.Logger.Infof("Verifying serverless '%s'", testutil.SecondServerlessName) + if err := utils.WithRetry(testutil, serverless.VerifyNew); err != nil { + return err + } + + // verify Severless won't delete with function depending on it + testutil.Logger.Infof("Verifying serverless '%s' deletion is stuck", testutil.SecondServerlessName) + if err := serverless.DeleteNew(testutil); err != nil { + return err + } + if err := utils.WithRetry(testutil, serverless.VerifyDeletionStuck); err != nil { + return err + } + testutil.Logger.Infof("Deleting function '%s'", testutil.FunctionName) + if err := function.Delete(testutil); err != nil { return err } // delete Serverless - testutil.Logger.Infof("Deleting serverless '%s'", testutil.ServerlessName) - if err := serverless.Delete(testutil); err != nil { + testutil.Logger.Infof("Deleting serverless '%s'", testutil.SecondServerlessName) + if err := serverless.DeleteNew(testutil); err != nil { return err } // verify Serverless deletion - testutil.Logger.Infof("Verifying serverless '%s' deletion", testutil.ServerlessName) - if err := utils.WithRetry(testutil, serverless.VerifyDeletion); err != nil { + testutil.Logger.Infof("Verifying serverless '%s' deletion", testutil.SecondServerlessName) + if err := utils.WithRetry(testutil, serverless.VerifyDeletionNew); err != nil { return err } diff --git a/tests/operator/serverless/create.go b/tests/operator/serverless/create.go index 48805c179..e9681ef38 100644 --- a/tests/operator/serverless/create.go +++ b/tests/operator/serverless/create.go @@ -7,12 +7,18 @@ import ( ) func Create(utils *utils.TestUtils) error { - serverlessObj := fixServerless(utils) + serverlessObj := fixServerless(utils, utils.ServerlessName) return utils.Client.Create(utils.Ctx, serverlessObj) } -func fixServerless(testUtils *utils.TestUtils) *v1alpha1.Serverless { +func CreateSecond(utils *utils.TestUtils) error { + serverlessObj := fixServerless(utils, utils.SecondServerlessName) + + return utils.Client.Create(utils.Ctx, serverlessObj) +} + +func fixServerless(testUtils *utils.TestUtils, name string) *v1alpha1.Serverless { annotations := map[string]string{} if testUtils.LegacyMode { annotations["serverless.kyma-project.io/buildless-mode"] = "disabled" @@ -20,7 +26,7 @@ func fixServerless(testUtils *utils.TestUtils) *v1alpha1.Serverless { return &v1alpha1.Serverless{ ObjectMeta: v1.ObjectMeta{ - Name: testUtils.ServerlessName, + Name: name, Namespace: testUtils.Namespace, Annotations: annotations, }, diff --git a/tests/operator/serverless/delete.go b/tests/operator/serverless/delete.go index d4cc1c6a7..47e2ff525 100644 --- a/tests/operator/serverless/delete.go +++ b/tests/operator/serverless/delete.go @@ -2,8 +2,13 @@ package serverless import "github.com/kyma-project/serverless/tests/operator/utils" -func Delete(utils *utils.TestUtils) error { - serverless := fixServerless(utils) +func DeleteOld(utils *utils.TestUtils) error { + serverless := fixServerless(utils, utils.ServerlessName) + + return utils.Client.Delete(utils.Ctx, serverless) +} +func DeleteNew(utils *utils.TestUtils) error { + serverless := fixServerless(utils, utils.SecondServerlessName) return utils.Client.Delete(utils.Ctx, serverless) } diff --git a/tests/operator/serverless/update.go b/tests/operator/serverless/update.go index a502e51e8..c76d6898a 100644 --- a/tests/operator/serverless/update.go +++ b/tests/operator/serverless/update.go @@ -9,7 +9,7 @@ import ( func Update(testutils *utils.TestUtils) error { var serverless v1alpha1.Serverless objectKey := client.ObjectKey{ - Name: testutils.ServerlessName, + Name: testutils.SecondServerlessName, Namespace: testutils.Namespace, } diff --git a/tests/operator/serverless/verify.go b/tests/operator/serverless/verify.go index 751035544..9b8e926ff 100644 --- a/tests/operator/serverless/verify.go +++ b/tests/operator/serverless/verify.go @@ -2,6 +2,8 @@ package serverless import ( "fmt" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/kyma-project/serverless/components/operator/api/v1alpha1" "github.com/kyma-project/serverless/tests/operator/serverless/configmap" @@ -11,8 +13,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func VerifyDeletion(utils *utils.TestUtils) error { - err := Verify(utils) +func VerifyDeletionOld(utils *utils.TestUtils) error { + err := VerifyOld(utils) if err == nil { return fmt.Errorf("serverless '%s' still exists", utils.ServerlessName) } @@ -23,14 +25,42 @@ func VerifyDeletion(utils *utils.TestUtils) error { return nil } -func Verify(utils *utils.TestUtils) error { - var serverless v1alpha1.Serverless - objectKey := client.ObjectKey{ - Name: utils.ServerlessName, - Namespace: utils.Namespace, +func VerifyDeletionNew(utils *utils.TestUtils) error { + err := VerifyOld(utils) + if err == nil { + return fmt.Errorf("serverless '%s' still exists", utils.SecondServerlessName) + } + if !errors.IsNotFound(err) { + return err } - if err := utils.Client.Get(utils.Ctx, objectKey, &serverless); err != nil { + return nil +} + +func VerifyOld(utils *utils.TestUtils) error { + serverless, err := getServerless(utils, utils.ServerlessName) + if err != nil { + return err + } + + if err := verifyState(utils, &serverless); err != nil { + return err + } + + if err := verifyStatus(&serverless, utils.LegacyMode); err != nil { + return err + } + + if utils.LegacyMode { + return deployment.VerifyCtrlMngrEnvs(utils, &serverless) + } + + return configmap.VerifyServerlessConfigmap(utils, &serverless) +} + +func VerifyNew(utils *utils.TestUtils) error { + serverless, err := getServerless(utils, utils.SecondServerlessName) + if err != nil { return err } @@ -49,6 +79,52 @@ func Verify(utils *utils.TestUtils) error { return configmap.VerifyServerlessConfigmap(utils, &serverless) } +func VerifyConfig(utils *utils.TestUtils) error { + configMap := &corev1.ConfigMap{} + objectKey := client.ObjectKey{ + Name: utils.ServerlessConfigMapName, + Namespace: utils.Namespace, + } + err := utils.Client.Get(utils.Ctx, objectKey, configMap) + return err +} + +func VerifyStuck(utils *utils.TestUtils) error { + serverless, err := getServerless(utils, utils.SecondServerlessName) + if err != nil { + return err + } + + if err := verifyStateStuck(utils, &serverless); err != nil { + return err + } + + return nil +} + +func VerifyDeletionStuck(utils *utils.TestUtils) error { + serverless, err := getServerless(utils, utils.SecondServerlessName) + if err != nil { + return err + } + + return verifyDeletionStuck(&serverless) +} + +func getServerless(utils *utils.TestUtils, name string) (v1alpha1.Serverless, error) { + var serverless v1alpha1.Serverless + objectKey := client.ObjectKey{ + Name: name, + Namespace: utils.Namespace, + } + + if err := utils.Client.Get(utils.Ctx, objectKey, &serverless); err != nil { + return v1alpha1.Serverless{}, err + } + + return serverless, nil +} + // check if all data from the spec is reflected in the status func verifyStatus(serverless *v1alpha1.Serverless, legacy bool) error { status := serverless.Status @@ -142,6 +218,33 @@ func verifyState(utils *utils.TestUtils, serverless *v1alpha1.Serverless) error if serverless.Status.State != v1alpha1.StateReady { return fmt.Errorf("serverless '%s' in '%s' state", utils.ServerlessName, serverless.Status.State) } - return nil } + +func verifyStateStuck(utils *utils.TestUtils, serverless *v1alpha1.Serverless) error { + for _, condition := range serverless.Status.Conditions { + if condition.Type == string(v1alpha1.ConditionTypeConfigured) { + if condition.Reason == string(v1alpha1.ConditionReasonServerlessDuplicated) && + condition.Status == metav1.ConditionFalse && + condition.Message == fmt.Sprintf("only one instance of Serverless is allowed (current served instance: %s/%s) - this Serverless CR is redundant - remove it to fix the problem", utils.Namespace, utils.ServerlessName) { + return nil + } + return fmt.Errorf("ConditionConfigured is not in expected state: %v", condition) + } + } + return fmt.Errorf("ConditionConfigured not found") +} + +func verifyDeletionStuck(serverless *v1alpha1.Serverless) error { + for _, condition := range serverless.Status.Conditions { + if condition.Type == string(v1alpha1.ConditionTypeDeleted) { + if condition.Reason == string(v1alpha1.ConditionReasonDeletionErr) && + condition.Status == metav1.ConditionFalse && + condition.Message == "found 1 items with VersionKind serverless.kyma-project.io/v1alpha2" { + return nil + } + return fmt.Errorf("ConditionDeleted is not in expected state: %v", condition) + } + } + return fmt.Errorf("ConditionDeleted not found") +} diff --git a/tests/operator/utils/client.go b/tests/operator/utils/client.go index 5406df785..126349bda 100644 --- a/tests/operator/utils/client.go +++ b/tests/operator/utils/client.go @@ -7,6 +7,7 @@ import ( "path/filepath" serverlessv1alpha1 "github.com/kyma-project/serverless/components/operator/api/v1alpha1" + serverlessv1alpha2 "github.com/kyma-project/serverless/components/serverless/pkg/apis/serverless/v1alpha2" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -25,6 +26,11 @@ func GetKuberentesClient() (client.Client, error) { return nil, err } + err = serverlessv1alpha2.AddToScheme(scheme.Scheme) + if err != nil { + return nil, err + } + return client.New(config, client.Options{Scheme: scheme.Scheme}) } diff --git a/tests/operator/utils/types.go b/tests/operator/utils/types.go index c12b42846..b76508b3e 100644 --- a/tests/operator/utils/types.go +++ b/tests/operator/utils/types.go @@ -15,9 +15,12 @@ type TestUtils struct { Namespace string ServerlessName string + SecondServerlessName string + FunctionName string ServerlessCtrlDeployName string ServerlessConfigName string ServerlessRegistryName string + ServerlessConfigMapName string ServerlessUpdateSpec v1alpha1.ServerlessSpec LegacyMode bool }