diff --git a/Dockerfile.windows b/Dockerfile.windows new file mode 100644 index 000000000000..8abab1947570 --- /dev/null +++ b/Dockerfile.windows @@ -0,0 +1,72 @@ +#################################################################################################### +# Builder image +# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image +# Also used as the image in CI jobs so needs all dependencies +#################################################################################################### +# had issues with official golange image for windows so I'm using plain servercore +FROM mcr.microsoft.com/windows/servercore:ltsc2019 as builder +ENV GOLANG_VERSION=1.13.4 +SHELL ["powershell", "-Command"] + +ARG IMAGE_OS=windows +ARG IMAGE_ARCH=amd64 + +# install chocolatey package manager +ENV chocolateyUseWindowsCompression=false +RUN iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1')); \ + choco feature disable --name showDownloadProgress ; \ + choco feature enable -n allowGlobalConfirmation + +# install golang, dep and other tools +RUN choco install golang --version=$env:GOLANG_VERSION ; \ + choco install make dep docker-cli git.portable + +#################################################################################################### +# argoexec-base +# Used as the base for both the release and development version of argoexec +#################################################################################################### +FROM mcr.microsoft.com/windows/nanoserver:1809 as argoexec-base +COPY --from=builder /windows/system32/netapi32.dll /windows/system32/netapi32.dll + +ARG IMAGE_OS=windows +ARG IMAGE_ARCH=amd64 + +# NOTE: keep the version synced with https://storage.googleapis.com/kubernetes-release/release/stable.txt +ENV KUBECTL_VERSION=1.15.1 +ENV JQ_VERSION=1.6 + +RUN mkdir C:\app && \ + curl -L -o C:\app\kubectl.exe "https://storage.googleapis.com/kubernetes-release/release/v%KUBECTL_VERSION%/bin/windows/amd64/kubectl.exe" && \ + curl -L -o C:\app\jq.exe "https://github.com/stedolan/jq/releases/download/jq-%JQ_VERSION%/jq-win64.exe" + +COPY --from=builder C:/ProgramData/chocolatey/lib/docker-cli/tools/docker.exe C:/app/docker.exe +COPY --from=builder C:/tools/git C:/app/git + +# add binaries to path +USER Administrator +RUN SETX /m path C:\app;C:\app\git\bin;%path% + +#################################################################################################### +# Argo Build stage which performs the actual build of Argo binaries +#################################################################################################### +FROM builder as argo-build + +ARG IMAGE_OS=windows +ARG IMAGE_ARCH=amd64 + +# Perform the build +WORKDIR C:/Users/ContainerAdministrator/go/src/github.com/argoproj/argo +COPY . . +# check we can use Git +RUN git rev-parse HEAD +# fail the build if we are "dirty" +RUN git diff --exit-code +# run in git bash for all the shell commands in Makefile to work +RUN bash -c 'make dist/argoexec-windows-amd64' + +#################################################################################################### +# argoexec +#################################################################################################### +FROM argoexec-base as argoexec +COPY --from=argo-build C:/Users/ContainerAdministrator/go/src/github.com/argoproj/argo/dist/argoexec-windows-amd64 C:/app/argoexec.exe +ENTRYPOINT [ "argoexec" ] \ No newline at end of file diff --git a/Makefile b/Makefile index ab7e5d9f34b4..afe09a3babc2 100644 --- a/Makefile +++ b/Makefile @@ -206,6 +206,7 @@ $(CONTROLLER_IMAGE_FILE): # argoexec dist/argoexec-linux-amd64: GOARGS = GOOS=linux GOARCH=amd64 +dist/argoexec-windows-amd64: GOARGS = GOOS=windows GOARCH=amd64 dist/argoexec-linux-arm64: GOARGS = GOOS=linux GOARCH=arm64 dist/argoexec-%: $(ARGOEXEC_PKGS) diff --git a/docs/README.md b/docs/README.md index 9c6bfde7715e..a9e3e59c2420 100644 --- a/docs/README.md +++ b/docs/README.md @@ -41,6 +41,7 @@ Some use-case specific documentation is available: * [Transport Layer Security](tls.md) * [Workflow Variables](variables.md) * [Versioning](versioning.md) +* [Windows Container Support](windows.md) * [Workflow Archive](workflow-archive.md) * [Workflow Controller Configmap](workflow-controller-configmap.md) * [Workflow Creator](workflow-creator.md) diff --git a/docs/fields.md b/docs/fields.md index 002db876e350..604a129ef377 100644 --- a/docs/fields.md +++ b/docs/fields.md @@ -82,6 +82,10 @@ Workflow is the definition of a workflow resource - [`hdfs-artifact.yaml`](../examples/hdfs-artifact.yaml) +- [`hello-hybrid.yaml`](../examples/hello-hybrid.yaml) + +- [`hello-windows.yaml`](../examples/hello-windows.yaml) + - [`hello-world.yaml`](../examples/hello-world.yaml) - [`image-pull-secrets.yaml`](../examples/image-pull-secrets.yaml) @@ -357,6 +361,10 @@ WorkflowSpec is the specification of a Workflow. - [`hdfs-artifact.yaml`](../examples/hdfs-artifact.yaml) +- [`hello-hybrid.yaml`](../examples/hello-hybrid.yaml) + +- [`hello-windows.yaml`](../examples/hello-windows.yaml) + - [`hello-world.yaml`](../examples/hello-world.yaml) - [`image-pull-secrets.yaml`](../examples/image-pull-secrets.yaml) @@ -645,6 +653,10 @@ CronWorkflowSpec is the specification of a CronWorkflow - [`hdfs-artifact.yaml`](../examples/hdfs-artifact.yaml) +- [`hello-hybrid.yaml`](../examples/hello-hybrid.yaml) + +- [`hello-windows.yaml`](../examples/hello-windows.yaml) + - [`hello-world.yaml`](../examples/hello-world.yaml) - [`image-pull-secrets.yaml`](../examples/image-pull-secrets.yaml) @@ -899,6 +911,10 @@ WorkflowTemplateSpec is a spec of WorkflowTemplate. - [`hdfs-artifact.yaml`](../examples/hdfs-artifact.yaml) +- [`hello-hybrid.yaml`](../examples/hello-hybrid.yaml) + +- [`hello-windows.yaml`](../examples/hello-windows.yaml) + - [`hello-world.yaml`](../examples/hello-world.yaml) - [`image-pull-secrets.yaml`](../examples/image-pull-secrets.yaml) @@ -1347,6 +1363,10 @@ Template is a reusable and composable unit of execution in a workflow - [`hdfs-artifact.yaml`](../examples/hdfs-artifact.yaml) +- [`hello-hybrid.yaml`](../examples/hello-hybrid.yaml) + +- [`hello-windows.yaml`](../examples/hello-windows.yaml) + - [`hello-world.yaml`](../examples/hello-world.yaml) - [`image-pull-secrets.yaml`](../examples/image-pull-secrets.yaml) @@ -2185,6 +2205,10 @@ Pod metdata - [`hdfs-artifact.yaml`](../examples/hdfs-artifact.yaml) +- [`hello-hybrid.yaml`](../examples/hello-hybrid.yaml) + +- [`hello-windows.yaml`](../examples/hello-windows.yaml) + - [`hello-world.yaml`](../examples/hello-world.yaml) - [`image-pull-secrets.yaml`](../examples/image-pull-secrets.yaml) @@ -2520,6 +2544,8 @@ WorkflowStep is a reference to a template to execute in a series of step - [`hdfs-artifact.yaml`](../examples/hdfs-artifact.yaml) +- [`hello-hybrid.yaml`](../examples/hello-hybrid.yaml) + - [`influxdb-ci.yaml`](../examples/influxdb-ci.yaml) - [`k8s-orchestration.yaml`](../examples/k8s-orchestration.yaml) @@ -3248,6 +3274,10 @@ ObjectMeta is metadata that all persisted resources must have, which includes al - [`hdfs-artifact.yaml`](../examples/hdfs-artifact.yaml) +- [`hello-hybrid.yaml`](../examples/hello-hybrid.yaml) + +- [`hello-windows.yaml`](../examples/hello-windows.yaml) + - [`hello-world.yaml`](../examples/hello-world.yaml) - [`image-pull-secrets.yaml`](../examples/image-pull-secrets.yaml) @@ -3676,6 +3706,10 @@ A single application container that you want to run within a pod. - [`hdfs-artifact.yaml`](../examples/hdfs-artifact.yaml) +- [`hello-hybrid.yaml`](../examples/hello-hybrid.yaml) + +- [`hello-windows.yaml`](../examples/hello-windows.yaml) + - [`hello-world.yaml`](../examples/hello-world.yaml) - [`image-pull-secrets.yaml`](../examples/image-pull-secrets.yaml) @@ -4250,6 +4284,10 @@ PersistentVolumeClaimSpec describes the common attributes of storage devicesand - [`hdfs-artifact.yaml`](../examples/hdfs-artifact.yaml) +- [`hello-hybrid.yaml`](../examples/hello-hybrid.yaml) + +- [`hello-windows.yaml`](../examples/hello-windows.yaml) + - [`hello-world.yaml`](../examples/hello-world.yaml) - [`image-pull-secrets.yaml`](../examples/image-pull-secrets.yaml) diff --git a/docs/windows.md b/docs/windows.md new file mode 100644 index 000000000000..bc7390ead0ff --- /dev/null +++ b/docs/windows.md @@ -0,0 +1,103 @@ +# Windows Container Support + +The Argo server and the workflow controller currently only run on Linux. The workflow executor however also runs on Windows nodes, meaning you can use Windows containers inside your workflows! Here are the steps to get started. + +## 0. Requirements +* Kubernetes 1.14 or later, supporting Windows nodes +* Hybrid cluster containing Linux and Windows nodes like described in the [Kubernetes docs](https://kubernetes.io/docs/setup/production-environment/windows/user-guide-windows-containers/) +* Argo configured and running like described [here](getting-started.md) + +## 1. Setting up the workflow executor + +Currently the worflow controller configuration doesn't support different configurations for the `dockerSockPath` based on the host OS. This means that the workflow executor, running in a Windows container can't use Docker for now. + +You therefore need to use `kubelet` or `k8sapi` instead in your workflow controller configmap: +```yaml +containerRuntimeExecutor: kubelet +kubeletInsecure: true # you can disable TLS verification of the kubelet executor for testing +``` + +## 2. Schedule workflows with Windows containers + +If you're running workflows in your hybrid Kubernetes cluster, always make sure to include a `nodeSelector` to run the steps on the correct host OS: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: hello-windows- +spec: + entrypoint: hello-win + templates: + - name: hello-win + nodeSelector: + kubernetes.io/os: windows # specify the OS your step should run on + container: + image: mcr.microsoft.com/windows/nanoserver:1809 + command: ["cmd", "/c"] + args: ["echo", "Hello from Windows Container!"] +``` + +You can run this example and get the logs: +``` +$ argo submit --watch https://raw.githubusercontent.com/argoproj/argo/master/examples/hello-windows.yaml +$ argo logs hello-windows-s9kk5 +hello-windows-s9kk5: "Hello from Windows Container!" +``` + +## Bonus: Hybrid workflows + +You can also run different steps on different host OSs. This can for example be very helpful when you need to compile your application on Windows and Linux. + +An example workflow can look like the following: +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: hello-hybrid- +spec: + entrypoint: mytemplate + templates: + - name: mytemplate + steps: + - - name: step1 + template: hello-win + - - name: step2 + template: hello-linux + + - name: hello-win + nodeSelector: + kubernetes.io/os: windows + container: + image: mcr.microsoft.com/windows/nanoserver:1809 + command: ["cmd", "/c"] + args: ["echo", "Hello from Windows Container!"] + - name: hello-linux + nodeSelector: + beta.kubernetes.io/os: linux + container: + image: alpine + command: [echo] + args: ["Hello from Linux Container!"] + +``` + +Again, you can run this example and get the logs: +``` +$ argo submit --watch https://raw.githubusercontent.com/argoproj/argo/master/examples/hello-hybrid.yaml +$ argo logs hello-hybrid-plqpp +hello-hybrid-plqpp-1977432187: "Hello from Windows Container!" +hello-hybrid-plqpp-764774907: Hello from Linux Container! +``` + +## Building the workflow executor image for Windows + +To build the workflow executor image for Windows you need a Windows machine running Windows Server 2019 with Docker installed like described [in the docs](https://docs.docker.com/ee/docker-ee/windows/docker-ee/#install-docker-engine---enterprise). + +You then clone the project and run the Docker build with the Dockerfile for Windows and `argoexec` as a target: + +``` +git clone https://github.com/argoproj/argo.git +cd argo +docker build -t myargoexec -f .\Dockerfile.windows --target argoexec . +``` \ No newline at end of file diff --git a/docs/workflow-controller-configmap.md b/docs/workflow-controller-configmap.md index 272844539ab2..4eca50a55ba3 100644 --- a/docs/workflow-controller-configmap.md +++ b/docs/workflow-controller-configmap.md @@ -37,6 +37,8 @@ spec: image: argoproj/workflow-controller:latest name: workflow-controller serviceAccountName: argo + nodeSelector: + kubernetes.io/os: linux ``` ## Alternate Structure diff --git a/examples/hello-hybrid.yaml b/examples/hello-hybrid.yaml new file mode 100644 index 000000000000..381b5a36b4ec --- /dev/null +++ b/examples/hello-hybrid.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: hello-hybrid- +spec: + entrypoint: mytemplate + templates: + - name: mytemplate + steps: + - - name: step1 + template: hello-win + - - name: step2 + template: hello-linux + + - name: hello-win + nodeSelector: + kubernetes.io/os: windows + container: + image: mcr.microsoft.com/windows/nanoserver:1809 + command: ["cmd", "/c"] + args: ["echo", "Hello from Windows Container!"] + - name: hello-linux + nodeSelector: + beta.kubernetes.io/os: linux + container: + image: alpine + command: [echo] + args: ["Hello from Linux Container!"] diff --git a/examples/hello-windows.yaml b/examples/hello-windows.yaml new file mode 100644 index 000000000000..382c797dfa2c --- /dev/null +++ b/examples/hello-windows.yaml @@ -0,0 +1,14 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: hello-windows- +spec: + entrypoint: hello-win + templates: + - name: hello-win + nodeSelector: + kubernetes.io/os: windows + container: + image: mcr.microsoft.com/windows/nanoserver:1809 + command: ["cmd", "/c"] + args: ["echo", "Hello from Windows Container!"] \ No newline at end of file diff --git a/manifests/base/argo-server/argo-server-deployment.yaml b/manifests/base/argo-server/argo-server-deployment.yaml index eed6dff4da5c..dbafbfd8829a 100644 --- a/manifests/base/argo-server/argo-server-deployment.yaml +++ b/manifests/base/argo-server/argo-server-deployment.yaml @@ -26,3 +26,5 @@ spec: path: / initialDelaySeconds: 10 periodSeconds: 20 + nodeSelector: + kubernetes.io/os: linux diff --git a/manifests/base/workflow-controller/workflow-controller-deployment.yaml b/manifests/base/workflow-controller/workflow-controller-deployment.yaml index 4c96b0e8e286..d33a90a43da0 100644 --- a/manifests/base/workflow-controller/workflow-controller-deployment.yaml +++ b/manifests/base/workflow-controller/workflow-controller-deployment.yaml @@ -22,3 +22,5 @@ spec: - workflow-controller-configmap - --executor-image - argoproj/argoexec:latest + nodeSelector: + kubernetes.io/os: linux \ No newline at end of file diff --git a/manifests/install.yaml b/manifests/install.yaml index 0e1579eba191..86a556039422 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -407,6 +407,8 @@ spec: scheme: HTTP initialDelaySeconds: 10 periodSeconds: 20 + nodeSelector: + kubernetes.io/os: linux serviceAccountName: argo-server --- apiVersion: apps/v1 @@ -432,4 +434,6 @@ spec: - workflow-controller image: argoproj/workflow-controller:latest name: workflow-controller + nodeSelector: + kubernetes.io/os: linux serviceAccountName: argo diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 9a9b77f4fe1e..47f2b70be6bb 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -306,6 +306,8 @@ spec: scheme: HTTP initialDelaySeconds: 10 periodSeconds: 20 + nodeSelector: + kubernetes.io/os: linux serviceAccountName: argo-server --- apiVersion: apps/v1 @@ -332,4 +334,6 @@ spec: - workflow-controller image: argoproj/workflow-controller:latest name: workflow-controller + nodeSelector: + kubernetes.io/os: linux serviceAccountName: argo diff --git a/manifests/quick-start-mysql.yaml b/manifests/quick-start-mysql.yaml index 37845a837bd5..fc809cfdd3b4 100644 --- a/manifests/quick-start-mysql.yaml +++ b/manifests/quick-start-mysql.yaml @@ -493,6 +493,8 @@ spec: scheme: HTTP initialDelaySeconds: 10 periodSeconds: 20 + nodeSelector: + kubernetes.io/os: linux serviceAccountName: argo-server --- apiVersion: apps/v1 @@ -537,6 +539,8 @@ spec: - SELECT 1 initialDelaySeconds: 15 timeoutSeconds: 2 + nodeSelector: + kubernetes.io/os: linux --- apiVersion: apps/v1 kind: Deployment @@ -562,6 +566,8 @@ spec: - workflow-controller image: argoproj/workflow-controller:latest name: workflow-controller + nodeSelector: + kubernetes.io/os: linux serviceAccountName: argo --- apiVersion: v1 diff --git a/manifests/quick-start-no-db.yaml b/manifests/quick-start-no-db.yaml index 3cac48aa8519..490448cc7535 100644 --- a/manifests/quick-start-no-db.yaml +++ b/manifests/quick-start-no-db.yaml @@ -450,6 +450,8 @@ spec: scheme: HTTP initialDelaySeconds: 10 periodSeconds: 20 + nodeSelector: + kubernetes.io/os: linux serviceAccountName: argo-server --- apiVersion: apps/v1 @@ -476,6 +478,8 @@ spec: - workflow-controller image: argoproj/workflow-controller:latest name: workflow-controller + nodeSelector: + kubernetes.io/os: linux serviceAccountName: argo --- apiVersion: v1 diff --git a/manifests/quick-start-postgres.yaml b/manifests/quick-start-postgres.yaml index 228c9140a3ef..d89f7231792c 100644 --- a/manifests/quick-start-postgres.yaml +++ b/manifests/quick-start-postgres.yaml @@ -493,6 +493,8 @@ spec: scheme: HTTP initialDelaySeconds: 10 periodSeconds: 20 + nodeSelector: + kubernetes.io/os: linux serviceAccountName: argo-server --- apiVersion: apps/v1 @@ -529,6 +531,8 @@ spec: - SELECT 1 initialDelaySeconds: 15 timeoutSeconds: 2 + nodeSelector: + kubernetes.io/os: linux --- apiVersion: apps/v1 kind: Deployment @@ -554,6 +558,8 @@ spec: - workflow-controller image: argoproj/workflow-controller:latest name: workflow-controller + nodeSelector: + kubernetes.io/os: linux serviceAccountName: argo --- apiVersion: v1 diff --git a/manifests/quick-start/mysql/mysql-deployment.yaml b/manifests/quick-start/mysql/mysql-deployment.yaml index 7d4e9ec23379..4013f94134ed 100644 --- a/manifests/quick-start/mysql/mysql-deployment.yaml +++ b/manifests/quick-start/mysql/mysql-deployment.yaml @@ -32,4 +32,6 @@ spec: exec: command: ["mysql", "-u", "mysql", "-ppassword", "argo", "-e", "SELECT 1"] initialDelaySeconds: 15 - timeoutSeconds: 2 \ No newline at end of file + timeoutSeconds: 2 + nodeSelector: + kubernetes.io/os: linux \ No newline at end of file diff --git a/manifests/quick-start/postgres/postgres-deployment.yaml b/manifests/quick-start/postgres/postgres-deployment.yaml index bc770295b759..c22a50091967 100644 --- a/manifests/quick-start/postgres/postgres-deployment.yaml +++ b/manifests/quick-start/postgres/postgres-deployment.yaml @@ -26,4 +26,6 @@ spec: exec: command: ["psql", "-U", "postgres", "-c", "SELECT 1"] initialDelaySeconds: 15 - timeoutSeconds: 2 \ No newline at end of file + timeoutSeconds: 2 + nodeSelector: + kubernetes.io/os: linux \ No newline at end of file diff --git a/workflow/executor/executor.go b/workflow/executor/executor.go index da4eb20f4472..b66bf556f06a 100644 --- a/workflow/executor/executor.go +++ b/workflow/executor/executor.go @@ -17,7 +17,6 @@ import ( "path/filepath" "runtime/debug" "strings" - "syscall" "time" argofile "github.com/argoproj/pkg/file" @@ -36,6 +35,7 @@ import ( "github.com/argoproj/argo/util/retry" artifact "github.com/argoproj/argo/workflow/artifacts" "github.com/argoproj/argo/workflow/common" + os_specific "github.com/argoproj/argo/workflow/executor/os-specific" ) const ( @@ -963,7 +963,7 @@ func (we *WorkflowExecutor) monitorAnnotations(ctx context.Context) <-chan struc // directly from kubernetes API. The controller uses this to fast-track notification of annotations // instead of waiting for the volume file to get updated (which can take minutes) sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGUSR2) + signal.Notify(sigs, os_specific.GetOsSignal()) we.setExecutionControl() diff --git a/workflow/executor/os-specific/chroot_linux.go b/workflow/executor/os-specific/chroot_linux.go new file mode 100644 index 000000000000..27df89668d6c --- /dev/null +++ b/workflow/executor/os-specific/chroot_linux.go @@ -0,0 +1,8 @@ +package os_specific + +import "syscall" + +func CallChroot() error { + err := syscall.Chroot(".") + return err +} diff --git a/workflow/executor/os-specific/chroot_windows.go b/workflow/executor/os-specific/chroot_windows.go new file mode 100644 index 000000000000..de591ba539a2 --- /dev/null +++ b/workflow/executor/os-specific/chroot_windows.go @@ -0,0 +1,5 @@ +package os_specific + +func CallChroot() error { + return nil // no chroot on windows +} diff --git a/workflow/executor/os-specific/signal_linux.go b/workflow/executor/os-specific/signal_linux.go new file mode 100644 index 000000000000..5ff0dc38b5d1 --- /dev/null +++ b/workflow/executor/os-specific/signal_linux.go @@ -0,0 +1,10 @@ +package os_specific + +import ( + "os" + "syscall" +) + +func GetOsSignal() os.Signal { + return syscall.SIGUSR2 +} diff --git a/workflow/executor/os-specific/signal_windows.go b/workflow/executor/os-specific/signal_windows.go new file mode 100644 index 000000000000..bc06d883d77e --- /dev/null +++ b/workflow/executor/os-specific/signal_windows.go @@ -0,0 +1,10 @@ +package os_specific + +import ( + "os" + "syscall" +) + +func GetOsSignal() os.Signal { + return syscall.SIGINT +} diff --git a/workflow/executor/pns/pns.go b/workflow/executor/pns/pns.go index 528942e6cfee..14fdd9693e07 100644 --- a/workflow/executor/pns/pns.go +++ b/workflow/executor/pns/pns.go @@ -22,6 +22,7 @@ import ( "github.com/argoproj/argo/util/archive" "github.com/argoproj/argo/workflow/common" execcommon "github.com/argoproj/argo/workflow/executor/common" + os_specific "github.com/argoproj/argo/workflow/executor/os-specific" ) type PNSExecutor struct { @@ -97,7 +98,7 @@ func (p *PNSExecutor) enterChroot() error { if err := p.mainFS.Chdir(); err != nil { return errors.InternalWrapErrorf(err, "failed to chdir to main filesystem: %v", err) } - err := syscall.Chroot(".") + err := os_specific.CallChroot() if err != nil { return errors.InternalWrapErrorf(err, "failed to chroot to main filesystem: %v", err) } @@ -109,7 +110,7 @@ func (p *PNSExecutor) exitChroot() error { if err := p.rootFS.Chdir(); err != nil { return errors.InternalWrapError(err) } - err := syscall.Chroot(".") + err := os_specific.CallChroot() if err != nil { return errors.InternalWrapError(err) }