diff --git a/.github/workflows/bundle-release.yml b/.github/workflows/bundle-release.yml index eab242572..537104598 100644 --- a/.github/workflows/bundle-release.yml +++ b/.github/workflows/bundle-release.yml @@ -49,10 +49,10 @@ jobs: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Set up GO 1.22.7 + - name: Set up GO 1.22.12 uses: actions/setup-go@v1 with: - go-version: 1.22.7 + go-version: 1.22.12 id: go - name: InstallKubebuilder @@ -180,10 +180,10 @@ jobs: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Set up GO 1.22.7 + - name: Set up GO 1.22.12 uses: actions/setup-go@v1 with: - go-version: 1.22.7 + go-version: 1.22.12 id: go - name: InstallKubebuilder diff --git a/.github/workflows/olm-verify.yml b/.github/workflows/olm-verify.yml index 4bd8a0869..e77859023 100644 --- a/.github/workflows/olm-verify.yml +++ b/.github/workflows/olm-verify.yml @@ -34,10 +34,10 @@ jobs: - name: checkout uses: actions/checkout@v2 - - name: Set up GO 1.22.7 + - name: Set up GO 1.22.12 uses: actions/setup-go@v1 with: - go-version: 1.22.7 + go-version: 1.22.12 id: go - name: InstallKubebuilder diff --git a/.github/workflows/project.yml b/.github/workflows/project.yml index 4962f014c..9ef572280 100644 --- a/.github/workflows/project.yml +++ b/.github/workflows/project.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - go-version: [1.21.9, 1.22.7] + go-version: [1.21.9, 1.22.12] steps: - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@v1.3.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c9cd098ad..3f03bb6c6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,10 +37,10 @@ jobs: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Set up GO 1.22.7 + - name: Set up GO 1.22.12 uses: actions/setup-go@v1 with: - go-version: 1.22.7 + go-version: 1.22.12 id: go - name: InstallKubebuilder diff --git a/.github/workflows/test-helm-charts.yml b/.github/workflows/test-helm-charts.yml index fffd4ea71..a5c991685 100644 --- a/.github/workflows/test-helm-charts.yml +++ b/.github/workflows/test-helm-charts.yml @@ -83,11 +83,11 @@ jobs: run: hack/kind-cluster-build.sh --name chart-testing -c 1 -v 10 --k8sVersion v1.23.17 if: steps.list-changed.outputs.changed == 'true' - - name: Set up GO 1.22.7 + - name: Set up GO 1.22.12 if: steps.list-changed.outputs.changed == 'true' uses: actions/setup-go@v1 with: - go-version: 1.22.7 + go-version: 1.22.12 id: go - name: setup kubebuilder 3.6.0 diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 886abd6b3..b2c0739f9 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -34,10 +34,10 @@ jobs: repository: ${{github.event.pull_request.head.repo.full_name}} ref: ${{ github.event.pull_request.head.sha }} - - name: Set up GO 1.22.7 + - name: Set up GO 1.22.12 uses: actions/setup-go@v1 with: - go-version: 1.22.7 + go-version: 1.22.12 id: go - name: InstallKubebuilder diff --git a/.github/workflows/trivy_scheduled_master.yml b/.github/workflows/trivy_scheduled_master.yml index fc5ee9617..173bcc2f9 100644 --- a/.github/workflows/trivy_scheduled_master.yml +++ b/.github/workflows/trivy_scheduled_master.yml @@ -45,10 +45,10 @@ jobs: repository: ${{github.event.pull_request.head.repo.full_name}} ref: ${{ github.event.pull_request.head.sha }} - - name: Set up GO 1.22.7 + - name: Set up GO 1.22.12 uses: actions/setup-go@v1 with: - go-version: 1.22.7 + go-version: 1.22.12 id: go - name: InstallKubebuilder diff --git a/Dockerfile b/Dockerfile index f785df248..6936160af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.22.7-bullseye as builder +FROM golang:1.22.12-bullseye as builder WORKDIR /workspace/api COPY api/ . diff --git a/controllers/spec/common.go b/controllers/spec/common.go index 9cc9c01df..e0ad5639e 100644 --- a/controllers/spec/common.go +++ b/controllers/spec/common.go @@ -1801,7 +1801,7 @@ func generateDownloaderVolumeMountsForDownloader(javaRuntime *v1alpha1.JavaRunti } func generateDownloaderVolumeMountsForRuntime(javaRuntime *v1alpha1.JavaRuntime, pythonRuntime *v1alpha1.PythonRuntime, - goRuntime *v1alpha1.GoRuntime) []corev1.VolumeMount { + goRuntime *v1alpha1.GoRuntime, genericRuntime *v1alpha1.GenericRuntime) []corev1.VolumeMount { downloadPath := "" if javaRuntime != nil && javaRuntime.JarLocation != "" { downloadPath = javaRuntime.Jar @@ -1809,6 +1809,8 @@ func generateDownloaderVolumeMountsForRuntime(javaRuntime *v1alpha1.JavaRuntime, downloadPath = pythonRuntime.Py } else if goRuntime != nil && goRuntime.GoLocation != "" { downloadPath = goRuntime.Go + } else if genericRuntime != nil && genericRuntime.FunctionFile != "" { + downloadPath = genericRuntime.FunctionFile } if downloadPath != "" { @@ -1830,9 +1832,10 @@ func generateDownloaderVolumeMountsForRuntime(javaRuntime *v1alpha1.JavaRuntime, SubPath: subPath, }} } + idx := strings.LastIndex(mountPath, "/") return []corev1.VolumeMount{{ Name: DownloaderVolume, - MountPath: mountPath[:len(mountPath)-len(subPath)], + MountPath: mountPath[:idx], }} } return nil diff --git a/controllers/spec/function.go b/controllers/spec/function.go index 6dd0669d1..a1b8e1651 100644 --- a/controllers/spec/function.go +++ b/controllers/spec/function.go @@ -20,6 +20,7 @@ package spec import ( "context" "regexp" + "strings" "github.com/streamnative/function-mesh/api/compute/v1alpha1" "github.com/streamnative/function-mesh/utils" @@ -177,7 +178,7 @@ func makeFunctionContainer(function *v1alpha1.Function) *corev1.Container { mounts := makeFunctionVolumeMounts(function, function.Spec.Pulsar.AuthConfig) if utils.EnableInitContainers { mounts = append(mounts, - generateDownloaderVolumeMountsForRuntime(function.Spec.Java, function.Spec.Python, function.Spec.Golang)...) + generateDownloaderVolumeMountsForRuntime(function.Spec.Java, function.Spec.Python, function.Spec.Golang, function.Spec.GenericRuntime)...) } return &corev1.Container{ // TODO new container to pull user code image and upload jars into bookkeeper @@ -230,7 +231,8 @@ func makeFunctionCommand(function *v1alpha1.Function) []string { } if spec.Java != nil { if spec.Java.Jar != "" { - return MakeJavaFunctionCommand(spec.Java.JarLocation, spec.Java.Jar, + mountPath := extractMountPath(spec.Java.Jar) + return MakeJavaFunctionCommand(spec.Java.JarLocation, mountPath, spec.Name, spec.ClusterName, GenerateJavaLogConfigCommand(spec.Java, spec.LogTopicAgent), parseJavaLogLevel(spec.Java), @@ -246,7 +248,8 @@ func makeFunctionCommand(function *v1alpha1.Function) []string { } } else if spec.Python != nil { if spec.Python.Py != "" { - return MakePythonFunctionCommand(spec.Python.PyLocation, spec.Python.Py, + mountPath := extractMountPath(spec.Python.Py) + return MakePythonFunctionCommand(spec.Python.PyLocation, mountPath, spec.Name, spec.ClusterName, generatePythonLogConfigCommand(spec.Name, spec.Python, spec.LogTopicAgent), generateFunctionDetailsInJSON(function), string(function.UID), hasPulsarctl, hasWget, @@ -255,11 +258,13 @@ func makeFunctionCommand(function *v1alpha1.Function) []string { } } else if spec.Golang != nil { if spec.Golang.Go != "" { - return MakeGoFunctionCommand(spec.Golang.GoLocation, spec.Golang.Go, function) + mountPath := extractMountPath(spec.Golang.Go) + return MakeGoFunctionCommand(spec.Golang.GoLocation, mountPath, function) } } else if spec.GenericRuntime != nil { if spec.GenericRuntime.FunctionFile != "" { - return MakeGenericFunctionCommand(spec.GenericRuntime.FunctionFileLocation, spec.GenericRuntime.FunctionFile, + mountPath := extractMountPath(spec.GenericRuntime.FunctionFile) + return MakeGenericFunctionCommand(spec.GenericRuntime.FunctionFileLocation, mountPath, spec.GenericRuntime.Language, spec.ClusterName, generateFunctionDetailsInJSON(function), string(function.UID), spec.Pulsar.AuthSecret != "", spec.Pulsar.TLSSecret != "", function.Spec.SecretsMap, @@ -280,3 +285,18 @@ func generateFunctionDetailsInJSON(function *v1alpha1.Function) string { log.Info(string(json)) return string(json) } + +func extractMountPath(p string) string { + if utils.EnableInitContainers { + mountPath := p + // for relative path, volume should be mounted to the WorkDir + // and path also should be under the $WorkDir dir + if !strings.HasPrefix(p, "/") { + mountPath = WorkDir + p + } else if !strings.HasPrefix(p, WorkDir) { + mountPath = strings.Replace(p, "/", WorkDir, 1) + } + return mountPath + } + return p +} diff --git a/controllers/spec/function_test.go b/controllers/spec/function_test.go index 59f64cf09..feaeb0268 100644 --- a/controllers/spec/function_test.go +++ b/controllers/spec/function_test.go @@ -21,6 +21,9 @@ import ( "strings" "testing" + "github.com/streamnative/function-mesh/utils" + corev1 "k8s.io/api/core/v1" + "github.com/streamnative/function-mesh/api/compute/v1alpha1" "gotest.tools/assert" @@ -93,3 +96,54 @@ func makeFunctionSample(functionName string) *v1alpha1.Function { }, } } + +func makeFunctionSamplePackageURL(functionName string) *v1alpha1.Function { + f := makeFunctionSample(functionName) + f.Spec.Java.JarLocation = "function://public/default/java-function" + f.Spec.Java.Jar = "/tmp/java-function.jar" + return f +} + +func TestInitContainerDownloader(t *testing.T) { + utils.EnableInitContainers = true + function := makeFunctionSamplePackageURL("test") + + objectMeta := MakeFunctionObjectMeta(function) + + runnerImagePullSecrets := getFunctionRunnerImagePullSecret() + for _, mapSecret := range runnerImagePullSecrets { + if value, ok := mapSecret["name"]; ok { + function.Spec.Pod.ImagePullSecrets = append(function.Spec.Pod.ImagePullSecrets, corev1.LocalObjectReference{Name: value}) + } + } + runnerImagePullPolicy := getFunctionRunnerImagePullPolicy() + function.Spec.ImagePullPolicy = runnerImagePullPolicy + + labels := makeFunctionLabels(function) + statefulSet := MakeStatefulSet(objectMeta, function.Spec.Replicas, function.Spec.DownloaderImage, + makeFunctionContainer(function), makeFunctionVolumes(function, function.Spec.Pulsar.AuthConfig), labels, function.Spec.Pod, + function.Spec.Pulsar.AuthConfig, function.Spec.Pulsar.TLSConfig, function.Spec.Pulsar.PulsarConfig, function.Spec.Pulsar.AuthSecret, + function.Spec.Pulsar.TLSSecret, function.Spec.Java, function.Spec.Python, function.Spec.Golang, function.Spec.Pod.Env, function.Name, + function.Spec.LogTopic, function.Spec.FilebeatImage, function.Spec.LogTopicAgent, function.Spec.VolumeMounts, + function.Spec.VolumeClaimTemplates, function.Spec.PersistentVolumeClaimRetentionPolicy) + + assert.Assert(t, statefulSet != nil, "statefulSet should not be nil") + assert.Assert(t, len(statefulSet.Spec.Template.Spec.InitContainers) == 1, "init container should be 1 but got %d", len(statefulSet.Spec.Template.Spec.InitContainers)) + assert.Assert(t, statefulSet.Spec.Template.Spec.InitContainers[0].Name == "downloader", "init container name should be downloader but got %s", statefulSet.Spec.Template.Spec.InitContainers[0].Name) + downloaderCommands := statefulSet.Spec.Template.Spec.InitContainers[0].Command + functionCommands := makeFunctionCommand(function) + assert.Assert(t, len(downloaderCommands) == 3, "downloader commands should be 3 but got %d", len(downloaderCommands)) + assert.Assert(t, len(functionCommands) == 3, "function commands should be 3 but got %d", len(functionCommands)) + assert.Assert(t, len(statefulSet.Spec.Template.Spec.InitContainers[0].VolumeMounts) == 1, "volume mounts should be 1 but got %d", len(statefulSet.Spec.Template.Spec.InitContainers[0].VolumeMounts)) + assert.Assert(t, len(statefulSet.Spec.Template.Spec.Containers[0].VolumeMounts) == 1, "volume mounts should be 1 but got %d", len(statefulSet.Spec.Template.Spec.Containers[0].VolumeMounts)) + startDownloadCommands := downloaderCommands[2] + downloadVolumeMount := statefulSet.Spec.Template.Spec.InitContainers[0].VolumeMounts[0] + assert.Assert(t, downloadVolumeMount.Name == "downloader-volume", "volume mount name should be download-volume but got %s", downloadVolumeMount.Name) + assert.Assert(t, downloadVolumeMount.MountPath == "/pulsar/download", "volume mount path should be /pulsar/download but got %s", downloadVolumeMount.MountPath) + startFunctionCommands := functionCommands[2] + functionVolumeMount := statefulSet.Spec.Template.Spec.Containers[0].VolumeMounts[0] + assert.Assert(t, functionVolumeMount.Name == "downloader-volume", "volume mount name should be downloader-volume but got %s", functionVolumeMount.Name) + assert.Assert(t, functionVolumeMount.MountPath == "/pulsar/tmp", "volume mount path should be /pulsar/tmp but got %s", functionVolumeMount.MountPath) + assert.Assert(t, strings.Contains(startDownloadCommands, "/pulsar/download/java-function.jar"), "download command should contain /pulsar/download/java-function.jar: %s", startDownloadCommands) + assert.Assert(t, strings.Contains(startFunctionCommands, "/pulsar/tmp/java-function.jar"), "function command should contain /pulsar/tmp/java-function.jar: %s", startFunctionCommands) +} diff --git a/controllers/spec/sink.go b/controllers/spec/sink.go index fde332f94..9460a46fa 100644 --- a/controllers/spec/sink.go +++ b/controllers/spec/sink.go @@ -21,12 +21,13 @@ import ( "context" "regexp" + corev1 "k8s.io/api/core/v1" + "github.com/streamnative/function-mesh/utils" "google.golang.org/protobuf/encoding/protojson" appsv1 "k8s.io/api/apps/v1" autov2 "k8s.io/api/autoscaling/v2" v1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" @@ -106,7 +107,7 @@ func makeSinkContainer(sink *v1alpha1.Sink) *corev1.Container { allowPrivilegeEscalation := false mounts := makeSinkVolumeMounts(sink, sink.Spec.Pulsar.AuthConfig) if utils.EnableInitContainers { - mounts = append(mounts, generateDownloaderVolumeMountsForRuntime(sink.Spec.Java, nil, nil)...) + mounts = append(mounts, generateDownloaderVolumeMountsForRuntime(sink.Spec.Java, nil, nil, nil)...) } return &corev1.Container{ // TODO new container to pull user code image and upload jars into bookkeeper @@ -225,7 +226,8 @@ func MakeSinkCommand(sink *v1alpha1.Sink) []string { hasPulsarctl = true hasWget = true } - return MakeJavaFunctionCommand(spec.Java.JarLocation, spec.Java.Jar, + mountPath := extractMountPath(spec.Java.Jar) + return MakeJavaFunctionCommand(spec.Java.JarLocation, mountPath, spec.Name, spec.ClusterName, GenerateJavaLogConfigCommand(spec.Java, spec.LogTopicAgent), parseJavaLogLevel(spec.Java), diff --git a/controllers/spec/source.go b/controllers/spec/source.go index c03670d1e..20d144165 100644 --- a/controllers/spec/source.go +++ b/controllers/spec/source.go @@ -102,7 +102,7 @@ func makeSourceContainer(source *v1alpha1.Source) *corev1.Container { allowPrivilegeEscalation := false mounts := makeSourceVolumeMounts(source, source.Spec.Pulsar.AuthConfig) if utils.EnableInitContainers { - mounts = append(mounts, generateDownloaderVolumeMountsForRuntime(source.Spec.Java, nil, nil)...) + mounts = append(mounts, generateDownloaderVolumeMountsForRuntime(source.Spec.Java, nil, nil, nil)...) } return &corev1.Container{ // TODO new container to pull user code image and upload jars into bookkeeper @@ -172,7 +172,9 @@ func makeSourceCommand(source *v1alpha1.Source) []string { hasPulsarctl = true hasWget = true } - return MakeJavaFunctionCommand(spec.Java.JarLocation, spec.Java.Jar, + + mountPath := extractMountPath(spec.Java.Jar) + return MakeJavaFunctionCommand(spec.Java.JarLocation, mountPath, spec.Name, spec.ClusterName, GenerateJavaLogConfigCommand(spec.Java, spec.LogTopicAgent), parseJavaLogLevel(spec.Java), diff --git a/redhat.Dockerfile b/redhat.Dockerfile index 80fba57d4..14fdce4e5 100644 --- a/redhat.Dockerfile +++ b/redhat.Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.22.7-bullseye as builder +FROM golang:1.22.12-bullseye as builder WORKDIR /workspace/api COPY api/ .