#!/bin/bash ####################################################################################### # Copyright ETSI Contributors and Others. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. ####################################################################################### APT_PROXY="" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" HTTPDDIR="${HOME}/.osm/httpd" HTTPPORT=8000 KUBECFG="~/.osm/microk8s-config.yaml" NO_CACHE="" OPENSTACKRC="/var/snap/microstack/common/etc/microstack.rc" REGISTRY="localhost:32000" ROOTDIR="$( cd "${DIR}/../../" &> /dev/null && pwd)" OSM_TESTS_IMAGE_TAG="devel" function check_arguments(){ while [ $# -gt 0 ] ; do case $1 in --debug) set -x ;; --apt-proxy) APT_PROXY="$2" && shift ;; --help | -h) show_help && exit 0 ;; --httpddir) HTTPDDIR="$2" && shift;; --install-local-registry) 'install_local_registry' ;; --install-microstack) 'install_microstack' ;; --install-qhttpd) INSTALL_HTTPD='install_qhttpd' ;; --run-httpserver) INSTALL_HTTPD='run_httpserver' ;; --kubecfg) KUBECFG="$2" && shift ;; --module) TARGET_MODULE="$2" && shift;; --no-cache) NO_CACHE="--no-cache" ;; --openstackrc) OPENSTACKRC="$2" && shift ;; --registry) REGISTRY="$2" && shift;; --robot-local-mounts) ROBOT_LOCAL=YES ;; --run-tests) TESTS=YES ;; --vim-vca) VIM_VCA="$2" && shift;; --osm-tests-image-tag) OSM_TESTS_IMAGE_TAG="$2" && shift;; stage-2) STAGE_2='stage_2 ${TARGET_MODULE}' ;; stage-3) STAGE_3='stage_3 ${TARGET_MODULE}' ;; registry-push) REGISTRY_PUSH='local_registry_push ${TARGET_MODULE}' ;; install-osm) INSTALL_OSM='install_osm' ;; start-robot) START_ROBOT='start_robot' ;; update-install) UPDATE_INSTALL='update_osm_module ${TARGET_MODULE}' REGISTRY_PUSH='local_registry_push ${TARGET_MODULE}' ;; *) echo "Unknown option $1" show_help exit 1;; esac shift done } function show_help() { cat << EOF Usage: $0 [OPTIONS] Perform a local build and potential installation of OSM from sources, using the same process as Jenkins. OPTIONS: --help display this help message --apt-proxy provide an apt proxy to docker build steps --debug enable set -x for this script --install-local-registry install and enable Microk8s local registry on port 32000 --install-microstack install Microstack and configure to run robot tests --install-qhttpd (deprecated, use --run-httpserver instead) install QHTTPD as an HTTP server on port ${HTTPPORT} --run-httpserver run HTTP server on port ${HTTPPORT} --kubecfg path to kubecfg.yaml (uses Charmed OSM by default) --no-cache do not use any cache when building docker images --module only build this comma delimited list of modules --openstackrc path to Openstack RC file (uses Microstack by default) --registry use this alternate docker registry --run-tests run stage 2 tests --vim-vca name of the a vca registered in OSM to use in the VIM account --osm-tests-image-tag tag to be used in the osm/tests docker image stage-2 run the stage 2 build stage-3 run the stage 3 build registry-push push to the local registry install-osm perform full installation of Charmed OSM from registry start-robot start the Robot test container and leave you at prompt update-install update Charmed OSM with new module container A typical use could be the following: Let's assume that we have different repos cloned in the folder workspace: cd workspace git clone https://osm.etsi.org/gerrit/osm/devops git clone https://osm.etsi.org/gerrit/osm/NBI git clone https://osm.etsi.org/gerrit/osm/LCM git clone "https://osm.etsi.org/gerrit/osm/RO git clone "https://osm.etsi.org/gerrit/osm/common git clone "https://osm.etsi.org/gerrit/osm/IM git clone "https://osm.etsi.org/gerrit/osm/N2VC First we run a light HTTP server to serve the artifacts: devops/tools/local-build.sh --run-httpserver Then we generate the artifacts (debian packages) for the different repos: common, IM, N2VC, RO, LCM, NBI devops/tools/local-build.sh --module common,IM,N2VC,RO,LCM,NBI stage-2 Then new docker images are generated locally with the tag "devel" (e.g.: opensourcemano/lcm:devel): devops/tools/local-build.sh --module RO,LCM,NBI stage-3 Finally, the deployment of OSM will have to be updated to use the new docker images. EOF } function print_section() { echo "$@" } function install_local_registry() { sudo snap install microk8s --classic microk8s status --wait-ready microk8s.enable registry } function install_microstack() { sudo snap install microstack --devmode --edge sudo snap set microstack config.network.ports.dashboard=8080 sudo microstack.init --auto --control sudo snap alias microstack.openstack openstack . /var/snap/microstack/common/etc/microstack.rc for i in $(microstack.openstack security group list | awk '/default/{ print $2 }'); do microstack.openstack security group rule create $i --protocol icmp --remote-ip 0.0.0.0/0 microstack.openstack security group rule create $i --protocol tcp --remote-ip 0.0.0.0/0 done microstack.openstack network create --enable --no-share osm-ext microstack.openstack subnet create osm-ext-subnet --network osm-ext --dns-nameserver 8.8.8.8 \ --subnet-range 172.30.0.0/24 microstack.openstack router create external-router microstack.openstack router add subnet external-router osm-ext-subnet microstack.openstack router set --external-gateway external external-router curl -L https://github.com/cirros-dev/cirros/releases/download/0.3.5/cirros-0.3.5-x86_64-disk.img \ | microstack.openstack image create --public --container-format=bare \ --disk-format=qcow2 cirros-0.3.5-x86_64-disk.img curl https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img \ | microstack.openstack image create --public --container-format=bare \ --disk-format=qcow2 ubuntu16.04 curl https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img \ | microstack.openstack image create --public --container-format=bare \ --disk-format=qcow2 US1604 curl https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img \ | microstack.openstack image create --public --container-format=bare \ --disk-format=qcow2 ubuntu18.04 curl https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img \ | microstack.openstack image create --public --container-format=bare \ --disk-format=qcow2 ubuntu20.04 } function create_httpddir() { mkdir -p ${HTTPDDIR} } function install_qhttpd() { sudo snap install qhttp EXISTING_PID=$(ps auxw | grep "http.server $HTTPPORT" | grep -v grep | awk '{print $2}') if [ ! -z $EXISTING_PID ] ; then kill $EXISTING_PID fi qhttp -p ${HTTPPORT} & } function run_httpserver() { EXISTING_PID=$(ps auxw | grep "http.server $HTTPPORT" | grep -v grep | awk '{print $2}') if [ ! -z $EXISTING_PID ] ; then kill $EXISTING_PID fi nohup python3 -m http.server ${HTTPPORT} --directory "${HTTPDDIR}" &>/dev/null & } function stage_2() { print_section "Performing Stage 2" MODULES="common devops IM LCM MON N2VC NBI NG-UI NG-SA osmclient PLA POL RO tests" if [ ! -z ${1} ] ; then POSSIBLE_MODULES=$(echo ${1} | sed "s/,/ /g") for MODULE in ${POSSIBLE_MODULES}; do if ! echo "${MODULES}" | grep -q "${MODULE}" ; then echo "Unknown stage 2 module ${MODULE}" echo "Must be one of ${MODULES}" exit 1 fi done MODULES=${POSSIBLE_MODULES} else print_section "Cleaning HTTP Directory for full build" rm -fv ${HTTPDDIR}/*.deb fi for MODULE in ${MODULES} ; do cd "${ROOTDIR}" if [ ! -d ${MODULE} ] ; then echo "Directory ${ROOTDIR}/${MODULE} does not exist" exit 1 fi print_section "Building ${MODULE}" cd ${MODULE} find . -name '*.deb' -exec rm -v {} \; BUILD_ARGS="" if [ ! -z $APT_PROXY ] ; then BUILD_ARGS="${BUILD_ARGS}--build-arg APT_PROXY=${APT_PROXY} " fi docker build ${NO_CACHE} ${BUILD_ARGS} -t ${MODULE,,}-stage2 . STAGES="stage-build.sh" if [ ! -z $TESTS ] ; then STAGES="stage-test.sh ${STAGES}" fi for STAGE in $STAGES ; do docker run -ti \ -v "$(pwd):/build" \ -w /build \ ${MODULE,,}-stage2 \ bash -c "groupadd -o -g $(id -g) -r $USER ; useradd -o -u $(id -u) -d /build -r -g $USER $USER ; runuser $USER -c devops-stages/${STAGE}" if [ $? -ne 0 ] ; then print_section "Failed to build ${MODULE}" exit 1 fi done find . -name '*.deb' -exec mv -v {} ${HTTPDDIR}/ \; done } function _find_module_dockerfile() { cd "${ROOTDIR}/devops/docker" MODULES=`find . -name Dockerfile -printf '%h\n' |sed 's|\./||' |sort |tr '\n' ' '` if [ ! -z ${1} ] ; then POSSIBLE_MODULES=$(echo ${1} | sed "s/,/ /g") for MODULE in ${POSSIBLE_MODULES}; do if ! echo "${MODULES}" | grep -q "${MODULE}" ; then echo "Unknown stage 3 module ${MODULE}" echo "Must be one of ${MODULES}" exit 1 fi done echo ${POSSIBLE_MODULES} else echo ${MODULES} fi } function stage_3() { print_section "Performing Stage 3" MODULES=$(_find_module_dockerfile $1) BUILD_ARGS="" if [ ! -z $APT_PROXY ] ; then BUILD_ARGS="${BUILD_ARGS}--build-arg APT_PROXY=${APT_PROXY} " fi HOSTIP=$(ip -4 addr show docker0 | grep -Po 'inet \K[\d.]+') [ -z "$DEFAULT_IF" ] && DEFAULT_IF=$(ip route list|awk '$1=="default" {print $5; exit}') [ -z "$DEFAULT_IF" ] && DEFAULT_IF=$(route -n |awk '$1~/^0.0.0.0/ {print $8; exit}') DEFAULT_IP=$(ip -o -4 a s ${DEFAULT_IF} |awk '{split($4,a,"/"); print a[1]; exit}') HOSTIP=${HOSTIP:=${DEFAULT_IP}} echo $HOSTIP for file in ${HTTPDDIR}/*.deb ; do file=`basename ${file}` name=`echo ${file} | cut -d_ -f1 | sed "s/-/_/g"`; name=${name^^}_URL BUILD_ARGS="${BUILD_ARGS}--build-arg ${name}=http://$HOSTIP:${HTTPPORT}/$file " echo Added ${name} as http://$HOSTIP:${HTTPPORT}/$file done for MODULE in ${MODULES} ; do cd "${ROOTDIR}/devops/docker" if [ ! -d ${MODULE} ] ; then echo "Directory ${ROOTDIR}/${MODULE} does not exist" exit 1 fi print_section "Building ${MODULE}" cd ${MODULE} MODULE=${MODULE,,} docker build ${NO_CACHE} -t opensourcemano/${MODULE}:devel ${BUILD_ARGS} . if [ $? -ne 0 ] ; then print_section "Failed to build ${MODULE}" exit 1 fi done } function local_registry_push() { print_section "Pushing to local registry" cd "${ROOTDIR}/devops/docker" MODULES=`find . -name Dockerfile -printf '%h\n' |sed 's|\./||' |sort |tr '\n' ' '` if [ ! -z ${1} ] ; then POSSIBLE_MODULES=$(echo ${1} | sed "s/,/ /g") for MODULE in ${POSSIBLE_MODULES}; do echo "${MODULE}" if ! echo "${MODULES}" | grep -q "${MODULE}" ; then echo "Unknown stage 3 module ${MODULE}" echo "Must be one of ${MODULES}" exit 1 fi done MODULES=${POSSIBLE_MODULES} fi for MODULE in ${MODULES} ; do MODULE=${MODULE,,} docker tag opensourcemano/${MODULE}:devel ${REGISTRY}/opensourcemano/${MODULE}:devel docker push ${REGISTRY}/opensourcemano/${MODULE}:devel done } function install_osm() { cd "${ROOTDIR}/devops/installers" VCA="" if juju controllers 2>/dev/null| grep osm-vca ; then VCA="--vca osm-vca" fi ./charmed_install.sh --registry localhost:32000 --tag devel ${VCA} } function start_robot() { mkdir -p "${ROOTDIR}/tests/local" cd "${ROOTDIR}/tests/local" . ${OPENSTACKRC} # Workaround for microstack auth URL if [ ${OPENSTACKRC} == "/var/snap/microstack/common/etc/microstack.rc" ] ; then export OS_AUTH_URL=${OS_AUTH_URL}/v3 fi export OSM_HOSTNAME=$(juju config -m osm nbi site_url | sed "s/http.*\?:\/\///"):443 export PROMETHEUS_HOSTNAME=$(juju config -m osm prometheus site_url | sed "s/http.*\?:\/\///") export PROMETHEUS_PORT=80 export JUJU_PASSWORD=`juju gui 2>&1 | grep password | awk '{print $2}'` export HOSTIP=$(echo $PROMETHEUS_HOSTNAME | sed "s/prometheus.//" | sed "s/.nip.io//") rm robot-systest.cfg for line in `env | grep "^OS_" | sort` ; do echo $line >> robot-systest.cfg ; done cat << EOF >> robot-systest.cfg VIM_TARGET=osm VIM_MGMT_NET=osm-ext ENVIRONMENTS_FOLDER=environments PACKAGES_FOLDER=/robot-systest/osm-packages OS_CLOUD=openstack LC_ALL=C.UTF-8 LANG=C.UTF-8 EOF cat << EOF > robot.etc.hosts 127.0.0.1 localhost ${HOSTIP} prometheus.${HOSTIP}.nip.io nbi.${HOSTIP}.nip.io EOF cat << EOF > clouds.yaml clouds: openstack: auth: auth_url: $OS_AUTH_URL project_name: $OS_PROJECT_NAME username: $OS_USERNAME password: $OS_PASSWORD user_domain_name: $OS_USER_DOMAIN_NAME project_domain_name: $OS_PROJECT_DOMAIN_NAME EOF VIM_AUTH_URL=$(osm vim-show osm | grep vim_url | awk '{print $4}' | tr -d \") if [[ ! -z ${VIM_AUTH_URL} && "$OS_AUTH_URL" != "${VIM_AUTH_URL}" ]] ; then echo "Deleting existing VIM osm as auth URLs have changed" osm vim-delete osm fi if ! osm vim-show osm &> /dev/null ; then echo "Creating VIM osm" if [ -v VIM_VCA ]; then VCA_OPT="--vca $VIM_VCA" fi osm vim-create --name osm $VCA_OPT --user "$OS_USERNAME" --password "$OS_PASSWORD" \ --auth_url "$OS_AUTH_URL" --tenant "$OS_USERNAME" --account_type openstack \ --config='{use_floating_ip: True, management_network_name: osm-ext}' fi if [ ! -z $ROBOT_LOCAL ] ; then LOCAL_MOUNT_1="/robot-systest/lib" LOCAL_MOUNT_2="/robot-systest/resources" LOCAL_MOUNT_3="/robot-systest/testsuite" else LOCAL_MOUNT_1="/tmp/lib" LOCAL_MOUNT_2="/tmp/resources" LOCAL_MOUNT_3="/tmp/testsuite" fi mkdir -p reports docker run -ti --entrypoint /bin/bash \ --env OSM_HOSTNAME=${OSM_HOSTNAME} \ --env PROMETHEUS_HOSTNAME=${PROMETHEUS_HOSTNAME} \ --env PROMETHEUS_PORT=${PROMETHEUS_PORT} \ --env JUJU_PASSWORD=${JUJU_PASSWORD} \ --env HOSTIP=${HOSTIP} \ --env-file robot-systest.cfg \ -v "$(pwd)/robot.etc.hosts":/etc/hosts \ -v ~/.osm/microk8s-config.yaml:/root/.kube/config \ -v "$(pwd)/clouds.yaml":/etc/openstack/clouds.yaml \ -v "${HOME}/snap/qhttp/common"/robot-systest/reports \ -v "${HOME}/snap/qhttp/common:"/robot-systest/conformance-tests/reports \ -v "${ROOTDIR}/tests/robot-systest/lib":${LOCAL_MOUNT_1} \ -v "${ROOTDIR}/tests/robot-systest/resources":${LOCAL_MOUNT_2} \ -v "${ROOTDIR}/tests/robot-systest/testsuite":${LOCAL_MOUNT_3} \ opensourcemano/tests:$OSM_TESTS_IMAGE_TAG } function update_osm_module() { MODULES=$(_find_module_dockerfile $1) for MODULE in ${MODULES} ; do MODULE=${MODULE,,} echo "Updating ${MODULE}" juju attach-resource ${MODULE} image=localhost:32000/opensourcemano/${MODULE}:devel done } if [ "$0" != "$BASH_SOURCE" ]; then _osm_local_build() { OPTIONS=$(show_help | sed '0,/^OPTIONS:$/d' | awk '{print $1}') COMPREPLY=($(compgen -W "${OPTIONS}" -- "${COMP_WORDS[-1]}")) } THIS_SCRIPT="$(basename ${BASH_SOURCE[0]})" echo "Setting up bash completion for ${THIS_SCRIPT}" complete -F _osm_local_build "${THIS_SCRIPT}" else check_arguments $@ create_httpddir eval "${INSTALL_HTTPD}" eval "${INSTALL_LOCAL_REGISTRY}" eval "${INSTALL_MICROSTACK}" eval "${STAGE_2}" eval "${STAGE_3}" eval "${REGISTRY_PUSH}" eval "${INSTALL_OSM}" eval "${UPDATE_INSTALL}" eval "${START_ROBOT}" fi