diff --git a/components/buildless-serverless/Makefile b/components/buildless-serverless/Makefile index 11b1f2734..14c92e02a 100644 --- a/components/buildless-serverless/Makefile +++ b/components/buildless-serverless/Makefile @@ -139,18 +139,18 @@ docker-buildx: ## Build and push docker image for the manager for cross-platform rm Dockerfile.cross build-image: ## Build buildless serverless backend image - docker build -t $(APP_NAME) -f Dockerfile . + docker build -t $(APP_NAME) -f Dockerfile ../.. ######## disable operator to prevent undo of local image update to k3d disable-operator: - $(KUBECTL) scale deployment serverless-operator -n kyma-system --replicas=0 + $(KUBECTL) scale deployment serverless-operator -n kyma-system --replicas=0 || true .PHONY: install-buildless -install-buildless: build-image disable-operator## Build buildless serverless image and install it on local k3d cluster +install-buildless: build-image disable-operator ## Build buildless serverless image and install it on local k3d cluster $(eval HASH_TAG=$(shell docker images $(APP_NAME):latest --quiet)) docker tag $(APP_NAME) $(APP_NAME):$(HASH_TAG) - k3d image import $(APP_NAME):$(HASH_TAG) -c kyma + k3d image import $(APP_NAME):$(HASH_TAG) $(KUBECTL) set image deployment -n kyma-system serverless-ctrl-mngr manager=$(APP_NAME):$(HASH_TAG) ##@ Deployment diff --git a/components/buildless-serverless/internal/controller/resources/deployment.go b/components/buildless-serverless/internal/controller/resources/deployment.go index 3c193a621..e0fa17a1f 100644 --- a/components/buildless-serverless/internal/controller/resources/deployment.go +++ b/components/buildless-serverless/internal/controller/resources/deployment.go @@ -93,7 +93,7 @@ type Deployment struct { podCmd []string } -func NewDeployment(f *serverlessv1alpha2.Function, c *config.FunctionConfig, clusterDeployment *appsv1.Deployment, commit string, gitAuth *git.GitAuth, opts ...deployOptions) *Deployment { +func NewDeployment(f *serverlessv1alpha2.Function, c *config.FunctionConfig, clusterDeployment *appsv1.Deployment, commit string, gitAuth *git.GitAuth, appName string, opts ...deployOptions) *Deployment { d := &Deployment{ functionConfig: c, function: f, @@ -114,6 +114,13 @@ func NewDeployment(f *serverlessv1alpha2.Function, c *config.FunctionConfig, clu }, } + if appName != "" { + if d.podLabels == nil { + d.podLabels = make(map[string]string) + } + d.podLabels["app.kubernetes.io/name"] = appName + } + for _, o := range opts { o(d) } diff --git a/components/buildless-serverless/internal/controller/resources/deployment_test.go b/components/buildless-serverless/internal/controller/resources/deployment_test.go index 7f08e7656..038d5e569 100644 --- a/components/buildless-serverless/internal/controller/resources/deployment_test.go +++ b/components/buildless-serverless/internal/controller/resources/deployment_test.go @@ -19,7 +19,7 @@ func TestNewDeployment(t *testing.T) { f := minimalFunction() c := minimalFunctionConfig() - r := NewDeployment(f, c, nil, "test-commit", nil) + r := NewDeployment(f, c, nil, "test-commit", nil, "") require.NotNil(t, r) d := r.Deployment @@ -85,7 +85,7 @@ func TestDeployment_construct(t *testing.T) { "shtern": "stoic", "boyd": "vigilant", } - d := NewDeployment(f, minimalFunctionConfig(), nil, "", nil) + d := NewDeployment(f, minimalFunctionConfig(), nil, "", nil, "") r := d.construct() @@ -167,7 +167,7 @@ func TestDeployment_construct(t *testing.T) { t.Run("use container image based on function and function configuration", func(t *testing.T) { d := NewDeployment(minimalFunction(), &config.FunctionConfig{ Images: config.ImagesConfig{Python312: "special-test-image"}, - }, nil, "", nil) + }, nil, "", nil, "") r := d.construct() @@ -1218,7 +1218,7 @@ func TestDeployment_envs(t *testing.T) { d := NewDeployment(tt.function, &config.FunctionConfig{ FunctionPublisherProxyAddress: "test-proxy-address", FunctionTraceCollectorEndpoint: "test-trace-collector-endpoint", - }, nil, "", nil) + }, nil, "", nil, "") assert.ElementsMatch(t, tt.want, d.podEnvs) }) @@ -1445,7 +1445,7 @@ func minimalFunctionConfig() *config.FunctionConfig { } func minimalDeploymentForFunction(f *serverlessv1alpha2.Function) *Deployment { - return NewDeployment(f, minimalFunctionConfig(), nil, "", nil) + return NewDeployment(f, minimalFunctionConfig(), nil, "", nil, "") } func minimalDeployment() *Deployment { diff --git a/components/buildless-serverless/internal/controller/state/adjust_status_test.go b/components/buildless-serverless/internal/controller/state/adjust_status_test.go index 467da13ae..82db9d018 100644 --- a/components/buildless-serverless/internal/controller/state/adjust_status_test.go +++ b/components/buildless-serverless/internal/controller/state/adjust_status_test.go @@ -2,6 +2,8 @@ package state import ( "context" + "testing" + serverlessv1alpha2 "github.com/kyma-project/serverless/components/buildless-serverless/api/v1alpha2" "github.com/kyma-project/serverless/components/buildless-serverless/internal/config" "github.com/kyma-project/serverless/components/buildless-serverless/internal/controller/fsm" @@ -12,7 +14,6 @@ import ( k8sresource "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" - "testing" ) func Test_sFnAdjustStatus(t *testing.T) { @@ -40,7 +41,7 @@ func Test_sFnAdjustStatus(t *testing.T) { m := fsm.StateMachine{ State: fsm.SystemState{ Function: f, - BuiltDeployment: resources.NewDeployment(&f, &fc, nil, "test-commit", nil), + BuiltDeployment: resources.NewDeployment(&f, &fc, nil, "test-commit", nil, ""), ClusterDeployment: &appsv1.Deployment{ Status: appsv1.DeploymentStatus{ Replicas: int32(686)}}}, @@ -105,7 +106,7 @@ func Test_sFnAdjustStatus(t *testing.T) { State: fsm.SystemState{ Function: f, Commit: "test-commit", - BuiltDeployment: resources.NewDeployment(&f, &fc, nil, "test-commit", nil), + BuiltDeployment: resources.NewDeployment(&f, &fc, nil, "test-commit", nil, ""), ClusterDeployment: &appsv1.Deployment{ Status: appsv1.DeploymentStatus{ Replicas: int32(686)}}}, @@ -180,7 +181,7 @@ func Test_sFnAdjustStatus(t *testing.T) { m := fsm.StateMachine{ State: fsm.SystemState{ Function: f, - BuiltDeployment: resources.NewDeployment(&f, &fc, nil, "test-commit", nil), + BuiltDeployment: resources.NewDeployment(&f, &fc, nil, "test-commit", nil, ""), ClusterDeployment: &appsv1.Deployment{ Status: appsv1.DeploymentStatus{ Replicas: int32(686)}}}, @@ -229,7 +230,7 @@ func Test_sFnAdjustStatus(t *testing.T) { m := fsm.StateMachine{ State: fsm.SystemState{ Function: f, - BuiltDeployment: resources.NewDeployment(&f, &fc, nil, "test-commit", nil), + BuiltDeployment: resources.NewDeployment(&f, &fc, nil, "test-commit", nil, ""), ClusterDeployment: &appsv1.Deployment{ Status: appsv1.DeploymentStatus{ Replicas: int32(686)}}}, diff --git a/components/buildless-serverless/internal/controller/state/handle_deployment.go b/components/buildless-serverless/internal/controller/state/handle_deployment.go index 0cb76e660..70491d328 100644 --- a/components/buildless-serverless/internal/controller/state/handle_deployment.go +++ b/components/buildless-serverless/internal/controller/state/handle_deployment.go @@ -3,17 +3,18 @@ package state import ( "context" "fmt" + "reflect" + "time" + serverlessv1alpha2 "github.com/kyma-project/serverless/components/buildless-serverless/api/v1alpha2" "github.com/kyma-project/serverless/components/buildless-serverless/internal/controller/fsm" "github.com/kyma-project/serverless/components/buildless-serverless/internal/controller/resources" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "reflect" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "time" ) func sFnHandleDeployment(ctx context.Context, m *fsm.StateMachine) (fsm.StateFn, *ctrl.Result, error) { @@ -33,7 +34,7 @@ func sFnHandleDeployment(ctx context.Context, m *fsm.StateMachine) (fsm.StateFn, } m.State.ClusterDeployment = clusterDeployment - m.State.BuiltDeployment = resources.NewDeployment(&m.State.Function, &m.FunctionConfig, clusterDeployment, m.State.Commit, m.State.GitAuth) + m.State.BuiltDeployment = resources.NewDeployment(&m.State.Function, &m.FunctionConfig, clusterDeployment, m.State.Commit, m.State.GitAuth, "") builtDeployment := m.State.BuiltDeployment.Deployment if m.State.ClusterDeployment == nil { diff --git a/components/buildless-serverless/internal/controller/state/handle_deployment_test.go b/components/buildless-serverless/internal/controller/state/handle_deployment_test.go index 6e25c41da..71e764312 100644 --- a/components/buildless-serverless/internal/controller/state/handle_deployment_test.go +++ b/components/buildless-serverless/internal/controller/state/handle_deployment_test.go @@ -3,6 +3,9 @@ package state import ( "context" "errors" + "testing" + "time" + serverlessv1alpha2 "github.com/kyma-project/serverless/components/buildless-serverless/api/v1alpha2" "github.com/kyma-project/serverless/components/buildless-serverless/internal/config" "github.com/kyma-project/serverless/components/buildless-serverless/internal/controller/fsm" @@ -19,8 +22,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/client/interceptor" - "testing" - "time" ) func Test_sFnHandleDeployment(t *testing.T) { @@ -207,7 +208,7 @@ func Test_sFnHandleDeployment(t *testing.T) { } cd := appsv1.Deployment{} // identical deployment will be generated inside sFnHandleDeployment - deployment := resources.NewDeployment(&f, &fc, &cd, "test-commit", nil).Deployment + deployment := resources.NewDeployment(&f, &fc, &cd, "test-commit", nil, "").Deployment // scheme and fake client scheme := runtime.NewScheme() require.NoError(t, serverlessv1alpha2.AddToScheme(scheme)) diff --git a/components/buildless-serverless/internal/endpoint/function.go b/components/buildless-serverless/internal/endpoint/function.go index 7e18ae6fc..76cd01453 100644 --- a/components/buildless-serverless/internal/endpoint/function.go +++ b/components/buildless-serverless/internal/endpoint/function.go @@ -12,6 +12,7 @@ import ( func (s *Server) handleFunctionRequest(w http.ResponseWriter, r *http.Request) { ns := r.URL.Query().Get("namespace") name := r.URL.Query().Get("name") + appName := r.URL.Query().Get("targetAppName") s.log.Infof("handling function request for function '%s/%s'", ns, name) @@ -27,7 +28,7 @@ func (s *Server) handleFunctionRequest(w http.ResponseWriter, r *http.Request) { return } - resourceFiles, err := runtime.BuildResources(&s.functionConfig, &function) + resourceFiles, err := runtime.BuildResources(&s.functionConfig, &function, appName) if err != nil { s.writeErrorResponse(w, http.StatusInternalServerError, errors.Wrapf(err, "failed to get resource files for function '%s/%s'", ns, name)) return diff --git a/components/buildless-serverless/internal/endpoint/runtime/resources.go b/components/buildless-serverless/internal/endpoint/runtime/resources.go index df03e08a7..7e9df8b1d 100644 --- a/components/buildless-serverless/internal/endpoint/runtime/resources.go +++ b/components/buildless-serverless/internal/endpoint/runtime/resources.go @@ -13,25 +13,29 @@ import ( "go.yaml.in/yaml/v2" ) -func BuildResources(functionConfig *config.FunctionConfig, f *v1alpha2.Function) ([]types.FileResponse, error) { - svc, err := buildServiceFileData(f) +func BuildResources(functionConfig *config.FunctionConfig, f *v1alpha2.Function, appName string) ([]types.FileResponse, error) { + svc, err := buildServiceFileData(f, appName) if err != nil { return nil, errors.Wrapf(err, "failed to build service") } - deployment, err := buildDeploymentFileData(functionConfig, f) + deployment, err := buildDeploymentFileData(functionConfig, f, appName) if err != nil { return nil, errors.Wrapf(err, "failed to build deployment") } return []types.FileResponse{ - {Name: "resources/service.yaml", Data: base64.StdEncoding.EncodeToString(svc)}, - {Name: "resources/deployment.yaml", Data: base64.StdEncoding.EncodeToString(deployment)}, + {Name: "k8s/service.yaml", Data: base64.StdEncoding.EncodeToString(svc)}, + {Name: "k8s/deployment.yaml", Data: base64.StdEncoding.EncodeToString(deployment)}, }, nil } -func buildServiceFileData(function *v1alpha2.Function) ([]byte, error) { - svcName := fmt.Sprintf("%s-ejected", function.Name) +func buildServiceFileData(function *v1alpha2.Function, appName string) ([]byte, error) { + svcName := appName + if svcName == "" { + svcName = fmt.Sprintf("%s-ejected", function.Name) + } + svc := resources.NewService( function, resources.ServiceName(svcName), @@ -49,19 +53,24 @@ func buildServiceFileData(function *v1alpha2.Function) ([]byte, error) { return data, nil } -func buildDeploymentFileData(functionConfig *config.FunctionConfig, function *v1alpha2.Function) ([]byte, error) { +func buildDeploymentFileData(functionConfig *config.FunctionConfig, function *v1alpha2.Function, appName string) ([]byte, error) { if function.HasGitSources() { // TODO: support git source return nil, errors.New("ejecting functions with git source is not supported") } - deployName := fmt.Sprintf("%s-ejected", function.Name) + deployName := appName + if deployName == "" { + deployName = fmt.Sprintf("%s-ejected", function.Name) + } + deploy := resources.NewDeployment( function, functionConfig, nil, "", nil, + appName, resources.DeploySetName(deployName), resources.DeployTrimClusterInfoLabels(), resources.DeployAppendSelectorLabels(map[string]string{ diff --git a/components/buildless-serverless/internal/endpoint/runtime/resources_test.go b/components/buildless-serverless/internal/endpoint/runtime/resources_test.go index aa3f90081..fae25a39c 100644 --- a/components/buildless-serverless/internal/endpoint/runtime/resources_test.go +++ b/components/buildless-serverless/internal/endpoint/runtime/resources_test.go @@ -2,6 +2,7 @@ package runtime import ( "encoding/base64" + "fmt" "testing" "github.com/kyma-project/serverless/components/buildless-serverless/api/v1alpha2" @@ -26,14 +27,14 @@ func TestBuildResources(t *testing.T) { Name: "test-function", Namespace: "test-namespace", }, - }) + }, "") require.NoError(t, err) require.Len(t, files, 2) - require.Equal(t, "resources/service.yaml", files[0].Name) - requireEqualBase64Objects(t, fixTestService(), files[0].Data) - require.Equal(t, "resources/deployment.yaml", files[1].Name) - requireEqualBase64Objects(t, fixDeployment(), files[1].Data) + require.Equal(t, "k8s/service.yaml", files[0].Name) + requireEqualBase64Objects(t, fixTestService("test-function-ejected"), files[0].Data) + require.Equal(t, "k8s/deployment.yaml", files[1].Name) + requireEqualBase64Objects(t, fixDeployment("test-function-ejected", "test-function"), files[1].Data) }) t.Run("error on git source", func(t *testing.T) { @@ -48,19 +49,45 @@ func TestBuildResources(t *testing.T) { Name: "test-function", Namespace: "test-namespace", }, - }) + }, "") require.ErrorContains(t, err, "ejecting functions with git source is not supported") require.Nil(t, files) }) + + t.Run("build resources for function with specified app name", func(t *testing.T) { + files, err := BuildResources(&config.FunctionConfig{}, &v1alpha2.Function{ + Spec: v1alpha2.FunctionSpec{ + Runtime: "nodejs22", + Source: v1alpha2.Source{ + Inline: &v1alpha2.InlineSource{ + Source: "console.log('Hello World')", + Dependencies: "{}", + }, + }, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-function", + Namespace: "test-namespace", + }, + }, "test-app-name") + + require.NoError(t, err) + require.Len(t, files, 2) + fmt.Println(files) + require.Equal(t, "k8s/service.yaml", files[0].Name) + requireEqualBase64Objects(t, fixTestService("test-app-name"), files[0].Data) + require.Equal(t, "k8s/deployment.yaml", files[1].Name) + requireEqualBase64Objects(t, fixDeployment("test-app-name", "test-app-name"), files[1].Data) + }) } -func fixTestService() string { - return `apiVersion: v1 +func fixTestService(appName string) string { + return fmt.Sprintf(`apiVersion: v1 kind: Service metadata: creationTimestamp: null - name: test-function-ejected + name: %s namespace: test-namespace spec: ports: @@ -69,25 +96,25 @@ spec: protocol: TCP targetPort: 8080 selector: - app.kubernetes.io/instance: test-function-ejected + app.kubernetes.io/instance: %s serverless.kyma-project.io/resource: deployment status: loadBalancer: {} -` +`, appName, appName) } -func fixDeployment() string { - return `apiVersion: apps/v1 +func fixDeployment(appName, functionName string) string { + return fmt.Sprintf(`apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null - name: test-function-ejected + name: %s namespace: test-namespace spec: replicas: 1 selector: matchLabels: - app.kubernetes.io/instance: test-function-ejected + app.kubernetes.io/instance: %s serverless.kyma-project.io/resource: deployment strategy: {} template: @@ -97,8 +124,8 @@ spec: sidecar.istio.io/nativeSidecar: "true" creationTimestamp: null labels: - app.kubernetes.io/instance: test-function-ejected - app.kubernetes.io/name: test-function + app.kubernetes.io/instance: %s + app.kubernetes.io/name: %s serverless.kyma-project.io/resource: deployment spec: containers: @@ -170,7 +197,7 @@ spec: - emptyDir: {} name: tmp status: {} -` +`, appName, appName, appName, functionName) } func requireEqualBase64Objects(t *testing.T, expectedObj, actual string) { diff --git a/config/buildless-serverless/files/kyma-commands.yaml b/config/buildless-serverless/files/kyma-commands.yaml index d446845f7..da9441477 100644 --- a/config/buildless-serverless/files/kyma-commands.yaml +++ b/config/buildless-serverless/files/kyma-commands.yaml @@ -64,12 +64,17 @@ subCommands: description: "Directory where the files will be saved" shorthand: "d" default: "." + - type: string + name: "target-app-name" + description: "App's name" + default: "" with: outputDir: ${{ .flags.dir.value }} request: parameters: name: ${{ .args.value }} namespace: ${{ .flags.namespace.value }} + targetAppName: ${{ .flags.targetappname.value}} targetPod: path: "/internal/function/eject/" port: "12137"