Update rke, kubectl and helm
[oom/offline-installer.git] / build / build_nexus_blob.sh
1 #! /usr/bin/env bash
2
3 #   COPYRIGHT NOTICE STARTS HERE
4 #
5 #   Copyright 2018-2019 © Samsung Electronics Co., Ltd.
6 #
7 #   Licensed under the Apache License, Version 2.0 (the "License");
8 #   you may not use this file except in compliance with the License.
9 #   You may obtain a copy of the License at
10 #
11 #       http://www.apache.org/licenses/LICENSE-2.0
12 #
13 #   Unless required by applicable law or agreed to in writing, software
14 #   distributed under the License is distributed on an "AS IS" BASIS,
15 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 #   See the License for the specific language governing permissions and
17 #   limitations under the License.
18 #
19 #   COPYRIGHT NOTICE ENDS HERE
20
21 ### This script prepares Nexus repositories data blobs for ONAP
22
23 ## The script requires following dependencies are installed: nodejs, jq, docker, twine, expect
24 ## All required resources are expected in the upper directory created during
25 ## download procedure as DATA_DIR or in the directory given as --input-directory
26 ## All lists used must be in project data_lists directory or in the directory given
27 ## as --resource-list-directory
28
29 # Fail fast settings
30 set -e
31
32 TIMESTAMP="date +'%Y-%m-%d_%H-%M-%S'"
33 SCRIPT_LOG="/tmp/$(basename $0)_$(eval ${TIMESTAMP}).log"
34
35 # Log everything
36 exec &> >(tee -a "${SCRIPT_LOG}")
37
38 # Nexus repository location
39 NEXUS_DOMAIN="nexus"
40 NEXUS_PORT="8081"
41 NEXUS_DOCKER_PORT="8082"
42 NPM_REGISTRY="http://${NEXUS_DOMAIN}:${NEXUS_PORT}/repository/npm-private/"
43 PYPI_REGISTRY="http://${NEXUS_DOMAIN}:${NEXUS_PORT}/repository/pypi-private/"
44 DOCKER_REGISTRY="${NEXUS_DOMAIN}:${NEXUS_DOCKER_PORT}"
45 DEFAULT_REGISTRY="docker.io"
46
47 # Nexus repository credentials
48 NEXUS_USERNAME=admin
49 NEXUS_PASSWORD=admin123
50 NEXUS_EMAIL=admin@example.org
51
52 # Setting paths
53 LOCAL_PATH="$(readlink -f $(dirname ${0}))"
54
55 # Defaults
56 DOCKER_LOAD="false"
57 NPM_PUSH="false"
58 PYPI_PUSH="false"
59 DATA_DIR="$(realpath ${LOCAL_PATH}/../../resources)"
60 NEXUS_DATA_DIR="${DATA_DIR}/nexus_data"
61 LISTS_DIR="${LOCAL_PATH}/data_lists"
62
63 # Required dependencies
64 COMMANDS=(jq docker)
65
66 usage () {
67     echo "
68     Usage: $(basename $0) [OPTION...] [FILE]...
69
70     This script prepares Nexus repositories data blobs for ONAP
71
72     Following dependencies are required: nodejs, jq, docker, twine, expect
73     By default, without any lists or dirs provided, the resources are expected as downloaded
74     during download process and default lists will be used to build the Nexus blob in the same
75     resources dir
76
77     Examples:
78         $(basename $0) --input-directory </path/to/downloaded/files/dir> -ld --output-directory
79            </path/to/output/dir> --resource-list-directory </path/to/dir/with/resource/list>
80            # Docker images, npms and pypi packages will be loaded from specified directory
81            # and the blob is created
82         $(basename $0) -d </path/to/docker/images/list> -d </path/to/another/docker/images/list>
83         -n </path/to/npm/list> -p </path/to/pip/list>
84            # Docker images, npms and pypi packages will be pushed to Nexus based and provided data
85            # lists (multiple lists can be provided)
86
87      -d  | --docker                     use specific list of docker images to be pushed into Nexus
88                                         (in case of -ld used, this list will be used for loading of
89                                         the images)
90      -h  | --help                       print this usage
91      -i  | --input-directory            use specific directory containing resources needed to
92                                         create nexus blob
93                                         The structure of this directory must organized as described
94                                         in build guide
95      -ld | --load-docker-images         load docker images from resource directory
96      -n  | --npm                        list of npm packages to be pushed into Nexus
97      -o  | --output-directory           use specific directory for the target blob
98      -p  | --pypi                       use specific list of pypi packages to be pushed into Nexus
99      -rl | --resource-list-directory    use specific directory with docker, pypi and npm lists
100     "
101     exit 1
102 }
103
104 publish_ports () {
105     for REGISTRY in $(sed -n '/\.[^/].*\//p' ${1} | sed -e 's/\/.*$//' | sort -u | grep -v ${DEFAULT_REGISTRY} || true) ${NEXUS_PORT}; do
106         if [[ ${REGISTRY} != *":"* ]]; then
107             if [[ ${PUBLISHED_PORTS} != *"80:${NEXUS_DOCKER_PORT}"* ]]; then
108                 PUBLISHED_PORTS="${PUBLISHED_PORTS} -p 80:${NEXUS_DOCKER_PORT}"
109             fi
110         else
111             REGISTRY_PORT="$(sed 's/^.*\:\([[:digit:]]*\)$/\1/' <<< ${REGISTRY})"
112             if [[ ${PUBLISHED_PORTS} != *"${REGISTRY_PORT}:${NEXUS_DOCKER_PORT}"* ]]; then
113                 PUBLISHED_PORTS="${PUBLISHED_PORTS} -p ${REGISTRY_PORT}:${NEXUS_DOCKER_PORT}"
114             fi
115         fi
116     done
117 }
118
119 simulated_hosts () {
120     SIMUL_HOSTS=($(sed -n '/\.[^/].*\//p' ${1} | sed -e 's/\/.*$// ; s/:.*$//' | sort -u | grep -v ${DEFAULT_REGISTRY} || true ) ${NEXUS_DOMAIN})
121     for HOST in "${SIMUL_HOSTS[@]}"; do
122         if ! grep -wq ${HOST} /etc/hosts; then
123             echo "127.0.0.1 ${HOST}" >> /etc/hosts
124         fi
125     done
126 }
127
128 load_docker_images () {
129     for ARCHIVE in $(sed $'s/\r// ; /^#/d ; s/\:/\_/g ; s/\//\_/g ; s/$/\.tar/g' ${1} | awk '{ print $1 }'); do
130         docker load -i ${NXS_SRC_DOCKER_IMG_DIR}/${ARCHIVE}
131     done
132 }
133
134 prepare_npm () {
135     # Configure NPM registry to our Nexus repository
136     echo "Configure NPM registry to ${NPM_REGISTRY}"
137     npm config set registry "${NPM_REGISTRY}"
138
139     # Login to NPM registry
140     /usr/bin/expect <<- EOF
141         spawn npm login
142         expect "Username:"
143         send "${NEXUS_USERNAME}\n"
144         expect "Password:"
145         send "${NEXUS_PASSWORD}\n"
146         expect Email:
147         send "${NEXUS_EMAIL}\n"
148         expect eof
149         EOF
150 }
151
152 patch_npm () {
153     # Patch problematic package
154     PATCHED_NPM="$(grep tsscmp ${1} | sed $'s/\r// ; s/\\@/\-/ ; s/$/\.tgz/')"
155     if [[ ! -z "${PATCHED_NPM}" ]] && ! zgrep -aq "${NPM_REGISTRY}" "${PATCHED_NPM}" 2>/dev/null
156     then
157         tar xzf "${PATCHED_NPM}"
158         rm -f "${PATCHED_NPM}"
159         sed -i 's|\"registry\":\ \".*\"|\"registry\":\ \"'"${NPM_REGISTRY}"'\"|g' package/package.json
160         tar -zcf "${PATCHED_NPM}" package
161         rm -rf package
162     fi
163 }
164
165 push_npm () {
166     for ARCHIVE in $(sed $'s/\r// ; s/\\@/\-/g ; s/$/\.tgz/g' ${1}); do
167         npm publish --access public ${ARCHIVE} > /dev/null
168         echo "NPM ${ARCHIVE} pushed to Nexus"
169     done
170 }
171
172 push_pip () {
173     for PACKAGE in $(sed $'s/\r//; s/==/-/' ${1}); do
174         twine upload -u "${NEXUS_USERNAME}" -p "${NEXUS_PASSWORD}" --repository-url ${PYPI_REGISTRY} ${PACKAGE}* > /dev/null
175         echo "PYPI ${PACKAGE} pushed to Nexus"
176     done
177 }
178
179 docker_login () {
180     for REGISTRY in $(sed -n '/\.[^/].*\//p' ${1} | sed -e 's/\/.*$//' | sort -u | grep -v ${DEFAULT_REGISTRY}) ${DOCKER_REGISTRY}; do
181         if ! grep -wqs ${REGISTRY} ~/.docker/config.json; then
182                 echo "Docker login to ${REGISTRY}"
183             echo -n "${NEXUS_PASSWORD}" | docker login -u "${NEXUS_USERNAME}" --password-stdin ${REGISTRY} > /dev/null
184             fi
185     done
186 }
187
188 push_docker () {
189     for IMAGE in $(sed $'s/\r// ; /^#/d' ${1} | awk '{ print $1 }'); do
190         PUSH=""
191         if [[ ${IMAGE} != *"/"* ]]; then
192             PUSH="${DOCKER_REGISTRY}/library/${IMAGE}"
193         elif [[ ${IMAGE} == *"${DEFAULT_REGISTRY}"* ]]; then
194             if [[ ${IMAGE} == *"/"*"/"* ]]; then
195                 PUSH="$(sed 's/'"${DEFAULT_REGISTRY}"'/'"${DOCKER_REGISTRY}"'/' <<< ${IMAGE})"
196             else
197                 PUSH="$(sed 's/'"${DEFAULT_REGISTRY}"'/'"${DOCKER_REGISTRY}"'\/library/' <<< ${IMAGE})"
198             fi
199         elif [[ -z $(sed -n '/\.[^/].*\//p' <<< ${IMAGE}) ]]; then
200             PUSH="${DOCKER_REGISTRY}/${IMAGE}"
201         fi
202         if [[ ! -z ${PUSH} ]]; then
203             docker tag ${IMAGE} ${PUSH}
204         else
205             PUSH="${IMAGE}"
206         fi
207             docker push ${PUSH}
208         echo "${IMAGE} pushed as ${PUSH} to Nexus"
209     done
210 }
211
212 while [ "${1}" != "" ]; do
213     case ${1} in
214         -d | --docker )                    shift
215                                            NXS_DOCKER_IMG_LISTS+=("${1}")
216                                            ;;
217         -i | --input-directory )           shift
218                                            DATA_DIR="${1}"
219                                            ;;
220         -ld | --load-docker-images )       DOCKER_LOAD="true"
221                                            ;;
222         -n | --npm )                       NPM_PUSH="true"
223                                            COMMANDS+=(expect npm)
224                                            shift
225                                            NXS_NPM_LISTS+=("${1}")
226                                            ;;
227         -o | --output-directory )          shift
228                                            NEXUS_DATA_DIR="${1}"
229                                            ;;
230         -p | --pypi )                      PYPI_PUSH="true"
231                                            COMMANDS+=(twine)
232                                            shift
233                                            NXS_PYPI_LISTS+=("${1}")
234                                            ;;
235         -rl | --resource-list-directory )  shift
236                                            LISTS_DIR="${1}"
237                                            ;;
238         -h | --help )                      usage
239                                            ;;
240         *)                                 usage
241     esac
242     shift
243 done
244
245 # Verify all dependencies are available in PATH
246 FAILED_COMMANDS=()
247 for cmd in ${COMMANDS[*]}; do
248     command -v $cmd >/dev/null 2>&1 || FAILED_COMMANDS+=($cmd)
249 done
250
251 if [ ${#FAILED_COMMANDS[*]} -gt 0 ]; then
252     echo "Following commands where not found in PATH and are required:"
253     echo ${FAILED_COMMANDS[*]}
254     echo "Aborting."
255     exit 1
256 fi
257
258 # Setup directories with resources for docker, npm and pypi
259 NXS_SRC_DOCKER_IMG_DIR="${DATA_DIR}/offline_data/docker_images_for_nexus"
260 NXS_SRC_NPM_DIR="${DATA_DIR}/offline_data/npm_tar"
261 NXS_SRC_PYPI_DIR="${DATA_DIR}/offline_data/pypi"
262
263 # Setup specific resources lists
264 NXS_INFRA_LIST="${LISTS_DIR}/infra_docker_images.list"
265 NXS_DOCKER_IMG_LIST="${LISTS_DIR}/onap_docker_images.list"
266 NXS_RKE_DOCKER_IMG_LIST="${LISTS_DIR}/rke_docker_images.list"
267 NXS_K8S_DOCKER_IMG_LIST="${LISTS_DIR}/k8s_docker_images.list"
268
269 # Setup Nexus image used for build and install infra
270 NEXUS_IMAGE="$(grep sonatype/nexus3 ${NXS_INFRA_LIST})"
271 NEXUS_IMAGE_TAR="${DATA_DIR}/offline_data/docker_images_infra/$(sed 's/\//\_/ ; s/$/\.tar/ ; s/\:/\_/' <<< ${NEXUS_IMAGE})"
272
273 # Set default lists if nothing specific defined by user
274 if [ ${#NXS_DOCKER_IMG_LISTS[@]} -eq 0 ]; then
275     NXS_DOCKER_IMG_LISTS=("${NXS_DOCKER_IMG_LIST}" "${NXS_RKE_DOCKER_IMG_LIST}" "${NXS_K8S_DOCKER_IMG_LIST}")
276 fi
277
278 # Backup /etc/hosts
279 HOSTS_BACKUP="$(eval ${TIMESTAMP}_hosts.bk)"
280 cp /etc/hosts /etc/${HOSTS_BACKUP}
281
282 # Backup the current docker registry settings
283 if [ -f ~/.docker/config.json ]; then
284     DOCKER_CONF_BACKUP="$(eval ${TIMESTAMP}_config.json.bk)"
285     mv ~/.docker/config.json ~/.docker/${DOCKER_CONF_BACKUP}
286 fi
287
288 # Setup default ports published to host as docker registry
289 PUBLISHED_PORTS="-p ${NEXUS_PORT}:${NEXUS_PORT} -p ${NEXUS_DOCKER_PORT}:${NEXUS_DOCKER_PORT}"
290
291 # Setup additional ports published to host based on simulated docker registries
292 # Setup simulated domain names to be able to push all to private Nexus repository
293 for DOCKER_IMG_LIST in "${NXS_DOCKER_IMG_LISTS[@]}"; do
294     publish_ports "${DOCKER_IMG_LIST}"
295     simulated_hosts "${DOCKER_IMG_LIST}"
296 done
297
298 # Nexus repository configuration setup
299 NEXUS_CONFIG_GROOVY='import org.sonatype.nexus.security.realm.RealmManager
300 import org.sonatype.nexus.repository.attributes.AttributesFacet
301 import org.sonatype.nexus.security.user.UserManager
302 import org.sonatype.nexus.repository.manager.RepositoryManager
303 import org.sonatype.nexus.security.user.UserNotFoundException
304 /* Use the container to look up some services. */
305 realmManager = container.lookup(RealmManager.class)
306 userManager = container.lookup(UserManager.class, "default") //default user manager
307 repositoryManager = container.lookup(RepositoryManager.class)
308 /* Managers are used when scripting api cannot. Note that scripting api can only create mostly, and that creation methods return objects of created entities. */
309 /* Perform cleanup by removing all repos and users. Realms do not need to be re-disabled, admin and anonymous user will not be removed. */
310 userManager.listUserIds().each({ id ->
311     if (id != "anonymous" && id != "admin")
312         userManager.deleteUser(id)
313 })
314 repositoryManager.browse().each {
315     repositoryManager.delete(it.getName())
316 }
317 /* Add bearer token realms at the end of realm lists... */
318 realmManager.enableRealm("NpmToken")
319 realmManager.enableRealm("DockerToken")
320 realmManager.enableRealm("PypiToken")
321 /* Create the docker user. */
322 security.addUser("docker", "docker", "docker", "docker@example.com", true, "docker", ["nx-anonymous"])
323 /* Create docker, npm and pypi repositories. Their default configuration should be compliant with our requirements, except the docker registry creation. */
324 repository.createNpmHosted("npm-private")
325 repository.createPyPiHosted("pypi-private")
326 def r = repository.createDockerHosted("onap", 8082, 0)
327 /* force basic authentication true by default, must set to false for docker repo. */
328 conf=r.getConfiguration()
329 conf.attributes("docker").set("forceBasicAuth", false)
330 repositoryManager.update(conf)'
331
332 # Prepare the Nexus configuration
333 NEXUS_CONFIG=$(echo "${NEXUS_CONFIG_GROOVY}" | jq -Rsc  '{"name":"configure", "type":"groovy", "content":.}')
334
335 #################################
336 # Docker repository preparation #
337 #################################
338
339 if [ "${DOCKER_LOAD}" == "true" ]; then
340     # Load predefined Nexus image
341     docker load -i ${NEXUS_IMAGE_TAR}
342     # Load all necessary images
343     for DOCKER_IMG_LIST in "${NXS_DOCKER_IMG_LISTS[@]}"; do
344         load_docker_images "${DOCKER_IMG_LIST}"
345     done
346 fi
347
348 ################################
349 # Nexus repository preparation #
350 ################################
351
352 # Prepare nexus-data directory
353 if [ -d ${NEXUS_DATA_DIR} ]; then
354    if [ "$(docker ps -q -f name="${NEXUS_DOMAIN}")" ]; then
355        echo "Removing container ${NEXUS_DOMAIN}"
356        docker rm -f $(docker ps -aq -f name="${NEXUS_DOMAIN}")
357    fi
358    pushd ${NEXUS_DATA_DIR}/..
359    NXS_BACKUP="$(eval ${TIMESTAMP})_$(basename ${NEXUS_DATA_DIR})_bk"
360    mv ${NEXUS_DATA_DIR} "${NXS_BACKUP}"
361    echo "${NEXUS_DATA_DIR} already exists - backing up to ${NXS_BACKUP}"
362    popd
363 fi
364
365 mkdir -p ${NEXUS_DATA_DIR}
366 chown 200:200 ${NEXUS_DATA_DIR}
367 chmod 777 ${NEXUS_DATA_DIR}
368
369 # Save Nexus version to prevent/catch data incompatibility
370 # Adding commit informations to have link to data from which the blob was built
371 cat >> ${NEXUS_DATA_DIR}/nexus.ver << INFO
372 nexus_image=$(docker image ls ${NEXUS_IMAGE} --no-trunc --format "{{.Repository}}:{{.Tag}}\nnexus_image_digest={{.ID}}")
373 $(for INDEX in ${!NXS_DOCKER_IMG_LISTS[@]}; do printf 'used_image_list%s=%s\n' "$INDEX" "$(sed 's/^.*\/\(.*\)$/\1/' <<< ${NXS_DOCKER_IMG_LISTS[$INDEX]})"; done)
374 $(sed -n 's/^.*OOM\ commit\ /oom_repo_commit=/p' ${NXS_DOCKER_IMG_LISTS[@]})
375 installer_repo_commit=$(git --git-dir="${LOCAL_PATH}/../.git" rev-parse HEAD)
376 INFO
377
378 # Start the Nexus
379 NEXUS_CONT_ID=$(docker run -d --rm -v ${NEXUS_DATA_DIR}:/nexus-data:rw --name ${NEXUS_DOMAIN} ${PUBLISHED_PORTS} ${NEXUS_IMAGE})
380 echo "Waiting for Nexus to fully start"
381 until curl -su ${NEXUS_USERNAME}:${NEXUS_PASSWORD} http://${NEXUS_DOMAIN}:${NEXUS_PORT}/service/metrics/healthcheck | grep '"healthy":true' > /dev/null ; do
382     printf "."
383     sleep 3
384 done
385 echo -e "\nNexus started"
386
387 # Configure the nexus repository
388 curl -sX POST --header 'Content-Type: application/json' --data-binary "${NEXUS_CONFIG}" http://${NEXUS_USERNAME}:${NEXUS_PASSWORD}@${NEXUS_DOMAIN}:${NEXUS_PORT}/service/rest/v1/script
389 curl -sX POST --header "Content-Type: text/plain" http://${NEXUS_USERNAME}:${NEXUS_PASSWORD}@${NEXUS_DOMAIN}:${NEXUS_PORT}/service/rest/v1/script/configure/run > /dev/null
390
391 ###########################
392 # Populate NPM repository #
393 ###########################
394 if [ $NPM_PUSH == "true" ]; then
395     prepare_npm
396     pushd ${NXS_SRC_NPM_DIR}
397     for NPM_LIST in "${NXS_NPM_LISTS[@]}"; do
398         patch_npm "${NPM_LIST}"
399         push_npm "${NPM_LIST}"
400     done
401     popd
402     # Return default settings
403     npm logout
404     npm config set registry "https://registry.npmjs.org"
405 fi
406
407 ###############################
408 ##  Populate PyPi repository  #
409 ###############################
410 if [ $PYPI_PUSH == "true" ]; then
411     pushd ${NXS_SRC_PYPI_DIR}
412     for PYPI_LIST in "${NXS_PYPI_LISTS[@]}"; do
413         push_pip "${PYPI_LIST}"
414     done
415     popd
416 fi
417
418 ###############################
419 ## Populate Docker repository #
420 ###############################
421
422 # Login to simulated docker registries
423 # Push images to private nexus based on the lists
424 # Images from default registry need to be tagged to private registry
425 # and those without defined repository in tag uses default repository 'library'
426 for DOCKER_IMG_LIST in "${NXS_DOCKER_IMG_LISTS[@]}"; do
427     docker_login "${DOCKER_IMG_LIST}"
428     push_docker "${DOCKER_IMG_LIST}"
429 done
430
431 ##############################
432 # Stop the Nexus and cleanup #
433 ##############################
434
435 echo "Stopping Nexus and returning backups"
436
437 # Stop the Nexus
438 docker stop ${NEXUS_CONT_ID} > /dev/null
439
440 # Return backed up configuration files
441 mv -f "/etc/${HOSTS_BACKUP}" /etc/hosts
442
443 if [ -f ~/.docker/${DOCKER_CONF_BACKUP} ]; then
444     mv -f ~/.docker/${DOCKER_CONF_BACKUP} ~/.docker/config.json
445 fi
446
447 echo "Nexus blob is built"
448 exit 0