Search code examples
pythondockerfilecode-coveragealpine-linux

Couldn't use data file .coverage: unable to open database file


A strange issue with permissions occured when pushing to GitHub. I have a test job which runs tests with coverage and then pushes results to codecov on every push and pull request. However, this scenario only works with root user.

If running with digitalshop user it throws an error:

Couldn't use data file '/digital-shop-app/.coverage': unable to open database file

My question is: how to run coverage in docker container so it won't throw this error? My guess is that it's because of permissions.

docker-compose.yml:

version: '3.9'

services:
  test:
    build: .
    command: >
      sh -c "
        python manage.py wait_for_db &&
        coverage run --source='.' manage.py test mainapp.tests &&
        coverage report &&
        coverage xml
      "
    volumes: 
      - ./digital-shop-app:/digital-shop-app
    env_file: .env
    depends_on: 
      - db

  db:
    image: postgres:13-alpine
    environment:
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASS}

Dockerfile:

FROM python:3.9-alpine3.13

ENV PYTHONUNBUFFERED 1

COPY ./requirements.txt /requirements.txt
COPY ./digital-shop-app /digital-shop-app
COPY ./scripts /scripts

WORKDIR /digital-shop-app

RUN python -m venv /py && \
    /py/bin/pip install --upgrade pip && \
    apk add --no-cache bash && \
    apk add --update --no-cache postgresql-client && \
    apk add --update --no-cache --virtual .tmp-deps \
        build-base jpeg-dev postgresql-dev musl-dev linux-headers \
        zlib-dev libffi-dev openssl-dev python3-dev cargo && \
    apk add --update --no-cache libjpeg && \
    /py/bin/pip install -r /requirements.txt && \
    apk del .tmp-deps && \
    adduser --disabled-password --no-create-home digitalshop && \
    chown -R digitalshop:digitalshop /py/lib/python3.9/site-packages && \
    chmod -R +x /scripts

ENV PATH="/scripts:/py/bin:/py/lib:$PATH"

USER digitalshop

CMD ["run.sh"]

Solution

  • So I ended up creating another Dockerfile called Dockerfile.test and putting pretty much the same configuration except non-admin user creation. Here's the final variant:

    Running code as root user is not recommended thus please read UPDATE section

    Dockerfile.test:

    FROM python:3.9-alpine3.13
    
    ENV PYTHONUNBUFFERED 1
    
    COPY ./requirements.txt /requirements.txt
    COPY ./digital-shop-app /digital-shop-app
    
    WORKDIR /digital-shop-app
    
    RUN python -m venv /py && \
        /py/bin/pip install --upgrade pip && \
        apk add --no-cache bash curl gnupg coreutils && \
        apk add --update --no-cache postgresql-client libjpeg && \
        apk add --update --no-cache --virtual .tmp-deps \
            build-base jpeg-dev postgresql-dev musl-dev linux-headers \
            zlib-dev libffi-dev openssl-dev python3-dev cargo && \
        /py/bin/pip install -r /requirements.txt && \
        apk del .tmp-deps
    
    ENV PATH="/py/bin:/py/lib:$PATH"
    

    docker-compose.yml:

    version: '3.9'
    
    services:
      test:
        build:
          context: .
          dockerfile: Dockerfile.test
        command: >
          sh -c "
            python manage.py wait_for_db &&
            coverage run --source='.' manage.py test mainapp.tests &&
            coverage report &&
            coverage xml
          "
        volumes: 
          - ./digital-shop-app:/digital-shop-app
        env_file: .env
        depends_on: 
          - db
    

    I don't know exactly whether it is a good practice. If not then please tell how to do it correctly.

    UPDATE:

    Thanks to @β.εηοιτ.βε for giving me food for thought.

    After some local debugging I found out that coverage needs user to own the directory where .coverage file is located. So I created subdir named /cov inside project folder and set digitalshop user as its owner including everything inside. Finally I specified path to .coverage file by setting env variable COVERAGE_FILE=/digital-shop-app/cov/.coverage where digital-shop-app is project root folder. And also specified the same path to coverage.xml report in docker-compose.yml. Here's the code:

    docker-compose.yml (added -o flag to coverage xml command):

    version: '3.9'
    
    services:
      test:
        build:
          context: .
        command: >
          sh -c "
            python manage.py wait_for_db &&
            coverage run --source='.' manage.py test mainapp.tests &&
            coverage xml -o /digital-shop-app/cov/coverage.xml
          "
        env_file: .env
        depends_on: 
          - db
    
      db:
        image: postgres:13-alpine
        environment:
          - POSTGRES_DB=${DB_NAME}
          - POSTGRES_USER=${DB_USER}
          - POSTGRES_PASSWORD=${DB_PASS}
    

    Dockerfile:

    FROM python:3.9-alpine3.13
    
    ENV PYTHONUNBUFFERED 1
    
    COPY ./requirements.txt /requirements.txt
    COPY ./digital-shop-app /digital-shop-app
    COPY ./scripts /scripts
    
    WORKDIR /digital-shop-app
    
    RUN python -m venv /py && \
        /py/bin/pip install --upgrade pip && \
        apk add --no-cache bash && \
        apk add --update --no-cache postgresql-client && \
        apk add --update --no-cache --virtual .tmp-deps \
            build-base jpeg-dev postgresql-dev musl-dev linux-headers \
            zlib-dev libffi-dev openssl-dev python3-dev cargo && \
        apk add --update --no-cache libjpeg && \
        /py/bin/pip install -r /requirements.txt && \
        apk del .tmp-deps && \
        adduser --disabled-password --no-create-home digitalshop && \
        chown -R digitalshop:digitalshop /py/lib/python3.9/site-packages && \
        chmod -R +x /scripts && \
        # New code here
        mkdir -p /digital-shop-app/cov && \
        chown -R digitalshop:digitalshop /digital-shop-app/cov
    
    ENV PATH="/scripts:/py/bin:/py/lib:$PATH"
    
    USER digitalshop
    
    CMD ["run.sh"]