diff --git a/Dockerfile b/Dockerfile index 6a0c30a04..3cd09c888 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ RUN --mount=type=cache,target=/go/pkg --mount=type=cache,target=/root/.cache/go- FROM ghcr.io/acorn-io/images-mirror/golang:1.21-alpine AS loglevel WORKDIR /usr/src -RUN apk -U add curl +RUN apk -U add curl && rm -rf /var/cache/apk/* RUN curl -sfL https://github.com/acorn-io/loglevel/archive/refs/tags/v0.1.6.tar.gz | tar xzf - --strip-components=1 RUN --mount=type=cache,target=/go/pkg --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 go build -o /usr/local/bin/loglevel -ldflags "-s -w" @@ -25,14 +25,14 @@ COPY --from=sleep /sleep /src/pkg/controller/appdefinition/embed/acorn-sleep RUN --mount=type=cache,target=/go/pkg --mount=type=cache,target=/root/.cache/go-build GO_TAGS=netgo,image make build FROM ghcr.io/acorn-io/images-mirror/nginx:1.23.2-alpine AS base -RUN apk add --no-cache ca-certificates iptables ip6tables fuse3 git openssh pigz xz \ +RUN apk add --no-cache ca-certificates iptables ip6tables fuse3 git openssh pigz xz busybox-static \ && ln -s fusermount3 /usr/bin/fusermount RUN adduser -D acorn RUN mkdir apiserver.local.config && chown acorn apiserver.local.config RUN --mount=from=binfmt,src=/usr/bin,target=/usr/src for i in aarch64 x86_64; do if [ -e /usr/src/qemu-$i ]; then cp /usr/src/qemu-$i /usr/bin; fi; done RUN --mount=from=buildkit,src=/usr/bin,target=/usr/src for i in aarch64 x86_64; do if [ -e /usr/src/buildkit-qemu-$i ]; then cp /usr/src/buildkit-qemu-$i /usr/bin; fi; done COPY --from=binfmt /usr/bin/binfmt /usr/local/bin -COPY --from=buildkit /usr/bin/buildkitd /usr/bin/buildctl /usr/bin/buildkit-runc /usr/local/bin +COPY --from=buildkit /usr/bin/buildkitd /usr/bin/buildctl /usr/bin/buildkit-runc /usr/local/bin/ COPY --from=registry /etc/docker/registry/config.yml /etc/docker/registry/config.yml COPY --from=registry /bin/registry /usr/local/bin COPY --from=klipper-lb /usr/bin/entry /usr/local/bin/klipper-lb @@ -43,6 +43,7 @@ COPY --from=loglevel /usr/local/bin/loglevel /usr/local/bin/ VOLUME /var/lib/buildkit COPY /scripts/acorn-helper-init /usr/local/bin +COPY /scripts/acorn-busybox-init /usr/local/bin COPY /scripts/acorn-job-helper-init /usr/local/bin COPY /scripts/acorn-job-helper-shutdown /usr/local/bin COPY /scripts/acorn-job-get-output /usr/local/bin diff --git a/integration/client/imagerules/testdata/nested-perms/Acornfile b/integration/client/imagerules/testdata/nested-perms/Acornfile index 767ab4811..3034a7558 100644 --- a/integration/client/imagerules/testdata/nested-perms/Acornfile +++ b/integration/client/imagerules/testdata/nested-perms/Acornfile @@ -1,6 +1,6 @@ containers: { "rootapp": { - image: "nginx:latest" + image: "ghcr.io/acorn-io/images-mirror/nginx:latest" permissions: rules: [{ verbs: ["get"] apiGroups: ["foo.bar.com"] diff --git a/integration/client/imagerules/testdata/nested-perms/nested.acorn b/integration/client/imagerules/testdata/nested-perms/nested.acorn index 504b9ff12..4cb5054bb 100644 --- a/integration/client/imagerules/testdata/nested-perms/nested.acorn +++ b/integration/client/imagerules/testdata/nested-perms/nested.acorn @@ -1,6 +1,6 @@ containers: { "awsapp": { - image: "nginx:latest" + image: "ghcr.io/acorn-io/images-mirror/nginx:latest" permissions: { rules: [{ verbs: ["get"] diff --git a/pkg/apis/internal.acorn.io/v1/appspec.go b/pkg/apis/internal.acorn.io/v1/appspec.go index 81b455495..2987bbaac 100644 --- a/pkg/apis/internal.acorn.io/v1/appspec.go +++ b/pkg/apis/internal.acorn.io/v1/appspec.go @@ -165,6 +165,7 @@ type VolumeMount struct { Volume string `json:"volume,omitempty"` SubPath string `json:"subPath,omitempty"` ContextDir string `json:"contextDir,omitempty"` + Preload bool `json:"preload,omitempty"` Secret VolumeSecretMount `json:"secret,omitempty"` } diff --git a/pkg/apis/internal.acorn.io/v1/unmarshal.go b/pkg/apis/internal.acorn.io/v1/unmarshal.go index 6872c2986..f89cb6582 100644 --- a/pkg/apis/internal.acorn.io/v1/unmarshal.go +++ b/pkg/apis/internal.acorn.io/v1/unmarshal.go @@ -974,7 +974,7 @@ func (in *VolumeMount) UnmarshalJSON(data []byte) error { } else if strings.HasPrefix(s, "./") { in.ContextDir = s } else { - in.Volume, in.SubPath, err = parseVolumeReference(s) + in.Volume, in.SubPath, in.Preload, err = parseVolumeReference(s) if err != nil { return err } @@ -1411,14 +1411,19 @@ func parseVolumeDefinition(anonName, s string) (VolumeBinding, error) { return result, nil } -func parseVolumeReference(s string) (string, string, error) { +// parseVolumeReference parses a volume reference string into its components, including query parameters +// @return string - volume reference (unmodified) +// @return string - subpath that should be mounted (query param) +// @return bool - preload: if the volume should be pre-populated with data from the container image (query param) +// @return error - parse error +func parseVolumeReference(s string) (string, string, bool, error) { if !strings.HasPrefix(s, "volume://") && !strings.HasPrefix(s, "ephemeral://") { - return s, "", nil + return s, "", false, nil } u, err := url.Parse(s) if err != nil { - return "", "", fmt.Errorf("parsing volume reference %s: %w", s, err) + return "", "", false, fmt.Errorf("parsing volume reference %s: %w", s, err) } subPath := u.Query().Get("subPath") @@ -1429,7 +1434,16 @@ func parseVolumeReference(s string) (string, string, error) { subPath = u.Query().Get("sub-path") } - return s, subPath, nil + preload := false + preloadStr := u.Query().Get("preload") + if preloadStr != "" { + preload, err = strconv.ParseBool(preloadStr) + if err != nil { + return "", "", false, fmt.Errorf("malformed ?preload value %q: %v", preloadStr, err) + } + } + + return s, subPath, preload, nil } func MustParseResourceQuantity(s Quantity) *resource.Quantity { diff --git a/pkg/apis/internal.acorn.io/v1/unmarshal_test.go b/pkg/apis/internal.acorn.io/v1/unmarshal_test.go index 6d5046836..1d91f5b08 100644 --- a/pkg/apis/internal.acorn.io/v1/unmarshal_test.go +++ b/pkg/apis/internal.acorn.io/v1/unmarshal_test.go @@ -190,3 +190,29 @@ func FuzzUserContextUnmarshalJSON(f *testing.F) { } }) } + +func TestParseVolumeReference(t *testing.T) { + s, subPath, preload, err := parseVolumeReference("name") + require.NoError(t, err) + require.Equal(t, "name", s) + require.Empty(t, subPath) + require.False(t, preload) + + _, subPath, preload, err = parseVolumeReference("volume://foo?subPath=bar") + require.NoError(t, err) + require.False(t, preload) + require.Equal(t, "bar", subPath) + + _, subPath, preload, err = parseVolumeReference("volume://foo?preload=true&subPath=bar") + require.NoError(t, err) + require.Equal(t, "bar", subPath) + require.True(t, preload) + + _, subPath, preload, err = parseVolumeReference("volume://foo?preload=false") + require.NoError(t, err) + require.Empty(t, subPath) + require.False(t, preload) + + _, _, _, err = parseVolumeReference("volume://foo?preload=foo") + require.Error(t, err) +} diff --git a/pkg/controller/appdefinition/deploy.go b/pkg/controller/appdefinition/deploy.go index 1be66e044..b0000f9a4 100644 --- a/pkg/controller/appdefinition/deploy.go +++ b/pkg/controller/appdefinition/deploy.go @@ -31,6 +31,7 @@ import ( "github.com/acorn-io/z" "github.com/google/go-containerregistry/pkg/name" "github.com/rancher/wrangler/pkg/data/convert" + wname "github.com/rancher/wrangler/pkg/name" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" apierror "k8s.io/apimachinery/pkg/api/errors" @@ -213,7 +214,7 @@ func hasContextDir(container v1.Container) bool { return false } -func toContainers(app *v1.AppInstance, tag name.Reference, name string, container v1.Container, interpolator *secrets.Interpolator, addWait bool) ([]corev1.Container, []corev1.Container) { +func toContainers(app *v1.AppInstance, tag name.Reference, name string, container v1.Container, interpolator *secrets.Interpolator, addWait, addBusybox bool) ([]corev1.Container, []corev1.Container) { var ( containers []corev1.Container initContainers []corev1.Container @@ -234,7 +235,49 @@ func toContainers(app *v1.AppInstance, tag name.Reference, name string, containe }) } + if addBusybox { + // Drop the static busybox binary into a shared volume so that we can use it in initContainers. + initContainers = append(initContainers, corev1.Container{ + Name: wname.SafeConcatName("acorn-helper-busybox", string(app.UID)), + Image: system.DefaultImage(), + Command: []string{"acorn-busybox-init"}, + VolumeMounts: []corev1.VolumeMount{ + { + Name: sanitizeVolumeName(AcornHelper), + MountPath: AcornHelperPath, + }, + }, + }, + ) + } + newContainer := toContainer(app, tag, name, container, interpolator, addWait && len(container.Ports) > 0) + for src, dir := range container.Dirs { + if dir.Preload { + // If a directory is marked for preload, then add an initContainer that copies the directory into the shared volume. + // Data will be copied to the data/ subdirectory and we'll drop a .preload-done file in the root to indicate that + // the copy has been completed (and should not be repeated). + initContainers = append(initContainers, corev1.Container{ + Name: wname.SafeConcatName("acorn-preload-dir", sanitizeVolumeName(src), string(app.UID)), + Image: newContainer.Image, + Command: []string{AcornHelperBusyboxPath, "sh", "-c"}, + ImagePullPolicy: corev1.PullIfNotPresent, + // cp -aT == copy recursively, following symlinks and preserving file attributes, only copy the contents of the source directory + Args: []string{fmt.Sprintf("if [ ! -f /dest/.preload-done ]; then mkdir -p /dest/data && cp -aT %s /dest/data && date > /dest/.preload-done; fi", src)}, + VolumeMounts: []corev1.VolumeMount{ + { + Name: sanitizeVolumeName(dir.Volume), + MountPath: "/dest", + SubPath: dir.SubPath, + }, + { + Name: sanitizeVolumeName(AcornHelper), + MountPath: AcornHelperPath, + }, + }, + }) + } + } containers = append(containers, newContainer) for _, entry := range typed.Sorted(container.Sidecars) { newContainer = toContainer(app, tag, entry.Key, entry.Value, interpolator, addWait && len(entry.Value.Ports) > 0) @@ -293,11 +336,18 @@ func toMounts(app *v1.AppInstance, container v1.Container, interpolation *secret helperMounted = true } } else if mount.Secret.Name == "" { - result = append(result, corev1.VolumeMount{ + vm := corev1.VolumeMount{ Name: sanitizeVolumeName(mount.Volume), MountPath: path.Join("/", mountPath), SubPath: mount.SubPath, - }) + } + if mount.Preload { + // Preloaded contents land in the data/ subdirectory, since we have to drop the .preload-done file + // in the root of the volume to indicate that the copy has been completed + // and that may cause issues with applications that dislike unknown files in their path. + vm.SubPath = path.Join(vm.SubPath, "data") + } + result = append(result, vm) } else { result = append(result, corev1.VolumeMount{ Name: secretPodVolName(mount.Secret.Name), @@ -670,20 +720,27 @@ func getSecretAnnotations(req router.Request, appInstance *v1.AppInstance, conta func toDeployment(req router.Request, appInstance *v1.AppInstance, tag name.Reference, name string, container v1.Container, pullSecrets *PullSecrets, interpolator *secrets.Interpolator) (*appsv1.Deployment, error) { var ( - stateful = isStateful(appInstance, container) - addWait = !stateful && len(acornSleepBinary) > 0 && z.Dereference(container.Scale) > 1 && !appInstance.Status.GetDevMode() + stateful = isStateful(appInstance, container) + addWait = !stateful && len(acornSleepBinary) > 0 && z.Dereference(container.Scale) > 1 && !appInstance.Status.GetDevMode() + addBusybox = false ) + for _, v := range container.Dirs { + if v.Preload { + addBusybox = true + } + } + interpolator = interpolator.ForContainer(name) - containers, initContainers := toContainers(appInstance, tag, name, container, interpolator, addWait) + containers, initContainers := toContainers(appInstance, tag, name, container, interpolator, addWait, addBusybox) secretAnnotations, err := getSecretAnnotations(req, appInstance, container, interpolator) if err != nil { return nil, err } - volumes, err := toVolumes(appInstance, container, interpolator, addWait) + volumes, err := toVolumes(appInstance, container, interpolator, addWait, addBusybox) if err != nil { return nil, err } diff --git a/pkg/controller/appdefinition/jobs.go b/pkg/controller/appdefinition/jobs.go index e5c1cbe98..bcefb9cd8 100644 --- a/pkg/controller/appdefinition/jobs.go +++ b/pkg/controller/appdefinition/jobs.go @@ -40,7 +40,13 @@ func toJobs(req router.Request, appInstance *v1.AppInstance, pullSecrets *PullSe if err != nil { return nil, err } - job, err := toJob(req, appInstance, pullSecrets, tag, jobName, jobDef, interpolator) + addBusybox := false + for _, v := range jobDef.Dirs { + if v.Preload { + addBusybox = true + } + } + job, err := toJob(req, appInstance, pullSecrets, tag, jobName, jobDef, interpolator, addBusybox) if err != nil { return nil, err } @@ -85,7 +91,7 @@ func setSecretOutputVolume(containers []corev1.Container) (result []corev1.Conta return } -func toJob(req router.Request, appInstance *v1.AppInstance, pullSecrets *PullSecrets, tag name.Reference, name string, container v1.Container, interpolator *secrets.Interpolator) (kclient.Object, error) { +func toJob(req router.Request, appInstance *v1.AppInstance, pullSecrets *PullSecrets, tag name.Reference, name string, container v1.Container, interpolator *secrets.Interpolator, addBusybox bool) (kclient.Object, error) { interpolator = interpolator.ForJob(name) jobEventName := jobs.GetEvent(name, appInstance) @@ -100,7 +106,7 @@ func toJob(req router.Request, appInstance *v1.AppInstance, pullSecrets *PullSec return nil, nil } - containers, initContainers := toContainers(appInstance, tag, name, container, interpolator, false) + containers, initContainers := toContainers(appInstance, tag, name, container, interpolator, false, addBusybox) containers = append(containers, corev1.Container{ Name: jobs.Helper, @@ -114,7 +120,7 @@ func toJob(req router.Request, appInstance *v1.AppInstance, pullSecrets *PullSec return nil, err } - volumes, err := toVolumes(appInstance, container, interpolator, false) + volumes, err := toVolumes(appInstance, container, interpolator, false, addBusybox) if err != nil { return nil, err } diff --git a/pkg/controller/appdefinition/testdata/volumes/preload/expected.golden b/pkg/controller/appdefinition/testdata/volumes/preload/expected.golden new file mode 100644 index 000000000..11cbffd17 --- /dev/null +++ b/pkg/controller/appdefinition/testdata/volumes/preload/expected.golden @@ -0,0 +1,249 @@ +`apiVersion: v1 +data: + .dockerconfigjson: eyJhdXRocyI6eyJnaGNyLmlvIjp7ImF1dGgiOiJPZz09In0sImluZGV4LmRvY2tlci5pbyI6eyJhdXRoIjoiT2c9PSJ9fX0= +kind: Secret +metadata: + creationTimestamp: null + labels: + acorn.io/managed: "true" + acorn.io/pull-secret: "true" + name: container-name-pull-1234567890ab + namespace: app-created-namespace +type: kubernetes.io/dockerconfigjson + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + acorn.io/config-hash: "" + creationTimestamp: null + labels: + acorn.io/app-name: app-name + acorn.io/app-namespace: app-namespace + acorn.io/app-public-name: app-name + acorn.io/container-name: container-name + acorn.io/managed: "true" + acorn.io/project-name: app-namespace + name: container-name + namespace: app-created-namespace + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + acorn.io/config-hash: "" + creationTimestamp: null + labels: + acorn.io/app-name: app-name + acorn.io/app-namespace: app-namespace + acorn.io/app-public-name: app-name + acorn.io/container-name: container-name + acorn.io/managed: "true" + acorn.io/project-name: app-namespace + name: container-name + namespace: app-created-namespace +spec: + replicas: 1 + selector: + matchLabels: + acorn.io/app-name: app-name + acorn.io/app-namespace: app-namespace + acorn.io/container-name: container-name + acorn.io/managed: "true" + strategy: + type: Recreate + template: + metadata: + annotations: + acorn.io/container-spec: '{"dirs":{"/foo/bar":{"preload":true,"secret":{},"subPath":"baz","volume":"testvol"},"/spam/eggs":{"secret":{},"volume":"testvol2"}},"image":"image-name","metrics":{},"probes":null}' + karpenter.sh/do-not-evict: "true" + creationTimestamp: null + labels: + acorn.io/app-name: app-name + acorn.io/app-namespace: app-namespace + acorn.io/app-public-name: app-name + acorn.io/container-name: container-name + acorn.io/managed: "true" + acorn.io/project-name: app-namespace + spec: + containers: + - image: image-name + name: container-name + resources: {} + volumeMounts: + - mountPath: /foo/bar + name: testvol + subPath: baz/data + - mountPath: /spam/eggs + name: testvol2 + enableServiceLinks: false + hostname: container-name + imagePullSecrets: + - name: container-name-pull-1234567890ab + initContainers: + - command: + - acorn-busybox-init + image: ghcr.io/acorn-io/runtime:main + name: acorn-helper-busybox-1234567890abcdef + resources: {} + volumeMounts: + - mountPath: /.acorn + name: 6c47b5c86693 + - args: + - if [ ! -f /dest/.preload-done ]; then mkdir -p /dest/data && cp -aT /foo/bar + /dest/data && date > /dest/.preload-done; fi + command: + - /.acorn/busybox + - sh + - -c + image: image-name + imagePullPolicy: IfNotPresent + name: acorn-preload-dir-a05d96ad6bf8-1234567890abcdef + resources: {} + volumeMounts: + - mountPath: /dest + name: testvol + subPath: baz + - mountPath: /.acorn + name: 6c47b5c86693 + serviceAccountName: container-name + terminationGracePeriodSeconds: 10 + volumes: + - emptyDir: {} + name: 6c47b5c86693 + - name: testvol + persistentVolumeClaim: + claimName: testvol + - name: testvol2 + persistentVolumeClaim: + claimName: testvol2 +status: {} + +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + annotations: + acorn.io/config-hash: "" + creationTimestamp: null + labels: + acorn.io/app-name: app-name + acorn.io/app-namespace: app-namespace + acorn.io/app-public-name: app-name + acorn.io/container-name: container-name + acorn.io/managed: "true" + acorn.io/project-name: app-namespace + name: container-name + namespace: app-created-namespace +spec: + maxUnavailable: 1 + selector: + matchLabels: + acorn.io/app-name: app-name + acorn.io/app-namespace: app-namespace + acorn.io/container-name: container-name + acorn.io/managed: "true" +status: + currentHealthy: 0 + desiredHealthy: 0 + disruptionsAllowed: 0 + expectedPods: 0 + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + annotations: + acorn.io/config-hash: "" + creationTimestamp: null + labels: + acorn.io/app-name: app-name + acorn.io/app-namespace: app-namespace + acorn.io/managed: "true" + acorn.io/public-name: app-name.testvol + acorn.io/volume-name: testvol + name: testvol + namespace: app-created-namespace +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10G +status: {} + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + annotations: + acorn.io/config-hash: "" + creationTimestamp: null + labels: + acorn.io/app-name: app-name + acorn.io/app-namespace: app-namespace + acorn.io/managed: "true" + acorn.io/public-name: app-name.testvol2 + acorn.io/volume-name: testvol2 + name: testvol2 + namespace: app-created-namespace +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10G +status: {} + +--- +apiVersion: internal.acorn.io/v1 +kind: AppInstance +metadata: + creationTimestamp: null + name: app-name + namespace: app-namespace + uid: 1234567890abcdef +spec: + image: test +status: + appImage: + buildContext: {} + id: test + imageData: {} + vcs: {} + appSpec: + containers: + container-name: + dirs: + /foo/bar: + preload: true + secret: {} + subPath: baz + volume: testvol + /spam/eggs: + secret: {} + volume: testvol2 + image: image-name + metrics: {} + probes: null + volumes: + testvol: {} + testvol2: {} + appStatus: {} + columns: {} + conditions: + reason: Success + status: "True" + success: true + type: defined + defaults: {} + namespace: app-created-namespace + staged: + appImage: + buildContext: {} + imageData: {} + vcs: {} + summary: {} +` diff --git a/pkg/controller/appdefinition/testdata/volumes/preload/input.yaml b/pkg/controller/appdefinition/testdata/volumes/preload/input.yaml new file mode 100644 index 000000000..a19f1271d --- /dev/null +++ b/pkg/controller/appdefinition/testdata/volumes/preload/input.yaml @@ -0,0 +1,23 @@ +kind: AppInstance +apiVersion: internal.acorn.io/v1 +metadata: + name: app-name + namespace: app-namespace + uid: 1234567890abcdef +spec: + image: test +status: + namespace: app-created-namespace + appImage: + id: test + appSpec: + containers: + container-name: + image: "image-name" + dirs: + "/foo/bar": + volume: testvol + subPath: baz + preload: true + "/spam/eggs": + volume: testvol2 diff --git a/pkg/controller/appdefinition/volume.go b/pkg/controller/appdefinition/volume.go index e585abe0c..2b8e9d0d6 100644 --- a/pkg/controller/appdefinition/volume.go +++ b/pkg/controller/appdefinition/volume.go @@ -26,9 +26,10 @@ import ( ) const ( - AcornHelper = " /acorn-helper" - AcornHelperPath = "/.acorn" - AcornHelperSleepPath = "/.acorn/sleep" + AcornHelper = " /acorn-helper" + AcornHelperPath = "/.acorn" + AcornHelperSleepPath = "/.acorn/sleep" + AcornHelperBusyboxPath = "/.acorn/busybox" ) func translateAccessModes(accessModes []v1.AccessMode) []corev1.PersistentVolumeAccessMode { @@ -294,8 +295,8 @@ func volumeLabels(appInstance *v1.AppInstance, volume string, volumeRequest v1.V volumeRequest.Labels, appInstance.Spec.Labels)) } -func isEphemeral(appInstance *v1.AppInstance, volume string) (v1.VolumeRequest, bool) { - if volume == AcornHelper && appInstance.Status.GetDevMode() { +func isEphemeral(appInstance *v1.AppInstance, volume string, addHelperVolume bool) (v1.VolumeRequest, bool) { + if volume == AcornHelper && (appInstance.Status.GetDevMode() || addHelperVolume) { return v1.VolumeRequest{ Class: v1.VolumeRequestTypeEphemeral, }, true @@ -331,6 +332,9 @@ func toVolumeName(appInstance *v1.AppInstance, volume string) (string, bool) { func addVolumeReferencesForContainer(app *v1.AppInstance, volumeReferences map[volumeReference]bool, container v1.Container) { for _, entry := range typed.Sorted(container.Dirs) { volume := entry.Value + if volume.Preload { + volumeReferences[volumeReference{name: AcornHelper}] = true + } if volume.ContextDir != "" { if app.Status.GetDevMode() { volumeReferences[volumeReference{name: AcornHelper}] = true @@ -402,7 +406,7 @@ func secretPodVolName(secretName string) string { return strings.ReplaceAll(name.SafeConcatName("secret-", secretName), ".", "-") } -func toVolumes(appInstance *v1.AppInstance, container v1.Container, interpolator *secrets.Interpolator, addWaitVolume bool) (result []corev1.Volume, _ error) { +func toVolumes(appInstance *v1.AppInstance, container v1.Container, interpolator *secrets.Interpolator, addWaitVolume, addHelperVolume bool) (result []corev1.Volume, _ error) { hasPorts := len(container.Ports) > 0 volumeReferences := map[volumeReference]bool{} addVolumeReferencesForContainer(appInstance, volumeReferences, container) @@ -430,7 +434,7 @@ func toVolumes(appInstance *v1.AppInstance, container v1.Container, interpolator } name, bind := toVolumeName(appInstance, volume.name) - if vr, ok := isEphemeral(appInstance, volume.name); ok && !bind { + if vr, ok := isEphemeral(appInstance, volume.name, addHelperVolume); ok && !bind { result = append(result, corev1.Volume{ Name: sanitizeVolumeName(volume.name), VolumeSource: corev1.VolumeSource{ diff --git a/pkg/openapi/generated/openapi_generated.go b/pkg/openapi/generated/openapi_generated.go index 1062c642d..1c1fd20a6 100644 --- a/pkg/openapi/generated/openapi_generated.go +++ b/pkg/openapi/generated/openapi_generated.go @@ -12757,6 +12757,12 @@ func schema_pkg_apis_internalacornio_v1_VolumeMount(ref common.ReferenceCallback Format: "", }, }, + "preload": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, "secret": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, diff --git a/scripts/acorn-busybox-init b/scripts/acorn-busybox-init new file mode 100755 index 000000000..84f5da2d7 --- /dev/null +++ b/scripts/acorn-busybox-init @@ -0,0 +1,2 @@ +#!/bin/sh +cp -f /bin/busybox.static /.acorn/busybox