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
40 NEXUS_HOST="127.0.0.1"
42 NEXUS_DOCKER_PORT="8082"
43 DEFAULT_REGISTRY="docker.io"
45 # Nexus repository credentials
47 NEXUS_PASSWORD=admin123
48 NEXUS_EMAIL=admin@example.org
51 LOCAL_PATH="$(readlink -f $(dirname ${0}))"
57 DATA_DIR="$(realpath ${LOCAL_PATH}/../../resources)"
58 NEXUS_DATA_DIR="${DATA_DIR}/nexus_data"
59 LISTS_DIR="${LOCAL_PATH}/data_lists"
61 # Required dependencies
66 Usage: $(basename $0) [OPTION...] [FILE]...
68 This script prepares Nexus repositories data blobs for ONAP
70 Following dependencies are required: nodejs, jq, docker, twine, expect
71 By default, without any lists or dirs provided, the resources are expected as downloaded
72 during download process and default lists will be used to build the Nexus blob in the same
76 $(basename $0) --input-directory </path/to/downloaded/files/dir> -ld --output-directory
77 </path/to/output/dir> --resource-list-directory </path/to/dir/with/resource/list>
78 # Docker images, npms and pypi packages will be loaded from specified directory
79 # and the blob is created
80 $(basename $0) -d </path/to/docker/images/list> -d </path/to/another/docker/images/list>
81 -n </path/to/npm/list> -p </path/to/pip/list>
82 # Docker images, npms and pypi packages will be pushed to Nexus based and provided data
83 # lists (multiple lists can be provided)
85 -d | --docker use specific list of docker images to be pushed into Nexus
86 (in case of -ld used, this list will be used for loading of
88 -h | --help print this usage
89 -i | --input-directory use specific directory containing resources needed to
91 The structure of this directory must organized as described
93 -ld | --load-docker-images load docker images from resource directory
94 -n | --npm list of npm packages to be pushed into Nexus
95 -o | --output-directory use specific directory for the target blob
96 -p | --pypi use specific list of pypi packages to be pushed into Nexus
97 -rl | --resource-list-directory use specific directory with docker, pypi and npm lists
98 -c | --container-name use specific Nexus docker container name
103 load_docker_images () {
104 for ARCHIVE in $(sed $'s/\r// ; /^#/d ; s/\:/\_/g ; s/\//\_/g ; s/$/\.tar/g' ${1} | awk '{ print $1 }'); do
105 docker load -i ${NXS_SRC_DOCKER_IMG_DIR}/${ARCHIVE}
110 # Configure NPM registry to our Nexus repository
111 echo "Configure NPM registry to ${NPM_REGISTRY}"
112 npm config set registry "${NPM_REGISTRY}"
114 # Login to NPM registry
115 /usr/bin/expect <<- EOF
118 send "${NEXUS_USERNAME}\n"
120 send "${NEXUS_PASSWORD}\n"
122 send "${NEXUS_EMAIL}\n"
128 # Patch problematic package
129 PATCHED_NPM="$(grep tsscmp ${1} | sed $'s/\r// ; s/\\@/\-/ ; s/$/\.tgz/')"
130 if [[ ! -z "${PATCHED_NPM}" ]] && ! zgrep -aq "${NPM_REGISTRY}" "${PATCHED_NPM}" 2>/dev/null
132 tar xzf "${PATCHED_NPM}"
133 rm -f "${PATCHED_NPM}"
134 sed -i 's|\"registry\":\ \".*\"|\"registry\":\ \"'"${NPM_REGISTRY}"'\"|g' package/package.json
135 tar -zcf "${PATCHED_NPM}" package
141 for ARCHIVE in $(sed $'s/\r// ; s/\\@/\-/g ; s/$/\.tgz/g' ${1}); do
142 npm publish --access public ${ARCHIVE} > /dev/null
143 echo "NPM ${ARCHIVE} pushed to Nexus"
148 for PACKAGE in $(sed $'s/\r//; s/==/-/' ${1}); do
149 twine upload -u "${NEXUS_USERNAME}" -p "${NEXUS_PASSWORD}" --repository-url ${PYPI_REGISTRY} ${PACKAGE}* > /dev/null
150 echo "PYPI ${PACKAGE} pushed to Nexus"
155 if ! grep -wqs ${DOCKER_REGISTRY} ~/.docker/config.json; then
156 echo "Docker login to ${DOCKER_REGISTRY}"
157 echo -n "${NEXUS_PASSWORD}" | docker login -u "${NEXUS_USERNAME}" --password-stdin ${DOCKER_REGISTRY} > /dev/null
162 for IMAGE in $(sed $'s/\r// ; /^#/d' ${1} | awk '{ print $1 }'); do
164 if [[ ${IMAGE} != *"/"* ]]; then
165 PUSH="${DOCKER_REGISTRY}/library/${IMAGE}"
166 elif [[ ${IMAGE} == *"${DEFAULT_REGISTRY}"* ]]; then
167 if [[ ${IMAGE} == *"/"*"/"* ]]; then
168 PUSH="$(sed 's/'"${DEFAULT_REGISTRY}"'/'"${DOCKER_REGISTRY}"'/' <<< ${IMAGE})"
170 PUSH="$(sed 's/'"${DEFAULT_REGISTRY}"'/'"${DOCKER_REGISTRY}"'\/library/' <<< ${IMAGE})"
172 elif [[ -z $(sed -n '/\.[^/].*\//p' <<< ${IMAGE}) ]]; then
173 PUSH="${DOCKER_REGISTRY}/${IMAGE}"
175 # substitute all host names with $DOCKER_REGISTRY
176 repo_host=$(sed -e 's/\/.*$//' <<< ${IMAGE})
177 PUSH="$(sed -e 's/'"${repo_host}"'/'"${DOCKER_REGISTRY}"'/' <<< ${IMAGE})"
179 docker tag ${IMAGE} ${PUSH}
183 echo "${IMAGE} pushed as ${PUSH} to Nexus"
187 validate_container_name () {
188 # Verify $1 is a valid hostname
189 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])$";
191 echo "ERROR: ${1} is not a valid name!"
196 while [ "${1}" != "" ]; do
198 -d | --docker ) shift
199 NXS_DOCKER_IMG_LISTS+=("$(realpath ${1})")
201 -i | --input-directory ) shift
202 DATA_DIR="$(realpath ${1})"
204 -ld | --load-docker-images ) DOCKER_LOAD="true"
206 -n | --npm ) NPM_PUSH="true"
207 COMMANDS+=(expect npm)
209 NXS_NPM_LISTS+=("$(realpath ${1})")
211 -c | --container-name ) shift
212 validate_container_name "${1}"
215 -o | --output-directory ) shift
216 NEXUS_DATA_DIR="$(realpath ${1})"
218 -p | --pypi ) PYPI_PUSH="true"
221 NXS_PYPI_LISTS+=("$(realpath ${1})")
223 -rl | --resource-list-directory ) shift
224 LISTS_DIR="$(realpath ${1})"
234 # Verify all dependencies are available in PATH
236 for cmd in ${COMMANDS[*]}; do
237 command -v $cmd >/dev/null 2>&1 || FAILED_COMMANDS+=($cmd)
240 if [ ${#FAILED_COMMANDS[*]} -gt 0 ]; then
241 echo "Following commands where not found in PATH and are required:"
242 echo ${FAILED_COMMANDS[*]}
247 # Nexus repository locations
248 NPM_REGISTRY="http://${NEXUS_HOST}:${NEXUS_PORT}/repository/npm-private/"
249 PYPI_REGISTRY="http://${NEXUS_HOST}:${NEXUS_PORT}/repository/pypi-private/"
250 DOCKER_REGISTRY="${NEXUS_HOST}:${NEXUS_DOCKER_PORT}"
252 # Setup directories with resources for docker, npm and pypi
253 NXS_SRC_DOCKER_IMG_DIR="${DATA_DIR}/offline_data/docker_images_for_nexus"
254 NXS_SRC_NPM_DIR="${DATA_DIR}/offline_data/npm_tar"
255 NXS_SRC_PYPI_DIR="${DATA_DIR}/offline_data/pypi"
257 # Setup specific resources lists
258 NXS_INFRA_LIST="${LISTS_DIR}/infra_docker_images.list"
259 NXS_DOCKER_IMG_LIST="${LISTS_DIR}/onap_docker_images.list"
260 NXS_RKE_DOCKER_IMG_LIST="${LISTS_DIR}/rke_docker_images.list"
261 NXS_K8S_DOCKER_IMG_LIST="${LISTS_DIR}/k8s_docker_images.list"
263 # Setup Nexus image used for build and install infra
264 NEXUS_IMAGE="$(grep sonatype/nexus3 ${NXS_INFRA_LIST})"
265 NEXUS_IMAGE_TAR="${DATA_DIR}/offline_data/docker_images_infra/$(sed 's/\//\_/ ; s/$/\.tar/ ; s/\:/\_/' <<< ${NEXUS_IMAGE})"
267 # Set default lists if nothing specific defined by user
268 if [ ${#NXS_DOCKER_IMG_LISTS[@]} -eq 0 ]; then
269 NXS_DOCKER_IMG_LISTS=("${NXS_DOCKER_IMG_LIST}" "${NXS_RKE_DOCKER_IMG_LIST}" "${NXS_K8S_DOCKER_IMG_LIST}")
272 # Backup the current docker registry settings
273 if [ -f ~/.docker/config.json ]; then
274 DOCKER_CONF_BACKUP="$(eval ${TIMESTAMP}_config.json.bk)"
275 mv ~/.docker/config.json ~/.docker/${DOCKER_CONF_BACKUP}
278 # Setup default ports published to host as docker registry
279 PUBLISHED_PORTS="-p ${NEXUS_PORT}:${NEXUS_PORT} -p ${NEXUS_DOCKER_PORT}:${NEXUS_DOCKER_PORT}"
281 # Nexus repository configuration setup
282 NEXUS_CONFIG_GROOVY='import org.sonatype.nexus.security.realm.RealmManager
283 import org.sonatype.nexus.repository.attributes.AttributesFacet
284 import org.sonatype.nexus.security.user.UserManager
285 import org.sonatype.nexus.repository.manager.RepositoryManager
286 import org.sonatype.nexus.security.user.UserNotFoundException
287 /* Use the container to look up some services. */
288 realmManager = container.lookup(RealmManager.class)
289 userManager = container.lookup(UserManager.class, "default") //default user manager
290 repositoryManager = container.lookup(RepositoryManager.class)
291 /* Managers are used when scripting api cannot. Note that scripting api can only create mostly, and that creation methods return objects of created entities. */
292 /* Perform cleanup by removing all repos and users. Realms do not need to be re-disabled, admin and anonymous user will not be removed. */
293 userManager.listUserIds().each({ id ->
294 if (id != "anonymous" && id != "admin")
295 userManager.deleteUser(id)
297 repositoryManager.browse().each {
298 repositoryManager.delete(it.getName())
300 /* Add bearer token realms at the end of realm lists... */
301 realmManager.enableRealm("NpmToken")
302 realmManager.enableRealm("DockerToken")
303 realmManager.enableRealm("PypiToken")
304 /* Create the docker user. */
305 security.addUser("docker", "docker", "docker", "docker@example.com", true, "docker", ["nx-anonymous"])
306 /* Create docker, npm and pypi repositories. Their default configuration should be compliant with our requirements, except the docker registry creation. */
307 repository.createNpmHosted("npm-private")
308 repository.createPyPiHosted("pypi-private")
309 def r = repository.createDockerHosted("onap", 8082, 0)
310 /* force basic authentication true by default, must set to false for docker repo. */
311 conf=r.getConfiguration()
312 conf.attributes("docker").set("forceBasicAuth", false)
313 repositoryManager.update(conf)'
315 # Prepare the Nexus configuration
316 NEXUS_CONFIG=$(echo "${NEXUS_CONFIG_GROOVY}" | jq -Rsc '{"name":"configure", "type":"groovy", "content":.}')
318 #################################
319 # Docker repository preparation #
320 #################################
322 if [ "${DOCKER_LOAD}" == "true" ]; then
323 # Load predefined Nexus image
324 docker load -i ${NEXUS_IMAGE_TAR}
325 # Load all necessary images
326 for DOCKER_IMG_LIST in "${NXS_DOCKER_IMG_LISTS[@]}"; do
327 load_docker_images "${DOCKER_IMG_LIST}"
331 ################################
332 # Nexus repository preparation #
333 ################################
335 # Prepare nexus-data directory
336 if [ -d ${NEXUS_DATA_DIR} ]; then
337 if [ "$(docker ps -q -f name="${NEXUS_DOMAIN}")" ]; then
338 echo "Removing container ${NEXUS_DOMAIN}"
339 docker rm -f $(docker ps -aq -f name="${NEXUS_DOMAIN}")
341 pushd ${NEXUS_DATA_DIR}/..
342 NXS_BACKUP="$(eval ${TIMESTAMP})_$(basename ${NEXUS_DATA_DIR})_bk"
343 mv ${NEXUS_DATA_DIR} "${NXS_BACKUP}"
344 echo "${NEXUS_DATA_DIR} already exists - backing up to ${NXS_BACKUP}"
348 mkdir -p ${NEXUS_DATA_DIR}
349 chown 200:200 ${NEXUS_DATA_DIR}
350 chmod 777 ${NEXUS_DATA_DIR}
352 # Save Nexus version to prevent/catch data incompatibility
353 # Adding commit informations to have link to data from which the blob was built
354 cat >> ${NEXUS_DATA_DIR}/nexus.ver << INFO
355 nexus_image=$(docker image ls ${NEXUS_IMAGE} --no-trunc --format "{{.Repository}}:{{.Tag}}\nnexus_image_digest={{.ID}}")
356 $(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)
357 $(sed -n 's/^.*OOM\ commit\ /oom_repo_commit=/p' ${NXS_DOCKER_IMG_LISTS[@]})
358 installer_repo_commit=$(git --git-dir="${LOCAL_PATH}/../.git" rev-parse HEAD)
362 NEXUS_CONT_ID=$(docker run -d --rm -v ${NEXUS_DATA_DIR}:/nexus-data:rw --name ${NEXUS_DOMAIN} ${PUBLISHED_PORTS} ${NEXUS_IMAGE})
363 echo "Waiting for Nexus to fully start"
364 until curl -su ${NEXUS_USERNAME}:${NEXUS_PASSWORD} http://${NEXUS_HOST}:${NEXUS_PORT}/service/metrics/healthcheck | grep '"healthy":true' > /dev/null ; do
368 echo -e "\nNexus started"
370 # Configure the nexus repository
371 curl -sX POST --header 'Content-Type: application/json' --data-binary "${NEXUS_CONFIG}" http://${NEXUS_USERNAME}:${NEXUS_PASSWORD}@${NEXUS_HOST}:${NEXUS_PORT}/service/rest/v1/script
372 curl -sX POST --header "Content-Type: text/plain" http://${NEXUS_USERNAME}:${NEXUS_PASSWORD}@${NEXUS_HOST}:${NEXUS_PORT}/service/rest/v1/script/configure/run > /dev/null
374 ###########################
375 # Populate NPM repository #
376 ###########################
377 if [ $NPM_PUSH == "true" ]; then
379 pushd ${NXS_SRC_NPM_DIR}
380 for NPM_LIST in "${NXS_NPM_LISTS[@]}"; do
381 patch_npm "${NPM_LIST}"
382 push_npm "${NPM_LIST}"
385 # Return default settings
387 npm config set registry "https://registry.npmjs.org"
390 ###############################
391 ## Populate PyPi repository #
392 ###############################
393 if [ $PYPI_PUSH == "true" ]; then
394 pushd ${NXS_SRC_PYPI_DIR}
395 for PYPI_LIST in "${NXS_PYPI_LISTS[@]}"; do
396 push_pip "${PYPI_LIST}"
401 ###############################
402 ## Populate Docker repository #
403 ###############################
405 # Login to docker registry simulated by Nexus container
406 # Push images to private nexus based on the lists
407 # All images need to be tagged to simulated registry
408 # and those without defined repository in tag use default repository 'library'
410 for DOCKER_IMG_LIST in "${NXS_DOCKER_IMG_LISTS[@]}"; do
411 push_docker "${DOCKER_IMG_LIST}"
414 ##############################
415 # Stop the Nexus and cleanup #
416 ##############################
418 echo "Stopping Nexus and returning backups"
421 docker stop ${NEXUS_CONT_ID} > /dev/null
423 if [ -f ~/.docker/${DOCKER_CONF_BACKUP} ]; then
424 mv -f ~/.docker/${DOCKER_CONF_BACKUP} ~/.docker/config.json
427 echo "Nexus blob is built"