File: build-images

package info (click to toggle)
charliecloud 0.43-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,116 kB
  • sloc: python: 6,021; sh: 4,284; ansic: 3,863; makefile: 598
file content (155 lines) | stat: -rwxr-xr-x 5,505 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/bin/sh

# If needed, build Dockerfiles specified by $2..n and upload it to the project
# registry. (This is used by CI jobs, not a Charliecloud test suite image.)
#
# Because of this bootstrap context, we need to run *this* job using an image
# available somewhere else. It also needs to have some image builder
# installed. GitLab recommends images provided by Docker that are very bare
# bones (Alpine with no programming language other than Busybox) [1], which is
# why this is a POSIX shell script.
#
# An alternative would be Podman images, which are Fedora based and do have a
# recent Python 3 [2].
#
# The gotcha here is that the local image builder does not know if the correct
# image is already in the registry, and it doesn’t even know the layer digests
# without building the layers, which we want to avoid. Therefore, we tag the
# images with the MD5 of the Dockerfile plus the base image digest, and build
# a new image if either is not in the registry.
#
# PREREQUISITES:
#
#   1. Log in to the gitlab.com container registry. For testing, do a manual
#      “docker login” with your username and a personal access token
#      (PAT) [3]. While your username/password will work, don’t do that
#      because Docker stores it in a plaintext file.
#
#   2. Set $CI_REGISTRY_IMAGE [4] if the default below isn’t appropriate.
#
# [1]: https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker
# [2]: https://quay.io/repository/podman/stable
# [3]: https://docs.gitlab.com/ee/user/packages/container_registry/authenticate_with_container_registry.html
# [4]: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html

set -e  # needs to be POSIX so can’t use our fancy traps

branch=$1
shift
echo "branch:        $branch"

# Convenience function for computing MD5 of a file.
md5 () {
    md5sum "$1" | cut -d' ' -f1
}

# Print the digest of the named image.
#shellcheck disable=SC2016
refhash () {
    case $1 in
    scratch)     # scratch is special
        printf '0%.0s' $(seq 1 64)
        echo
        ;;
    *':$branch')  # local file
        distro=$(echo "$1" | sed -E 's/^(\$\{.+\})?ci_(.+):\$branch$/\2/g')
        (cd "$(dirname "$0")" && md5 "$distro.df")
        ;;
    *)           # ask the registry
        # sed(1) drama is because we don’t have # any JSON parsing available.
          docker manifest inspect "$df_base" \
        | tr -d '"[:space:]' \
        | sed -E "s/^(.+)(digest:sha256:([0-9a-f]+),platform:\{architecture:${arch},)(.+)$/\3/"
        ;;
    esac
}

# Set up docker(1) alias if needed.
printf 'docker alias:  '
if docker info > /dev/null 2>&1; then
    echo 'not needed'
else
    alias docker='sudo docker'
    alias docker
fi

# Function to pass through proxy variables if needed.
build () {
    if [ -z "$HTTP_PROXY" ]; then
        docker build "$@"
    else
        docker build --build-arg HTTP_PROXY="${HTTP_PROXY:?}" \
                     --build-arg HTTPS_PROXY="${HTTPS_PROXY:?}" \
                     --build-arg NO_PROXY="${NO_PROXY:?}" \
                     --build-arg http_proxy="${http_proxy:?}" \
                     --build-arg https_proxy="${https_proxy:?}" \
                     --build-arg no_proxy="${no_proxy:?}" \
                     "$@"
    fi
}

# Registry path.
if [ -n "$CI_REGISTRY_IMAGE" ]; then
    regy=$CI_REGISTRY_IMAGE
else
    regy=registry.gitlab.com/charliecloud/charliecloud
fi
echo "registry:      $regy"

arch=$(arch.sh)
echo "architecture:  $arch"
#echo "nv distro:     $nv_distro"

for df in "$@"; do

    echo
    echo "file:          $df"
    df_hash=$(md5 "$df")
    echo "file digest:   $df_hash"
    df_base=$(sed -En 's/^FROM (.+)/\1/p' "$df")
    echo "base:          $df_base"
    printf "base digest:   "
    base_hash=$(refhash "$df_base")
    echo "$base_hash"
    digest=$(printf '%s%s' "$df_hash" "$base_hash" | md5 -)
    echo "full digest:   $digest"
    name=$(basename "$df" | sed -E 's/^(.+)\.df$/ci_\1/g')
    echo "image name:    $name"

    # Check if image exists in our registry; if not, build and push it.
    ref_digest=$regy/$name:$digest
    ref_branch=$regy/$name:$branch
    ref_latest=$regy/$name:latest
    printf 'in registry:   '
    if out=$(docker manifest inspect "$ref_digest" 2>&1); then
        echo 'yes, skipping build'
    else
        if ! ( echo "$out" | grep -Eq '^no such manifest:' ); then
            echo "bad response:" "$out" 1>&2
            exit 1
        fi
        echo 'no, building and pushing ...'
        context=$(dirname "$df")
        echo "context:   $context"
        build --pull -t "$ref_digest" -f "$df" \
              --build-arg=regy="$regy/" \
              --build-arg=branch="$branch" \
              "$context"
        docker tag "$ref_digest" "$name:$branch"  # for local use
        docker push "$ref_digest"
    fi

    # Re-tag in registry. Do this unconditionally because it’s fast and then we
    # don’t have to worry about whether the previous iteration of the script
    # worked correctly. Use Docker [1] rather than Skopeo because it’s already
    # installed.
    #
    # This does not completely eliminate the race condition in #1981, but now only
    # commits within the same branch will be racing, which seems acceptable.
    #
    # [1]: https://stackoverflow.com/questions/26763427#70526615
    echo 'tagging ...'
    docker buildx imagetools create "$ref_digest" --tag "$ref_branch"
    docker buildx imagetools create "$ref_digest" --tag "$ref_latest"

done