11
votes

I have yet another issue with permissions running Docker through Jenkins declarative pipeline. I want to build and publish a Python package through a Jenkins job in a Docker container:

pipeline {

  agent {
    docker {
      image 'python:3.7'
      label 'docker && linux'
    }
  }

  environment {
    PACKAGE_VERSION = readFile 'VERSION'
  }

  stages {

    stage('Package') {
      steps {
        sh 'python -V'
        sh 'python -m pip install -r requirements.txt --user --no-cache'
        sh 'python setup.py sdist'
      }
    }

    stage('Deploy') {
      steps {
        ...
      }
    }

  }

  post {
    always {
      cleanWs()
    }
  }

}

However, I am not allowed to pip install due to a PermissionError:

+python -m pip install -r requirements.txt --user --no-cache Requirement already satisfied: setuptools in /usr/local/lib/python3.7/site-packages (from -r requirements.txt (line 1)) (40.0.0) Collecting pytest (from -r requirements.txt (line 2))
Downloading https://files.pythonhosted.org/packages/9e/a1/8166a56ce9d89fdd9efcae5601e71758029d90e5644e0b7b6eda07e67c35/pytest-3.7.0-py2.py3-none-any.whl (202kB) Collecting py>=1.5.0 (from pytest->-r requirements.txt (line 2)) Downloading https://files.pythonhosted.org/packages/f3/bd/83369ff2dee18f22f27d16b78dd651e8939825af5f8b0b83c38729069962/py-1.5.4-py2.py3-none-any.whl (83kB) Collecting more-itertools>=4.0.0 (from pytest->-r requirements.txt (line 2)) Downloading https://files.pythonhosted.org/packages/79/b1/eace304ef66bd7d3d8b2f78cc374b73ca03bc53664d78151e9df3b3996cc/more_itertools-4.3.0-py3-none-any.whl (48kB) Collecting pluggy>=0.7 (from pytest->-r requirements.txt (line 2)) Downloading https://files.pythonhosted.org/packages/f5/f1/5a93c118663896d83f7bcbfb7f657ce1d0c0d617e6b4a443a53abcc658ca/pluggy-0.7.1-py2.py3-none-any.whl Collecting six>=1.10.0 (from pytest->-r requirements.txt (line 2))
Downloading https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl Collecting atomicwrites>=1.0 (from pytest->-r requirements.txt (line 2)) Downloading https://files.pythonhosted.org/packages/0a/e8/cd6375e7a59664eeea9e1c77a766eeac0fc3083bb958c2b41ec46b95f29c/atomicwrites-1.1.5-py2.py3-none-any.whl Collecting attrs>=17.4.0 (from pytest->-r requirements.txt (line 2))
Downloading https://files.pythonhosted.org/packages/41/59/cedf87e91ed541be7957c501a92102f9cc6363c623a7666d69d51c78ac5b/attrs-18.1.0-py2.py3-none-any.whl Installing collected packages: py, six, more-itertools, pluggy, atomicwrites, attrs, pytest

Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: '/.local' Check the permissions.

How do I fix these permissions?

4
Do not use --user since it looks like you're already root in the container. --user is for unprivileged installs.hoefling
You can of course run the agent as root by adding args '--user 0:0' to the docker declaration, but it would be interesting to figure out what's wrong with the default setup and pip installing with --user flag.hoefling
Jenkins added -u 1001:1001 and it's true that overruling this with -u 0:0 solves the issue. But then it doesn't make sense to me that pip install ... --user does not work. Could permissions in the docker image be faulty set up?casparjespersen
Yeah, it kinda makes sense. The problem is that the python:3.7 image knows nothing about the user with ID 1001 - if you run cat /etc/passwd | grep 1001 inside the container, nothing will be found. Jenkins starts the container as a user that does not exist, no entry in passwd, no home dir etc. I assume that HOME is left to / in that case, so pip install --user resorts to installing to /.local which 1. doesn't exist and if it would 2. would belong to root anyway.hoefling
I guess you have to either run the container as root (yuck! however, depends on what the container is used for - if it's disposed after build, it may be harmless to run it as root), or write your own Dockerfile inheriting from python:3.7 that adds a proper non-root user. Then pass this user to docker agent in Jenkinsfile.hoefling

4 Answers

10
votes

I have found what I myself would think is the prettier solution:

stage("Python Test") {
  agent { 
    docker {
      label "docker && linux" 
      image "python:3.7"
    }
  }
  steps {
    withEnv(["HOME=${env.WORKSPACE}"]) {
      sh "pip install -r requirements.txt --user"
      # python stuff
    }
  }
  post {
    cleanup {
      cleanWs()
    }
  }
}

This workaround steers completely around the issue itself, installing the packages at user level. The issue here was that the HOME-directory was not initially writeable either, thus overwriting the HOME directory.

4
votes

I had a very similar pipeline that I was running right after setting up Docker agents on my Jenkins system, so I thought my setup was wrong. Using the comments in your thread, I cooked up this solution:

First, you'll need to be root inside your container, so change your agent declaration to be similar to this:

agent {
    docker {
        image "python:3.7"
        args '--user 0:0'
    }
}

Now I was able to use pip install! However, subsequent runs of the job would try to run git clean and fail since the built files inside the container were created by root. To fix that, I ran the clean command inside the container as my last step:

steps {
    sh 'git clean -fdx'
}

Update:

I found a problem where a failed build wouldn't clean up and killed all of the builds after it. To fix this, I put the clean action as a post-build task that always runs:

post {
    cleanup {
        cleanWs()
    }
}
0
votes

withEnv(["HOME=${env.WORKSPACE}"]) {} is the key. Also, it can be used like:

node {
    def customImage = docker.build(...)
    customImage.inside {
        withEnv(["HOME=${env.WORKSPACE}"]) {
            sh '...'
        }
    }
}
-1
votes

You can try executing it as sudo:

 stage('Package') {
      steps {
        sh '''
            python -V
            sudo python -m pip install -r requirements.txt --user --no-cache
            sudo python setup.py sdist
           '''
      }
    }

You may have issues due Jenkins is not able to run command as sudo in that case I will recommend you to follow the steps mentioned in this article or in this SO question