diff --git a/Dockerfile.utils b/Dockerfile.utils index 0ccad03c90..f1ccdd2178 100644 --- a/Dockerfile.utils +++ b/Dockerfile.utils @@ -4,19 +4,26 @@ ARG BUILDPLATFORM ARG TARGETPLATFORM ARG TARGETARCH +WORKDIR /workspace + +COPY go.mod go.sum ./ +RUN go mod download -x + COPY . . -RUN GOARCH=$TARGETARCH go build -o deploy -trimpath -ldflags '-w -s' ./cmd/func-deployer +RUN GOARCH=$TARGETARCH go build -o func-util -trimpath -ldflags '-w -s' ./cmd/func-util ######################### FROM --platform=$TARGETPLATFORM index.docker.io/library/alpine:latest RUN apk add --no-cache socat tar \ - && addgroup func -g 1000 \ - && adduser func -u 1001 -D -G func + && addgroup func -g 65532 \ + && adduser func -u 65532 -D -G func -COPY --from=builder /go/deploy /usr/local/bin/ +COPY --from=builder /workspace/func-util /usr/local/bin/ +RUN ln -s /usr/local/bin/func-util /usr/local/bin/deploy && \ + ln -s /usr/local/bin/func-util /usr/local/bin/scaffold LABEL \ org.opencontainers.image.description="Knative Func Utils Image" \ @@ -24,4 +31,4 @@ LABEL \ org.opencontainers.image.vendor="https://github.com/knative/func" \ org.opencontainers.image.url="https://github.com/knative/func/pkgs/container/func-utils" -USER func:func +USER 65532:65532 diff --git a/cmd/func-deployer/main.go b/cmd/func-util/main.go similarity index 54% rename from cmd/func-deployer/main.go rename to cmd/func-util/main.go index 0ad583c19d..aca930d8ee 100644 --- a/cmd/func-deployer/main.go +++ b/cmd/func-util/main.go @@ -5,11 +5,14 @@ import ( "fmt" "os" "os/signal" + "path/filepath" "syscall" + "knative.dev/func/pkg/builders/s2i" fn "knative.dev/func/pkg/functions" "knative.dev/func/pkg/k8s" "knative.dev/func/pkg/knative" + "knative.dev/func/pkg/scaffolding" ) func main() { @@ -25,12 +28,68 @@ func main() { os.Exit(137) }() - err := deploy(ctx) + var cmd func(context.Context) error = unknown + + switch os.Args[0] { + case "deploy": + cmd = deploy + case "scaffold": + cmd = scaffold + } + + err := cmd(ctx) if err != nil { _, _ = fmt.Fprintf(os.Stderr, "ERROR: %s\n", err) os.Exit(1) } } + +func unknown(_ context.Context) error { + return fmt.Errorf("unknown command: " + os.Args[0]) +} + +func scaffold(ctx context.Context) error { + + if len(os.Args) != 2 { + return fmt.Errorf("expected exactly one positional argument (function project path)") + } + + path := os.Args[1] + + f, err := fn.NewFunction(path) + if err != nil { + return fmt.Errorf("cannot load func project: %w", err) + } + + if f.Runtime != "go" || f.Build.Builder != "s2i" { + // Scaffolding is for now supported/needed only for Go S2I build. + return nil + } + + embeddedRepo, err := fn.NewRepository("", "") + if err != nil { + return fmt.Errorf("cannot initialize repository: %w", err) + } + + appRoot := filepath.Join(f.Root, ".s2i", "builds", "last") + _ = os.RemoveAll(appRoot) + + err = scaffolding.Write(appRoot, f.Root, f.Runtime, f.Invoke, embeddedRepo.FS()) + if err != nil { + return fmt.Errorf("cannot write the scaffolding: %w", err) + } + + if err := os.MkdirAll(filepath.Join(f.Root, ".s2i", "bin"), 0755); err != nil { + return fmt.Errorf("unable to create .s2i bin dir. %w", err) + } + + if err := os.WriteFile(filepath.Join(f.Root, ".s2i", "bin", "assemble"), []byte(s2i.GoAssembler), 0700); err != nil { + return fmt.Errorf("unable to write go assembler. %w", err) + } + + return nil +} + func deploy(ctx context.Context) error { var err error deployer := knative.NewDeployer( diff --git a/pkg/pipelines/tekton/tasks.go b/pkg/pipelines/tekton/tasks.go index 239ddec25a..af32152c99 100644 --- a/pkg/pipelines/tekton/tasks.go +++ b/pkg/pipelines/tekton/tasks.go @@ -303,7 +303,7 @@ spec: description: Digest of the image just built. steps: - name: generate - image: quay.io/boson/s2i:latest + image: quay.io/boson/s2i@sha256:b4f80a3e6d6b2ee6d9a1a4e51a6b0f968cfd927132724b1209865e15b9ada4cb workingDir: $(workspaces.source.path) args: ["$(params.ENV_VARS[*])"] script: | @@ -321,7 +321,7 @@ spec: cat /env-vars/env-file echo "------------------------------" - /usr/local/bin/s2i --loglevel=$(params.LOGLEVEL) build $(params.PATH_CONTEXT) $(params.BUILDER_IMAGE) \ + /usr/local/bin/s2i --loglevel=$(params.LOGLEVEL) build --keep-symlinks $(params.PATH_CONTEXT) $(params.BUILDER_IMAGE) \ --image-scripts-url $(params.S2I_IMAGE_SCRIPTS_URL) \ --as-dockerfile /gen-source/Dockerfile.gen --environment-file /env-vars/env-file @@ -413,9 +413,34 @@ spec: `, DeployerImage) } +func getScaffoldTask() string { + return fmt.Sprintf(`apiVersion: tekton.dev/v1 +kind: Task +metadata: + name: func-scaffold + labels: + app.kubernetes.io/version: "0.1" + annotations: + tekton.dev/pipelines.minVersion: "0.12.1" + tekton.dev/categories: CLI + tekton.dev/tags: cli + tekton.dev/platforms: "linux/amd64" +spec: + params: + - name: path + description: Path to the function project + default: "" + steps: + - name: func-scaffold + image: %s + script: | + scaffold $(params.path) +`, DeployerImage) +} + // GetClusterTasks returns multi-document yaml containing tekton tasks used by func. func GetClusterTasks() string { - tasks := getBuildpackTask() + "\n---\n" + getS2ITask() + "\n---\n" + getDeployTask() + tasks := getBuildpackTask() + "\n---\n" + getS2ITask() + "\n---\n" + getDeployTask() + "\n---\n" + getScaffoldTask() tasks = strings.Replace(tasks, "kind: Task", "kind: ClusterTask", -1) tasks = strings.ReplaceAll(tasks, "apiVersion: tekton.dev/v1", "apiVersion: tekton.dev/v1beta1") return tasks diff --git a/pkg/pipelines/tekton/templates.go b/pkg/pipelines/tekton/templates.go index 41d6b112ed..0ced18214f 100644 --- a/pkg/pipelines/tekton/templates.go +++ b/pkg/pipelines/tekton/templates.go @@ -57,7 +57,7 @@ const ( - name: name value: git-clone - name: version - value: "0.4" + value: "0.9" workspaces: - name: output workspace: source-workspace` @@ -99,6 +99,7 @@ type templateData struct { FuncBuildpacksTaskRef string FuncS2iTaskRef string FuncDeployTaskRef string + FuncScaffoldTaskRef string // Reference for build task - whether it should run after fetch-sources task or not RunAfterFetchSources string @@ -128,6 +129,7 @@ func createPipelineTemplatePAC(f fn.Function, labels map[string]string) error { {getBuildpackTask(), &data.FuncBuildpacksTaskRef}, {getS2ITask(), &data.FuncS2iTaskRef}, {getDeployTask(), &data.FuncDeployTaskRef}, + {getScaffoldTask(), &data.FuncScaffoldTaskRef}, } { ts, err := getTaskSpec(val.ref) if err != nil { @@ -327,6 +329,7 @@ func createAndApplyPipelineTemplate(f fn.Function, namespace string, labels map[ {getBuildpackTask(), &data.FuncBuildpacksTaskRef}, {getS2ITask(), &data.FuncS2iTaskRef}, {getDeployTask(), &data.FuncDeployTaskRef}, + {getScaffoldTask(), &data.FuncScaffoldTaskRef}, } { ts, err := getTaskSpec(val.ref) if err != nil { diff --git a/pkg/pipelines/tekton/templates_pack.go b/pkg/pipelines/tekton/templates_pack.go index 88a2d76c40..52f2612a63 100644 --- a/pkg/pipelines/tekton/templates_pack.go +++ b/pkg/pipelines/tekton/templates_pack.go @@ -42,6 +42,15 @@ spec: type: array tasks: {{.GitCloneTaskRef}} + - name: scaffold + params: + - name: path + value: $(workspaces.source.path)/$(params.contextDir) + workspaces: + - name: source + workspace: source-workspace + {{.RunAfterFetchSources}} + {{.FuncScaffoldTaskRef}} - name: build params: - name: APP_IMAGE @@ -55,7 +64,8 @@ spec: - name: ENV_VARS value: - '$(params.buildEnvs[*])' - {{.RunAfterFetchSources}} + runAfter: + - scaffold {{.FuncBuildpacksTaskRef}} workspaces: - name: source diff --git a/pkg/pipelines/tekton/templates_s2i.go b/pkg/pipelines/tekton/templates_s2i.go index b78fc47a90..a786587740 100644 --- a/pkg/pipelines/tekton/templates_s2i.go +++ b/pkg/pipelines/tekton/templates_s2i.go @@ -46,6 +46,15 @@ spec: default: 'image:///usr/libexec/s2i' tasks: {{.GitCloneTaskRef}} + - name: scaffold + params: + - name: path + value: $(workspaces.source.path)/$(params.contextDir) + workspaces: + - name: source + workspace: source-workspace + {{.RunAfterFetchSources}} + {{.FuncScaffoldTaskRef}} - name: build params: - name: IMAGE @@ -61,7 +70,8 @@ spec: - '$(params.buildEnvs[*])' - name: S2I_IMAGE_SCRIPTS_URL value: $(params.s2iImageScriptsUrl) - {{.RunAfterFetchSources}} + runAfter: + - scaffold {{.FuncS2iTaskRef}} workspaces: - name: source diff --git a/test/oncluster/scenario_runtime_test.go b/test/oncluster/scenario_runtime_test.go index c783f9d7a6..615d116ceb 100644 --- a/test/oncluster/scenario_runtime_test.go +++ b/test/oncluster/scenario_runtime_test.go @@ -15,7 +15,7 @@ import ( var runtimeSupportMap = map[string][]string{ "node": {"pack", "s2i"}, - "go": {"pack"}, + "go": {"pack", "s2i"}, "rust": {"pack"}, "python": {"pack", "s2i"}, "quarkus": {"pack", "s2i"},