3 # COPYRIGHT NOTICE STARTS HERE
5 # Copyright 2018-2020© Samsung Electronics Co., Ltd.
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
11 # http://www.apache.org/licenses/LICENSE-2.0
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.
19 # COPYRIGHT NOTICE ENDS HERE
21 ### This script prepares Nexus repositories data blobs for ONAP
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
32 TIMESTAMP="date +'%Y-%m-%d_%H-%M-%S'"
33 SCRIPT_LOG="/tmp/$(basename $0)_$(eval ${TIMESTAMP}).log"
36 exec &> >(tee -a "${SCRIPT_LOG}")
38 # Nexus repository properties
41 NEXUS_DOCKER_PORT="8082"
42 DEFAULT_REGISTRY="docker.io"
44 # Nexus repository credentials
46 NEXUS_PASSWORD=admin123
47 NEXUS_EMAIL=admin@example.org
50 LOCAL_PATH="$(readlink -f $(dirname ${0}))"
56 DATA_DIR="$(realpath ${LOCAL_PATH}/../../resources)"
57 NEXUS_DATA_DIR="${DATA_DIR}/nexus_data"
58 LISTS_DIR="${LOCAL_PATH}/data_lists"
60 # Required dependencies
65 Usage: $(basename $0) [OPTION...] [FILE]...
67 This script prepares Nexus repositories data blobs for ONAP
69 Following dependencies are required: nodejs, jq, docker, twine, expect
70 By default, without any lists or dirs provided, the resources are expected as downloaded
71 during download process and default lists will be used to build the Nexus blob in the same
75 $(basename $0) --input-directory </path/to/downloaded/files/dir> -ld --output-directory
76 </path/to/output/dir> --resource-list-directory </path/to/dir/with/resource/list>
77 # Docker images, npms and pypi packages will be loaded from specified directory
78 # and the blob is created
79 $(basename $0) -d </path/to/docker/images/list> -d </path/to/another/docker/images/list>
80 -n </path/to/npm/list> -p </path/to/pip/list>
81 # Docker images, npms and pypi packages will be pushed to Nexus based and provided data
82 # lists (multiple lists can be provided)
84 -d | --docker use specific list of docker images to be pushed into Nexus
85 (in case of -ld used, this list will be used for loading of
87 -h | --help print this usage
88 -i | --input-directory use specific directory containing resources needed to
90 The structure of this directory must organized as described
92 -ld | --load-docker-images load docker images from resource directory
93 -n | --npm list of npm packages to be pushed into Nexus
94 -o | --output-directory use specific directory for the target blob
95 -p | --pypi use specific list of pypi packages to be pushed into Nexus
96 -rl | --resource-list-directory use specific directory with docker, pypi and npm lists
97 -c | --container-name use specific Nexus docker container name
103 for REGISTRY in $(sed -n '/\.[^/].*\//p' ${1} | sed -e 's/\/.*$//' | sort -u | grep -v ${DEFAULT_REGISTRY} || true) ${NEXUS_PORT}; do
104 if [[ ${REGISTRY} != *":"* ]]; then
105 if [[ ${PUBLISHED_PORTS} != *"80:${NEXUS_DOCKER_PORT}"* ]]; then
106 PUBLISHED_PORTS="${PUBLISHED_PORTS} -p 80:${NEXUS_DOCKER_PORT}"
109 REGISTRY_PORT="$(sed 's/^.*\:\([[:digit:]]*\)$/\1/' <<< ${REGISTRY})"
110 if [[ ${PUBLISHED_PORTS} != *"${REGISTRY_PORT}:${NEXUS_DOCKER_PORT}"* ]]; then
111 PUBLISHED_PORTS="${PUBLISHED_PORTS} -p ${REGISTRY_PORT}:${NEXUS_DOCKER_PORT}"
118 SIMUL_HOSTS=($(sed -n '/\.[^/].*\//p' ${1} | sed -e 's/\/.*$// ; s/:.*$//' | sort -u | grep -v ${DEFAULT_REGISTRY} || true ) ${NEXUS_DOMAIN})
119 for HOST in "${SIMUL_HOSTS[@]}"; do
120 if ! grep -wq ${HOST} /etc/hosts; then
121 echo "127.0.0.1 ${HOST}" >> /etc/hosts
126 load_docker_images () {
127 for ARCHIVE in $(sed $'s/\r// ; /^#/d ; s/\:/\_/g ; s/\//\_/g ; s/$/\.tar/g' ${1} | awk '{ print $1 }'); do
128 docker load -i ${NXS_SRC_DOCKER_IMG_DIR}/${ARCHIVE}
133 # Configure NPM registry to our Nexus repository
134 echo "Configure NPM registry to ${NPM_REGISTRY}"
135 npm config set registry "${NPM_REGISTRY}"
137 # Login to NPM registry
138 /usr/bin/expect <<- EOF
141 send "${NEXUS_USERNAME}\n"
143 send "${NEXUS_PASSWORD}\n"
145 send "${NEXUS_EMAIL}\n"
151 # Patch problematic package
152 PATCHED_NPM="$(grep tsscmp ${1} | sed $'s/\r// ; s/\\@/\-/ ; s/$/\.tgz/')"
153 if [[ ! -z "${PATCHED_NPM}" ]] && ! zgrep -aq "${NPM_REGISTRY}" "${PATCHED_NPM}" 2>/dev/null
155 tar xzf "${PATCHED_NPM}"
156 rm -f "${PATCHED_NPM}"
157 sed -i 's|\"registry\":\ \".*\"|\"registry\":\ \"'"${NPM_REGISTRY}"'\"|g' package/package.json
158 tar -zcf "${PATCHED_NPM}" package
164 for ARCHIVE in $(sed $'s/\r// ; s/\\@/\-/g ; s/$/\.tgz/g' ${1}); do
165 npm publish --access public ${ARCHIVE} > /dev/null
166 echo "NPM ${ARCHIVE} pushed to Nexus"
171 for PACKAGE in $(sed $'s/\r//; s/==/-/' ${1}); do
172 twine upload -u "${NEXUS_USERNAME}" -p "${NEXUS_PASSWORD}" --repository-url ${PYPI_REGISTRY} ${PACKAGE}* > /dev/null
173 echo "PYPI ${PACKAGE} pushed to Nexus"
178 if ! grep -wqs ${DOCKER_REGISTRY} ~/.docker/config.json; then
179 echo "Docker login to ${DOCKER_REGISTRY}"
180 echo -n "${NEXUS_PASSWORD}" | docker login -u "${NEXUS_USERNAME}" --password-stdin ${DOCKER_REGISTRY} > /dev/null
185 for IMAGE in $(sed $'s/\r// ; /^#/d' ${1} | awk '{ print $1 }'); do
187 if [[ ${IMAGE} != *"/"* ]]; then
188 PUSH="${DOCKER_REGISTRY}/library/${IMAGE}"
189 elif [[ ${IMAGE} == *"${DEFAULT_REGISTRY}"* ]]; then
190 if [[ ${IMAGE} == *"/"*"/"* ]]; then
191 PUSH="$(sed 's/'"${DEFAULT_REGISTRY}"'/'"${DOCKER_REGISTRY}"'/' <<< ${IMAGE})"
193 PUSH="$(sed 's/'"${DEFAULT_REGISTRY}"'/'"${DOCKER_REGISTRY}"'\/library/' <<< ${IMAGE})"
195 elif [[ -z $(sed -n '/\.[^/].*\//p' <<< ${IMAGE}) ]]; then
196 PUSH="${DOCKER_REGISTRY}/${IMAGE}"
198 # substitute all host names with $DOCKER_REGISTRY
199 repo_host=$(sed -e 's/\/.*$//' <<< ${IMAGE})
200 PUSH="$(sed -e 's/'"${repo_host}"'/'"${DOCKER_REGISTRY}"'/' <<< ${IMAGE})"
202 docker tag ${IMAGE} ${PUSH}
206 echo "${IMAGE} pushed as ${PUSH} to Nexus"
210 validate_container_name () {
211 # Verify $1 is a valid hostname
212 if ! echo "${1}" | egrep -q "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$";
214 echo "ERROR: ${1} is not a valid name!"
219 while [ "${1}" != "" ]; do
221 -d | --docker ) shift
222 NXS_DOCKER_IMG_LISTS+=("$(realpath ${1})")
224 -i | --input-directory ) shift
225 DATA_DIR="$(realpath ${1})"
227 -ld | --load-docker-images ) DOCKER_LOAD="true"
229 -n | --npm ) NPM_PUSH="true"
230 COMMANDS+=(expect npm)
232 NXS_NPM_LISTS+=("$(realpath ${1})")
234 -c | --container-name ) shift
235 validate_container_name "${1}"
238 -o | --output-directory ) shift
239 NEXUS_DATA_DIR="$(realpath ${1})"
241 -p | --pypi ) PYPI_PUSH="true"
244 NXS_PYPI_LISTS+=("$(realpath ${1})")
246 -rl | --resource-list-directory ) shift
247 LISTS_DIR="$(realpath ${1})"
257 # Verify all dependencies are available in PATH
259 for cmd in ${COMMANDS[*]}; do
260 command -v $cmd >/dev/null 2>&1 || FAILED_COMMANDS+=($cmd)
263 if [ ${#FAILED_COMMANDS[*]} -gt 0 ]; then
264 echo "Following commands where not found in PATH and are required:"
265 echo ${FAILED_COMMANDS[*]}
270 # Nexus repository locations
271 NPM_REGISTRY="http://${NEXUS_DOMAIN}:${NEXUS_PORT}/repository/npm-private/"
272 PYPI_REGISTRY="http://${NEXUS_DOMAIN}:${NEXUS_PORT}/repository/pypi-private/"
273 DOCKER_REGISTRY="${NEXUS_DOMAIN}:${NEXUS_DOCKER_PORT}"
275 # Setup directories with resources for docker, npm and pypi
276 NXS_SRC_DOCKER_IMG_DIR="${DATA_DIR}/offline_data/docker_images_for_nexus"
277 NXS_SRC_NPM_DIR="${DATA_DIR}/offline_data/npm_tar"
278 NXS_SRC_PYPI_DIR="${DATA_DIR}/offline_data/pypi"
280 # Setup specific resources lists
281 NXS_INFRA_LIST="${LISTS_DIR}/infra_docker_images.list"
282 NXS_DOCKER_IMG_LIST="${LISTS_DIR}/onap_docker_images.list"
283 NXS_RKE_DOCKER_IMG_LIST="${LISTS_DIR}/rke_docker_images.list"
284 NXS_K8S_DOCKER_IMG_LIST="${LISTS_DIR}/k8s_docker_images.list"
286 # Setup Nexus image used for build and install infra
287 NEXUS_IMAGE="$(grep sonatype/nexus3 ${NXS_INFRA_LIST})"
288 NEXUS_IMAGE_TAR="${DATA_DIR}/offline_data/docker_images_infra/$(sed 's/\//\_/ ; s/$/\.tar/ ; s/\:/\_/' <<< ${NEXUS_IMAGE})"
290 # Set default lists if nothing specific defined by user
291 if [ ${#NXS_DOCKER_IMG_LISTS[@]} -eq 0 ]; then
292 NXS_DOCKER_IMG_LISTS=("${NXS_DOCKER_IMG_LIST}" "${NXS_RKE_DOCKER_IMG_LIST}" "${NXS_K8S_DOCKER_IMG_LIST}")
296 HOSTS_BACKUP="$(eval ${TIMESTAMP}_hosts.bk)"
297 cp /etc/hosts /etc/${HOSTS_BACKUP}
299 # Backup the current docker registry settings
300 if [ -f ~/.docker/config.json ]; then
301 DOCKER_CONF_BACKUP="$(eval ${TIMESTAMP}_config.json.bk)"
302 mv ~/.docker/config.json ~/.docker/${DOCKER_CONF_BACKUP}
305 # Setup default ports published to host as docker registry
306 PUBLISHED_PORTS="-p ${NEXUS_PORT}:${NEXUS_PORT} -p ${NEXUS_DOCKER_PORT}:${NEXUS_DOCKER_PORT}"
308 # Setup additional ports published to host based on simulated docker registries
309 # Setup simulated domain names to be able to push all to private Nexus repository
310 for DOCKER_IMG_LIST in "${NXS_DOCKER_IMG_LISTS[@]}"; do
311 publish_ports "${DOCKER_IMG_LIST}"
312 simulated_hosts "${DOCKER_IMG_LIST}"
315 # Nexus repository configuration setup
316 NEXUS_CONFIG_GROOVY='import org.sonatype.nexus.security.realm.RealmManager
317 import org.sonatype.nexus.repository.attributes.AttributesFacet
318 import org.sonatype.nexus.security.user.UserManager
319 import org.sonatype.nexus.repository.manager.RepositoryManager
320 import org.sonatype.nexus.security.user.UserNotFoundException
321 /* Use the container to look up some services. */
322 realmManager = container.lookup(RealmManager.class)
323 userManager = container.lookup(UserManager.class, "default") //default user manager
324 repositoryManager = container.lookup(RepositoryManager.class)
325 /* Managers are used when scripting api cannot. Note that scripting api can only create mostly, and that creation methods return objects of created entities. */
326 /* Perform cleanup by removing all repos and users. Realms do not need to be re-disabled, admin and anonymous user will not be removed. */
327 userManager.listUserIds().each({ id ->
328 if (id != "anonymous" && id != "admin")
329 userManager.deleteUser(id)
331 repositoryManager.browse().each {
332 repositoryManager.delete(it.getName())
334 /* Add bearer token realms at the end of realm lists... */
335 realmManager.enableRealm("NpmToken")
336 realmManager.enableRealm("DockerToken")
337 realmManager.enableRealm("PypiToken")
338 /* Create the docker user. */
339 security.addUser("docker", "docker", "docker", "docker@example.com", true, "docker", ["nx-anonymous"])
340 /* Create docker, npm and pypi repositories. Their default configuration should be compliant with our requirements, except the docker registry creation. */
341 repository.createNpmHosted("npm-private")
342 repository.createPyPiHosted("pypi-private")
343 def r = repository.createDockerHosted("onap", 8082, 0)
344 /* force basic authentication true by default, must set to false for docker repo. */
345 conf=r.getConfiguration()
346 conf.attributes("docker").set("forceBasicAuth", false)
347 repositoryManager.update(conf)'
349 # Prepare the Nexus configuration
350 NEXUS_CONFIG=$(echo "${NEXUS_CONFIG_GROOVY}" | jq -Rsc '{"name":"configure", "type":"groovy", "content":.}')
352 #################################
353 # Docker repository preparation #
354 #################################
356 if [ "${DOCKER_LOAD}" == "true" ]; then
357 # Load predefined Nexus image
358 docker load -i ${NEXUS_IMAGE_TAR}
359 # Load all necessary images
360 for DOCKER_IMG_LIST in "${NXS_DOCKER_IMG_LISTS[@]}"; do
361 load_docker_images "${DOCKER_IMG_LIST}"
365 ################################
366 # Nexus repository preparation #
367 ################################
369 # Prepare nexus-data directory
370 if [ -d ${NEXUS_DATA_DIR} ]; then
371 if [ "$(docker ps -q -f name="${NEXUS_DOMAIN}")" ]; then
372 echo "Removing container ${NEXUS_DOMAIN}"
373 docker rm -f $(docker ps -aq -f name="${NEXUS_DOMAIN}")
375 pushd ${NEXUS_DATA_DIR}/..
376 NXS_BACKUP="$(eval ${TIMESTAMP})_$(basename ${NEXUS_DATA_DIR})_bk"
377 mv ${NEXUS_DATA_DIR} "${NXS_BACKUP}"
378 echo "${NEXUS_DATA_DIR} already exists - backing up to ${NXS_BACKUP}"
382 mkdir -p ${NEXUS_DATA_DIR}
383 chown 200:200 ${NEXUS_DATA_DIR}
384 chmod 777 ${NEXUS_DATA_DIR}
386 # Save Nexus version to prevent/catch data incompatibility
387 # Adding commit informations to have link to data from which the blob was built
388 cat >> ${NEXUS_DATA_DIR}/nexus.ver << INFO
389 nexus_image=$(docker image ls ${NEXUS_IMAGE} --no-trunc --format "{{.Repository}}:{{.Tag}}\nnexus_image_digest={{.ID}}")
390 $(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)
391 $(sed -n 's/^.*OOM\ commit\ /oom_repo_commit=/p' ${NXS_DOCKER_IMG_LISTS[@]})
392 installer_repo_commit=$(git --git-dir="${LOCAL_PATH}/../.git" rev-parse HEAD)
396 NEXUS_CONT_ID=$(docker run -d --rm -v ${NEXUS_DATA_DIR}:/nexus-data:rw --name ${NEXUS_DOMAIN} ${PUBLISHED_PORTS} ${NEXUS_IMAGE})
397 echo "Waiting for Nexus to fully start"
398 until curl -su ${NEXUS_USERNAME}:${NEXUS_PASSWORD} http://${NEXUS_DOMAIN}:${NEXUS_PORT}/service/metrics/healthcheck | grep '"healthy":true' > /dev/null ; do
402 echo -e "\nNexus started"
404 # Configure the nexus repository
405 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
406 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
408 ###########################
409 # Populate NPM repository #
410 ###########################
411 if [ $NPM_PUSH == "true" ]; then
413 pushd ${NXS_SRC_NPM_DIR}
414 for NPM_LIST in "${NXS_NPM_LISTS[@]}"; do
415 patch_npm "${NPM_LIST}"
416 push_npm "${NPM_LIST}"
419 # Return default settings
421 npm config set registry "https://registry.npmjs.org"
424 ###############################
425 ## Populate PyPi repository #
426 ###############################
427 if [ $PYPI_PUSH == "true" ]; then
428 pushd ${NXS_SRC_PYPI_DIR}
429 for PYPI_LIST in "${NXS_PYPI_LISTS[@]}"; do
430 push_pip "${PYPI_LIST}"
435 ###############################
436 ## Populate Docker repository #
437 ###############################
439 # Login to docker registry simulated by Nexus container
440 # Push images to private nexus based on the lists
441 # All images need to be tagged to simulated registry
442 # and those without defined repository in tag use default repository 'library'
444 for DOCKER_IMG_LIST in "${NXS_DOCKER_IMG_LISTS[@]}"; do
445 push_docker "${DOCKER_IMG_LIST}"
448 ##############################
449 # Stop the Nexus and cleanup #
450 ##############################
452 echo "Stopping Nexus and returning backups"
455 docker stop ${NEXUS_CONT_ID} > /dev/null
457 # Return backed up configuration files
458 mv -f "/etc/${HOSTS_BACKUP}" /etc/hosts
460 if [ -f ~/.docker/${DOCKER_CONF_BACKUP} ]; then
461 mv -f ~/.docker/${DOCKER_CONF_BACKUP} ~/.docker/config.json
464 echo "Nexus blob is built"