Search code examples
dockercircleci

CircleCI Docker ECR orb ERROR: failed to solve: failed to compute cache key: "/init.sh" not found


I am utilizing CircleCI and ECR orb to build my docker image and push it to Elastic Container Registry ( ECR ) but I keep running into the following error: ERROR: failed to solve: failed to compute cache key: "/init.sh" not found.

My directory structure is as follows:

Terraform
  .circleci
  infra
  frontend
    --- Dockerfile
    --- init.sh

So basically I have a Terraform directory. Inside that directory I have an infra directory and a frontend directory. The frontend directory consists of my Dockerfile and my init.sh script.

The contents of my Dockerfile is as follows:

FROM node:14
ENV JQ_VERSION=1.6
RUN wget --no-check-certificate https://github.com/stedolan/jq/releases/download/jq-${JQ_VERSION}/jq-linux64 -O /tmp/jq-linux64
RUN cp /tmp/jq-linux64 /usr/bin/jq
RUN chmod +x /usr/bin/jq
WORKDIR /app
COPY . .
RUN jq 'to_entries | map_values({ (.key) : ("$" + .key) }) | reduce .[] as $item ({}; . + $item)' ./src/config.json > ./src/config.tmp.json && mv ./src/config.tmp.json ./src/config.json
RUN npm install && npm run build

FROM nginx:1.17
ENV JSFOLDER=/usr/share/nginx/html/js/*.js
COPY ./init.sh /usr/bin/init.sh
RUN chmod +x /usr/bin/init.sh
WORKDIR /usr/share/nginx/html
COPY --from=0 /app/dist .
ENTRYPOINT [ "init.sh" ]

and my CircleCI config has the following content:

version: '2.1'
orbs:
  aws-ecr: circleci/[email protected]
  aws-cli: circleci/[email protected]
jobs:
  build:
    machine:
      image: ubuntu-2004:2022.10.1
    steps:
      - checkout
      - aws-ecr/build-and-push-image:
          repo: frontend
          push-image: true
          region: ${AWS_REGION}
          registry-id: REGISTRY_ID
          dockerfile: frontend/Dockerfile
          path: .
          tag: ${CIRCLE_SHA1}
workflows:
  test:
    jobs:
      - build:
           

But CircleCI gives me the following error:

> [stage-1 2/5] COPY ./init.sh /usr/bin/init.sh:
------
ERROR: failed to solve: failed to compute cache key: "/init.sh" not found: not found

Exited with code exit status 1 

Solution

  • It requires reading and understanding of the error message (and yes a little bit of discipline if it is all-new as we're easily distracted in complex computer systems):

    > ERROR: failed to solve: failed to compute cache key: "/init.sh" not found: not found
    

    The message result is at the end, the operation in front, separator is :. Additionally the message is prefixed by its class (ERROR).

    More structural:

    Error: failed to solve

    1. failed to compute cache key
    2. "/init.sh" not found
    3. not found

    If you're coming from development, compare it a bit with a backtrace, but you need to throw a bit more brain on it as we don't have file paths and line numbers (not even binary offsets) here.

    For me for example, it is that 2. and 3. tell me "not found" is a file not found. As this is within Dockerfile (or Docker build) context, we can spot one operation already, as we know it involves files and a real file-system that could give such I/O errors:

    COPY ./init.sh /usr/bin/init.sh
    

    Then I'd read-up the manual for COPY 1 to understand a bit better why the message tells it is /init.sh (absolute path) while within the COPY directive it is ./init.sh (relative path). I may not understand it within three minutes therefore I skip with this knowledge and know that I only assume it is a match.

    Then given it is a match I look into its CircleCI binding:

          - aws-ecr/build-and-push-image:
              repo: frontend
              push-image: true
              region: ${AWS_REGION}
              registry-id: REGISTRY_ID
              dockerfile: frontend/Dockerfile
              path: .
              tag: ${CIRCLE_SHA1}
    

    Again, we need to get into the know and that is finding the reference for the CircleCI Orb and its command in question: aws-ecr/build-and-push-image2. Without it, we have no reference and aren't able to gather any meaning (just to guess it, but who needs brute force all the time? The strong weak!).

    As our first assumption is this is file-system related we can perhaps identify the dockerfile and path:

    dockerfile: Name of dockerfile to use. Defaults to Dockerfile.

    REQUIRED: No    DEFAULT: Dockerfile    TYPE: string
    

    path: Path to the directory containing your Dockerfile. Defaults to . (working directory).

    REQUIRED: No    DEFAULT: .             TYPE: string
    

    You then lay all those three piece of information one after another:

    ERROR: failed to solve: failed to compute cache key: "/init.sh" not found: not found
    
    COPY ./init.sh /usr/bin/init.sh
    
    dockerfile: frontend/Dockerfile
    path: .
    

    Comparing this information against the actual project file layout, the cause of error is now relatively easy to spot: there is no such file ./init.sh.

    And it is now relatively easy to bind the correct path, as we knew it from the beginning:

    COPY ./frontend/init.sh /usr/bin/init.sh
    

    Learn on how to treat error messages with respect, they have a story to tell, despite how bad/or hard to read they may appear, always remember it is the best you'll get. And I always look up the references. If I don't know where the reference is at least (and expressing that knowledge with a URL is reasonable adequate for distributed systems), I'm often doing the wrong step first. It must not that I fully understand the system, but I need to know about what I know and what I not know about so I can slow down the moving target that decided to ran away and giving us an error at least. Trouble shooting 1x1.

    And as we have all parts under version control here (if you don't, do that first), those hypertext references are easy to add to the commit message, which should document the driver of the change (the error message) and how we think the change addresses it (and c.f. Beams):

    • original error message
    • Git reference since when it was introduced
    • and fix attempt rationale

    And just a final note: When we have fixed the error, it is easy (or easier) to say what the cause was. So in the moment of the commit and putting it to test, we can only document our little understanding. However it is important to at least document it so that even steep learning curves can be taken without losing track and going in all fresh each time of an error in (remote) CI. Feedback is too often slow to waste time with staying in the unknown. You should always only apply a fix by having the rationale of it a priori, with perhaps the exclusion that if it takes longer than three minutes - which in remote CI means it must be a single commit that did run through and was tested working within that three minutes time if you want to make a good rule of thumb out of it 3.

    COPY that .

    TLDR: CircleCI Docker ECR orb ERROR: failed to solve: failed to compute cache key: "/init.sh" not found


    1. COPY Dockerfile reference https://docs.docker.com/engine/reference/builder/#copy
    2. aws-ecr/build-and-push-image CircleCI Orb https://circleci.com/developer/orbs/orb/circleci/aws-ecr#commands-build-and-push-image
    3. I sadly don't have the reference for the three minute guess-time in debugging at hand, basically how I understand it here it means you should not guess longer than only a short time (like 3 minutes) while debugging but answer all your own questions before you question the system or others that configured it (asking for help is fine, cooperation is key). Also there is the Golden Rule https://en.wikipedia.org/wiki/Golden_Rule.