Optimise func-utils image (#2686)

* Use command instad of script in some tkn tasks

The "script" requires /bin/sh present in the image.

Signed-off-by: Matej Vašek <mvasek@redhat.com>

* Add s2i-generate command to func-util image

The command encompasses some logic previously implemented as shell
script defined in tekton task. This allows us to remove sh/shell from
the func-util image.

Signed-off-by: Matej Vašek <mvasek@redhat.com>

* Make func-util image "FROM scratch"

Signed-off-by: Matej Vašek <mvasek@redhat.com>

* Change func-utils image tag latest->v2

Since there are backward incompatible changes we must not change how
'latest' tag work (at least for some time).

For this reason we change tag to v2, so newer versions of func use that
and older use 'latest' that is compatible with them.

Signed-off-by: Matej Vašek <mvasek@redhat.com>

---------

Signed-off-by: Matej Vašek <mvasek@redhat.com>
This commit is contained in:
Matej Vašek 2025-02-06 08:02:13 +01:00 committed by GitHub
parent f8db896f85
commit 9c0c216193
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 178 additions and 136 deletions

View File

@ -180,6 +180,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: knative/actions/setup-go@main
- uses: docker/setup-qemu-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
@ -189,12 +190,14 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
run: |
./hack/fetch-util-img-prerequisites.sh
for a in amd64 arm64 ppc64le s390x; do
CGO_ENABLED=0 go build -o "func-util-$a" -trimpath -ldflags '-w -s' ./cmd/func-util
done
docker buildx create --name multiarch --driver docker-container --use
docker buildx build . -f Dockerfile.utils \
--platform=linux/ppc64le,linux/s390x,linux/amd64,linux/arm64 \
--push \
-t "ghcr.io/knative/func-utils:latest" \
-t "ghcr.io/knative/func-utils:v2" \
--annotation index:org.opencontainers.image.description="Knative Func Utils Image" \
--annotation index:org.opencontainers.image.source="https://github.com/knative/func" \
--annotation index:org.opencontainers.image.vendor="https://github.com/knative/func" \

View File

@ -1,41 +1,13 @@
FROM --platform=$BUILDPLATFORM scratch AS builder
ARG BUILDPLATFORM
ARG BUILDARCH
ARG TARGETPLATFORM
ARG TARGETARCH
ADD .artifacts/alpine-minirootfs-$BUILDARCH.tar.gz /
COPY .artifacts/go.tar.gz /tmp/go.tar.gz
RUN tar -C /usr/local -xzf /tmp/go.tar.gz
ENV PATH="/usr/local/go/bin:$PATH"
WORKDIR /workspace
COPY go.mod go.sum ./
RUN go mod download -x
COPY . .
RUN GOARCH=$TARGETARCH go build -o func-util -trimpath -ldflags '-w -s' ./cmd/func-util
#########################
FROM scratch
ARG TARGETARCH
ARG FUNC_UTIL_BINARY=func-util-$TARGETARCH
ADD .artifacts/alpine-minirootfs-$TARGETARCH.tar.gz /
ENV PATH=/
RUN apk add --no-cache tar
COPY $FUNC_UTIL_BINARY /func-util
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 && \
ln -s /usr/local/bin/func-util /usr/local/bin/s2i && \
ln -s /usr/local/bin/func-util /usr/local/bin/sh && \
ln -s /usr/local/bin/func-util /usr/local/bin/socat
ADD func-util-symlinks.tgz /
LABEL \
org.opencontainers.image.description="Knative Func Utils Image" \

View File

@ -30,7 +30,7 @@ KVER ?= $(shell git describe --tags --match 'knative-*')
LDFLAGS := -X knative.dev/func/pkg/app.vers=$(VERS) -X knative.dev/func/pkg/app.kver=$(KVER) -X knative.dev/func/pkg/app.hash=$(HASH)
FUNC_UTILS_IMG ?= ghcr.io/knative/func-utils:latest
FUNC_UTILS_IMG ?= ghcr.io/knative/func-utils:v2
LDFLAGS += -X knative.dev/func/pkg/k8s.SocatImage=$(FUNC_UTILS_IMG)
LDFLAGS += -X knative.dev/func/pkg/k8s.TarImage=$(FUNC_UTILS_IMG)
LDFLAGS += -X knative.dev/func/pkg/pipelines/tekton.DeployerImage=$(FUNC_UTILS_IMG)

View File

@ -52,6 +52,8 @@ func main() {
cmd = socat
case "sh":
cmd = sh
case "s2i-generate":
cmd = s2iGenerate
}
err := cmd(ctx)

View File

@ -0,0 +1,143 @@
//go:build exclude_graphdriver_btrfs || !cgo
// +build exclude_graphdriver_btrfs !cgo
package main
import (
"context"
"fmt"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/openshift/source-to-image/pkg/api"
"github.com/openshift/source-to-image/pkg/build"
"github.com/openshift/source-to-image/pkg/build/strategies"
"github.com/openshift/source-to-image/pkg/scm/git"
"github.com/spf13/cobra"
fn "knative.dev/func/pkg/functions"
)
func s2iGenerate(ctx context.Context) error {
cmd := newS2IGenerateCmd()
err := cmd.ExecuteContext(ctx)
if err != nil {
return fmt.Errorf("cannot s2i generate: %w", err)
}
return nil
}
type genConfig struct {
target string
pathContext string
builderImage string
registry string
imageScriptUrl string
logLevel string
envVars []string
}
func newS2IGenerateCmd() *cobra.Command {
var config genConfig
genCmd := &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
config.envVars = args
return runS2IGenerate(cmd.Context(), config)
},
}
genCmd.Flags().StringVar(&config.target, "target", "/gen-source", "")
genCmd.Flags().StringVar(&config.pathContext, "path-context", ".", "")
genCmd.Flags().StringVar(&config.builderImage, "builder-image", "", "")
genCmd.Flags().StringVar(&config.registry, "registry", "", "")
genCmd.Flags().StringVar(&config.imageScriptUrl, "image-script-url", "image:///usr/libexec/s2i", "")
genCmd.Flags().StringVar(&config.logLevel, "log-level", "0", "")
return genCmd
}
func runS2IGenerate(ctx context.Context, c genConfig) error {
wd, err := os.Getwd()
if err != nil {
return fmt.Errorf("cannot get working directory: %w", err)
}
funcRoot := filepath.Join(wd, c.pathContext)
// replace registry in func.yaml
f, err := fn.NewFunction(funcRoot)
if err != nil {
return fmt.Errorf("cannot load function: %w", err)
}
f.Registry = c.registry
err = f.Write()
if err != nil {
return fmt.Errorf("cannot write function: %w", err)
}
// append node_modules into .s2iignore
s2iIgnorePath := filepath.Join(funcRoot, ".s2iignore")
if fi, _ := os.Stat(s2iIgnorePath); fi != nil {
var file *os.File
file, err = os.OpenFile(s2iIgnorePath, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("cannot open s2i ignore file for append: %w", err)
}
defer func(file *os.File) {
_ = file.Close()
}(file)
_, err = file.Write([]byte("\nnode_modules"))
if err != nil {
return fmt.Errorf("cannot append node_modules directory to s2i ignore file: %w", err)
}
}
// prepare envvars
var envs = make([]api.EnvironmentSpec, 0, len(c.envVars))
for _, e := range c.envVars {
var es api.EnvironmentSpec
part := strings.SplitN(e, "=", 2)
switch len(part) {
case 1:
es.Name = part[0]
case 2:
es.Name = part[0]
es.Value = part[1]
default:
continue
}
if es.Name != "" {
envs = append(envs, es)
}
}
s2iConfig := api.Config{
Source: &git.URL{
URL: url.URL{Path: funcRoot},
Type: git.URLTypeLocal,
},
BuilderImage: c.builderImage,
ImageScriptsURL: c.imageScriptUrl,
KeepSymlinks: true,
Environment: envs,
AsDockerfile: filepath.Join(c.target, "Dockerfile.gen"),
}
builder, _, err := strategies.Strategy(nil, &s2iConfig, build.Overrides{})
if err != nil {
return fmt.Errorf("cannot create builder: %w", err)
}
_, err = builder.Build(&s2iConfig)
if err != nil {
return fmt.Errorf("cannot build: %w", err)
}
return nil
}

BIN
func-util-symlinks.tgz Normal file

Binary file not shown.

View File

@ -1,60 +0,0 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
alpine_version='3.21.2'
go_version='1.23.5'
alpine_release_key='0482D84022F52DF1C4E7CD43293ACD0907D9495A'
google_release_key='EB4C1BFD4F042F6DDDCCEC917721F63BD38B4796'
artifacts_dir="$(dirname "$(realpath "$0")")/../.artifacts"
if [ ! -d "$artifacts_dir" ]; then
mkdir "$artifacts_dir";
fi
GNUPGHOME="$(mktemp -d)"
export GNUPGHOME
gpg --keyserver 'keyserver.ubuntu.com' --recv-keys "$alpine_release_key"
gpg --keyserver 'keyserver.ubuntu.com' --recv-keys "$google_release_key"
declare -A arch_map
arch_map['x86_64']='amd64'
arch_map['aarch64']='arm64'
function fetch_alpine_minirootfs() {
local alpine_rel_url='https://dl-cdn.alpinelinux.org/alpine/latest-stable/releases'
local arch
for arch in 'x86_64' 'aarch64' 'ppc64le' 's390x'; do
local arch_alt="${arch_map[$arch]:-$arch}"
local tarball="${artifacts_dir}/alpine-minirootfs-${arch_alt}.tar.gz"
local signature="${artifacts_dir}/alpine-minirootfs-${arch_alt}.tar.gz.asc"
curl -sSL "${alpine_rel_url}/${arch}/alpine-minirootfs-${alpine_version}-${arch}.tar.gz" > \
"${tarball}"
curl -sSL "${alpine_rel_url}/${arch}/alpine-minirootfs-${alpine_version}-${arch}.tar.gz.asc" > \
"${signature}"
gpg --assert-signer="$alpine_release_key" --verify "${signature}" "${tarball}"
done
}
function fetch_golang() {
local arch
arch="$(uname -m)"
local arch_alt="${arch_map[$arch]:-$arch}"
local tarball="${artifacts_dir}/go.tar.gz"
local signature="${artifacts_dir}/go.tar.gz.asc"
curl -sSL "https://go.dev/dl/go${go_version}.linux-${arch_alt}.tar.gz" > "${tarball}"
curl -sSL "https://go.dev/dl/go${go_version}.linux-${arch_alt}.tar.gz.asc" > "${signature}"
gpg --assert-signer="$google_release_key" --verify "${signature}" "${tarball}"
}
function main() {
fetch_alpine_minirootfs
fetch_golang
}
main "$@"

View File

@ -4,11 +4,11 @@ set -o errexit
set -o nounset
set -o pipefail
FUNC_UTILS_IMG="localhost:50000/knative/func-utils:latest"
FUNC_UTILS_IMG="localhost:50000/knative/func-utils:v2"
"$(dirname "$(realpath "$0")")/fetch-util-img-prerequisites.sh"
CGO_ENABLED=0 go build -o "func-util" -trimpath -ldflags '-w -s' ./cmd/func-util
docker build . -f Dockerfile.utils -t "${FUNC_UTILS_IMG}"
docker build . -f Dockerfile.utils -t "${FUNC_UTILS_IMG}" --build-arg FUNC_UTIL_BINARY=func-util
docker push "${FUNC_UTILS_IMG}"
# Build custom buildah image for tests.

View File

@ -28,7 +28,7 @@ import (
"k8s.io/client-go/tools/remotecommand"
)
var SocatImage = "ghcr.io/knative/func-utils:latest"
var SocatImage = "ghcr.io/knative/func-utils:v2"
// NewInClusterDialer creates context dialer that will dial TCP connections via POD running in k8s cluster.
// This is useful when accessing k8s services that are not exposed outside cluster (e.g. openshift image registry).

View File

@ -67,7 +67,7 @@ func DeletePersistentVolumeClaims(ctx context.Context, namespaceOverride string,
return client.CoreV1().PersistentVolumeClaims(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
}
var TarImage = "ghcr.io/knative/func-utils:latest"
var TarImage = "ghcr.io/knative/func-utils:v2"
// UploadToVolume uploads files (passed in form of tar stream) into volume.
func UploadToVolume(ctx context.Context, content io.Reader, claimName, namespace string) error {

View File

@ -5,7 +5,7 @@ import (
"strings"
)
var DeployerImage = "ghcr.io/knative/func-utils:latest"
var DeployerImage = "ghcr.io/knative/func-utils:v2"
func getBuildpackTask() string {
return `apiVersion: tekton.dev/v1
@ -306,37 +306,21 @@ spec:
- name: generate
image: %s
workingDir: $(workspaces.source.path)
args: ["$(params.ENV_VARS[*])"]
script: |
echo "Processing Build Environment Variables"
echo "" > /env-vars/env-file
for var in "$@"
do
if [[ "$var" != "=" ]]; then
echo "$var" >> /env-vars/env-file
fi
done
echo "Generated Build Env Var file"
echo "------------------------------"
cat /env-vars/env-file
echo "------------------------------"
/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
echo "Preparing func.yaml for later deployment"
func_file="$(workspaces.source.path)/func.yaml"
if [ "$(params.PATH_CONTEXT)" != "" ]; then
func_file="$(workspaces.source.path)/$(params.PATH_CONTEXT)/func.yaml"
fi
sed -i "s|^registry:.*$|registry: $(params.REGISTRY)|" "$func_file"
echo "Function image registry: $(params.REGISTRY)"
s2iignore_file="$(dirname "$func_file")/.s2iignore"
[ -f "$s2iignore_file" ] || echo "node_modules" >> "$s2iignore_file"
command:
- s2i-generate
- "--target"
- /gen-source
- "--path-context"
- $(params.PATH_CONTEXT)
- "--builder-image"
- $(params.BUILDER_IMAGE)
- "--registry"
- $(params.REGISTRY)
- "--image-script-url"
- $(params.S2I_IMAGE_SCRIPTS_URL)
- "--log-level"
- $(params.LOGLEVEL)
- $(params.ENV_VARS[*])
volumeMounts:
- mountPath: /gen-source
name: gen-source
@ -418,8 +402,7 @@ spec:
steps:
- name: func-deploy
image: "%s"
script: |
deploy $(params.path) "$(params.image)"
command: ["deploy", "$(params.path)", "$(params.image)"]
`, DeployerImage)
}
@ -446,8 +429,7 @@ spec:
steps:
- name: func-scaffold
image: %s
script: |
scaffold $(params.path)
command: ["scaffold", "$(params.path)"]
`, DeployerImage)
}