From 97e8d9a3254989f086d7ad02c168442832b49946 Mon Sep 17 00:00:00 2001 From: Abukar Mohamed Date: Thu, 2 May 2019 06:11:43 +0000 Subject: [PATCH] Initial RIC Configuration Management Co-developer: Juha Hyttinen Change-Id: I39957dcf65ea688feaf7cd6b0e7f87cb1a94e53d Signed-off-by: Abukar Mohamed --- Dockerfile | 178 ++++++++++++++++++++++++++ Makefile | 144 ++++----------------- api/appmgr_rest_api.json | 7 +- appmgr-entrypoint.sh | 24 ++++ build/build_entrypoint.sh | 49 +++++++ build/make.docker.mk | 128 +++++++++++++++++++ build/make.go.mk | 100 +++++++++++++++ build/user_entrypoint.sh | 81 ++++++++++++ cmd/appmgr/api.go | 58 +++++++-- cmd/appmgr/api_test.go | 2 +- cmd/appmgr/db.go | 2 +- cmd/appmgr/desc.go | 267 +++++++++++++++++++++++++++++++++++++++ cmd/appmgr/helm.go | 103 ++++++--------- cmd/appmgr/logger.go | 2 +- cmd/appmgr/subscriptions_test.go | 2 +- cmd/appmgr/types.go | 12 +- config/appmgr.yaml | 9 +- go.mod | 8 +- go.sum | 19 +++ helm_chart/appmgr/values.yaml | 14 +- 20 files changed, 994 insertions(+), 215 deletions(-) create mode 100755 Dockerfile create mode 100755 appmgr-entrypoint.sh create mode 100644 build/build_entrypoint.sh create mode 100644 build/make.docker.mk create mode 100644 build/make.go.mk create mode 100644 build/user_entrypoint.sh create mode 100755 cmd/appmgr/desc.go diff --git a/Dockerfile b/Dockerfile new file mode 100755 index 0000000..b2b314b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,178 @@ +# Copyright (c) 2019 AT&T Intellectual Property. +# Copyright (c) 2019 Nokia. +# +# 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. + +#---------------------------------------------------------- +# +#---------------------------------------------------------- +FROM ubuntu:16.04 as xapp-base + +RUN apt-get update -y && \ + apt-get install -y wget + +RUN sed -i -e "s,http://archive.ubuntu.com/ubuntu,$(wget -qO - mirrors.ubuntu.com/mirrors.txt | head -1)," /etc/apt/sources.list +RUN sed -i -e "s,http://security.ubuntu.com/ubuntu,$(wget -qO - mirrors.ubuntu.com/mirrors.txt | head -1)," /etc/apt/sources.list + +# +# packages +# +RUN apt-get update -y && \ + apt-get upgrade -y && \ + apt-get install -y \ + build-essential \ + apt-utils \ + cmake \ + make \ + autoconf \ + autoconf-archive \ + gawk \ + libtool \ + automake \ + pkg-config \ + sudo \ + wget \ + nano \ + git \ + jq + + +# +# go +# +RUN wget https://dl.google.com/go/go1.12.linux-amd64.tar.gz && \ + tar -C /usr/local -xvf ./go1.12.linux-amd64.tar.gz + +ENV PATH="/usr/local/go/bin:${PATH}" + +# +# rancodev libs +# +RUN mkdir -p /opt/build \ + && cd /opt/build && git clone https://gerrit.oran-osc.org/r/com/log \ + && cd log/ ; ./autogen.sh ; ./configure ; make ; make install \ + && ldconfig + +COPY build/build_entrypoint.sh / +COPY build/user_entrypoint.sh / + +RUN chmod +x /build_entrypoint.sh +RUN chmod +x /user_entrypoint.sh + +RUN mkdir -p /ws +WORKDIR "/ws" +ENTRYPOINT ["/user_entrypoint.sh"] +CMD ["/bin/bash"] + +#---------------------------------------------------------- +# +#---------------------------------------------------------- +FROM xapp-base as appmgr-build + +ARG PACKAGEURL +ARG PACKAGEREPO +ARG SSH_PRIVATE_KEY +ARG NETRC_CONFIG +ARG HELMVERSION + + +# +# helm +# +RUN wget https://storage.googleapis.com/kubernetes-helm/helm-${HELMVERSION}-linux-amd64.tar.gz \ + && tar -zxvf helm-${HELMVERSION}-linux-amd64.tar.gz \ + && cp linux-amd64/helm /usr/bin/helm \ + && rm -rf helm-${HELMVERSION}-linux-amd64.tar.gz \ + && rm -rf linux-amd64 + +# Install kubectl from Docker Hub. +COPY --from=lachlanevenson/k8s-kubectl:v1.10.3 /usr/local/bin/kubectl /usr/local/bin/kubectl + +RUN mkdir -p /go/src/${PACKAGEURL} +WORKDIR "/go/src/${PACKAGEURL}" +ENV GOPATH="/go" + +# Module prepare (if go.mod/go.sum updated) +COPY go.mod /go/src/${PACKAGEURL} +COPY go.sum /go/src/${PACKAGEURL} +RUN GO111MODULE=on /build_entrypoint.sh go mod download + +# build +COPY . /go/src/${PACKAGEURL} +RUN /build_entrypoint.sh make -C /go/src/${PACKAGEURL} build + +ENTRYPOINT ["/build_entrypoint.sh"] +CMD ["/bin/bash"] + + +#---------------------------------------------------------- +# +#---------------------------------------------------------- +FROM appmgr-build as appmgr-test_unit +ARG PACKAGEURL +WORKDIR "/go/src/${PACKAGEURL}" +CMD ["make","go-test"] + + +#---------------------------------------------------------- +# +#---------------------------------------------------------- +FROM appmgr-build as appmgr-test_fmt +ARG PACKAGEURL +WORKDIR "/go/src/${PACKAGEURL}" +CMD ["make","go-test-fmt"] + +#---------------------------------------------------------- +# +#---------------------------------------------------------- +FROM appmgr-build as appmgr-test_sanity +ARG PACKAGEURL +WORKDIR "/go/src/${PACKAGEURL}" +CMD ["jq","-s",".", "api/appmgr_rest_api.json"] + + +#---------------------------------------------------------- +# +#---------------------------------------------------------- +FROM ubuntu:16.04 as appmgr +ARG PACKAGEURL + +RUN apt-get update -y \ + && apt-get install -y sudo openssl ca-certificates ca-cacert \ + && apt-get clean + + +# +# libraries and helm +# +COPY --from=appmgr-build /usr/local/include/ /usr/local/include/ +COPY --from=appmgr-build /usr/local/lib/ /usr/local/lib/ +COPY --from=appmgr-build /usr/bin/helm /usr/bin/helm +COPY --from=appmgr-build /usr/local/bin/kubectl /usr/bin/kubectl + +RUN ldconfig + +# +# xApp +# +RUN mkdir -p /opt/xAppManager \ + && chmod -R 755 /opt/xAppManager + +COPY --from=appmgr-build /go/src/${PACKAGEURL}/build/cache/cmd/appmgr /opt/xAppManager/appmgr +#COPY --from=appmgr-build /go/src/${PACKAGEURL}/config/appmgr.yaml /opt/etc/xAppManager/config-file.yaml + + +WORKDIR /opt/xAppManager + +COPY appmgr-entrypoint.sh /opt/xAppManager/ +ENTRYPOINT ["/opt/xAppManager/appmgr-entrypoint.sh"] diff --git a/Makefile b/Makefile index 8061874..51f53bb 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ + # Copyright (c) 2019 AT&T Intellectual Property. # Copyright (c) 2019 Nokia. # @@ -13,145 +14,52 @@ # See the License for the specific language governing permissions and # limitations under the License. +.DEFAULT: go-build -#------------------------------------------------------------------------------ -# -#-------------------------------------------------------------------- ---------- -ROOT_DIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST)))) -BUILD_DIR:=$(abspath $(ROOT_DIR)/build) - -PACKAGEURL:="gerrit.oran-osc.org/r/ric-plt/appmgr" -HELMVERSION:=v2.13.0-rc.1 - -#------------------------------------------------------------------------------ -# -#-------------------------------------------------------------------- ---------- -COVEROUT := $(abspath $(BUILD_DIR)/cover.out) -COVERHTML := $(abspath $(BUILD_DIR)/cover.html) - -GOOS=$(shell go env GOOS) -GOCMD=go -GOBUILD=$(GOCMD) build -a -installsuffix cgo -GORUN=$(GOCMD) run -a -installsuffix cgo -GOCLEAN=$(GOCMD) clean -GOTEST=$(GOCMD) test -v -coverprofile $(COVEROUT) -GOGET=$(GOCMD) get +default: go-build -GOFILES := $(shell find $(ROOT_DIR) -name '*.go' -not -name '*_test.go') go.mod go.sum -GOFILES_NO_VENDOR := $(shell find $(ROOT_DIR) -path ./vendor -prune -o -name "*.go" -not -name '*_test.go' -print) +build: go-build -CMDS:=$(BUILD_DIR)/appmgr +test: go-test #------------------------------------------------------------------------------ # -#-------------------------------------------------------------------- ---------- - .DEFAULT: build - -default: build - -.PHONY: FORCE - -FORCE: - -#------------------------------------------------------------------------------ +# Build and test targets # #------------------------------------------------------------------------------ +ROOT_DIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +BUILD_DIR:=$(abspath $(ROOT_DIR)/build) -$(CMDS): $(GOFILES) - GO111MODULE=on GO_ENABLED=0 GOOS=linux $(GOBUILD) -o $@ ./cmd/$(shell basename "$@") - - -$(addsuffix _test,$(CMDS)): $(GOFILES) - GO111MODULE=on GO_ENABLED=0 GOOS=linux $(GOTEST) -c -o $@ ./cmd/$(patsubst %_test,%, $(shell basename "$@")) - timeout -s KILL 5s $@ -test.coverprofile $(COVEROUT) - go tool cover -html=$(COVEROUT) -o $(COVERHTML) - - -build: $(CMDS) - - -test: $(addsuffix _test,$(CMDS)) - - -test-fmt: $(GOFILES_NO_VENDOR) - @(RESULT="$$(gofmt -l $^)"; test -z "$${RESULT}" || (echo -e "gofmt failed:\n$${RESULT}" && false) ) - - -fmt: $(GOFILES_NO_VENDOR) - gofmt -w -s $^ +XAPP_NAME:=appmgr +XAPP_ROOT:=cmd +XAPP_TESTENV:="RMR_SEED_RT=config/uta_rtg.rt CFG_FILE=$(ROOT_DIR)helm_chart/uemgr/descriptors/config-file.json" +include build/make.go.mk -clean: - @echo " > Cleaning build cache" - @-rm -rf $(CMDS)* 2> /dev/null - go clean 2> /dev/null #------------------------------------------------------------------------------ # +# DOCKER TARGETS +# #------------------------------------------------------------------------------ -BUILD_PREFIX?="${USER}-" - -DCKR_FILE:=docker/Dockerfile - -DCKR_NAME:=${BUILD_PREFIX}appmgr -DCKR_NAME:=$(shell echo $(DCKR_NAME) | tr '[:upper:]' '[:lower:]') -DCKR_NAME:=$(subst /,_,${DCKR_NAME}) - -DCKR_BUILD_OPTS:=${DCKR_BUILD_OPTS} --network=host --build-arg HELMVERSION=${HELMVERSION} --build-arg PACKAGEURL=${PACKAGEURL} - -DCKR_RUN_OPTS:=${DCKR_RUN_OPTS} --rm -i -DCKR_RUN_OPTS:=${DCKR_RUN_OPTS}$(shell test -t 0 && echo ' -t') -DCKR_RUN_OPTS:=${DCKR_RUN_OPTS}$(shell test -e /etc/localtime && echo ' -v /etc/localtime:/etc/localtime:ro') -DCKR_RUN_OPTS:=${DCKR_RUN_OPTS}$(shell test -e /var/run/docker.sock && echo ' -v /var/run/docker.sock:/var/run/docker.sock') - +HELMVERSION:=v2.13.0-rc.1 +DCKR_B_OPTS:=${DCKR_B_OPTS} --build-arg HELMVERSION=${HELMVERSION} -#------------------------------------------------------------------------------ -# -#------------------------------------------------------------------------------ -docker-name: - @echo $(DCKR_NAME) +PACKAGEURL:="gerrit.oran-osc.org/r/ric-plt/appmgr" -docker-build: - docker build --target release ${DCKR_BUILD_OPTS} -t $(DCKR_NAME) -f $(DCKR_FILE) . +DCKR_NAME:=appmgr-test_unit +include build/make.docker.mk -docker-run: - docker run ${DCKR_RUN_OPTS} -v /opt/ric:/opt/ric -p 8080:8080 $(DCKR_NAME) +DCKR_NAME:=appmgr-test_fmt +include build/make.docker.mk -docker-clean: - docker rmi $(DCKR_NAME) +DCKR_NAME:=appmgr-test_sanity +include build/make.docker.mk +DCKR_NAME:=appmgr +include build/make.docker.mk -#------------------------------------------------------------------------------ -# -#------------------------------------------------------------------------------ -docker-test-build: - docker build --target test_unit ${DCKR_BUILD_OPTS} -t ${DCKR_NAME}-test_unit -f $(DCKR_FILE) . - docker build --target test_sanity ${DCKR_BUILD_OPTS} -t ${DCKR_NAME}-test_sanity -f $(DCKR_FILE) . - docker build --target test_fmt ${DCKR_BUILD_OPTS} -t ${DCKR_NAME}-test_fmt -f $(DCKR_FILE) . - -docker-test-run-unit: - @( \ - RETVAL=0;\ - docker network create --driver bridge ${DCKR_NAME}-test_unit_network;\ - docker run ${DCKR_RUN_OPTS} -d --name ${DCKR_NAME}-test_unit_redis --network ${DCKR_NAME}-test_unit_network redis;\ - docker run ${DCKR_RUN_OPTS} --name ${DCKR_NAME}-test_unit_run --network ${DCKR_NAME}-test_unit_network -e DBAAS_SERVICE_HOST=${DCKR_NAME}-test_unit_redis ${DCKR_NAME}-test_unit;\ - RETVAL=$$?;\ - docker stop ${DCKR_NAME}-test_unit_redis;\ - docker network rm ${DCKR_NAME}-test_unit_network;\ - exit $${RETVAL};\ - ) - - -docker-test-run-fmt: - docker run ${DCKR_RUN_OPTS} ${DCKR_NAME}-test_fmt - -docker-test-run-sanity: - docker run ${DCKR_RUN_OPTS} ${DCKR_NAME}-test_sanity - -docker-test-clean: - docker rmi -f ${DCKR_NAME}-test_unit - docker rmi -f ${DCKR_NAME}-test_sanity - docker rmi -f ${DCKR_NAME}-test_fmt +docker-test: docker-run_appmgr-test_fmt docker-run_appmgr-test_sanity docker-run-redished_appmgr-test_unit diff --git a/api/appmgr_rest_api.json b/api/appmgr_rest_api.json index c5a4c0b..66250ae 100644 --- a/api/appmgr_rest_api.json +++ b/api/appmgr_rest_api.json @@ -2,7 +2,7 @@ "swagger": "2.0", "info": { "description": "This is a draft API for RIC appmgr", - "version": "0.0.10", + "version": "0.0.11", "title": "RIC appmgr", "license": { "name": "Apache 2.0", @@ -474,6 +474,7 @@ "enum": [ "created", "deleted", + "updated", "all" ] } @@ -502,6 +503,7 @@ "enum": [ "created", "deleted", + "updated", "all" ] }, @@ -533,7 +535,8 @@ "description": "Event to be notified", "enum": [ "created", - "deleted" + "deleted", + "updated" ] }, "xApps": { diff --git a/appmgr-entrypoint.sh b/appmgr-entrypoint.sh new file mode 100755 index 0000000..80e99df --- /dev/null +++ b/appmgr-entrypoint.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Copyright (c) 2019 AT&T Intellectual Property. +# Copyright (c) 2019 Nokia. +# +# 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. + +cp /opt/ric/config/appmgr.yaml /opt/xAppManager/config-file.yaml + +# Copy all certificates from mounted folder to root system +cp /opt/ric/certificates/* /etc/ssl/certs + +# Start services, etc. +/opt/xAppManager/appmgr -f /opt/xAppManager/config-file.yaml diff --git a/build/build_entrypoint.sh b/build/build_entrypoint.sh new file mode 100644 index 0000000..16f587f --- /dev/null +++ b/build/build_entrypoint.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# Copyright (c) 2019 AT&T Intellectual Property. +# Copyright (c) 2019 Nokia. +# +# 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. +# + +# +# SSH +# +if [ -n "${SSH_PRIVATE_KEY}" ] && [ -n "${PACKAGEREPO}" ] ; then + + # ssh configs (no private key) + mkdir -p ${HOME}/.ssh + test -n "${PACKAGEREPO}" && ssh-keyscan -H ${PACKAGEREPO} >> ${HOME}/.ssh/known_hosts + echo -e "IdentityFile ~/.ssh/id_rsa\n" > ${HOME}/.ssh/config + echo -e "Host *\n\tStrictHostKeyChecking no\n\n" >> ${HOME}/.ssh/config + chmod -R go= ${HOME}/.ssh/ + chmod -R u+rw ${HOME}/.ssh/ + + # gitconfig + test -n "${PACKAGEREPO}" && echo -e "[url \"ssh://git@${PACKAGEREPO}/\"]\n\tinsteadOf = https://${PACKAGEREPO}/" > ${HOME}/.gitconfig + + # ssh agent + TEMPFILE=/dev/shm/deployment.key + echo "$SSH_PRIVATE_KEY" > $TEMPFILE + chmod 0600 $TEMPFILE + eval $(ssh-agent) + ssh-add $TEMPFILE + rm $TEMPFILE +fi + +if [ -n "${NETRC_CONFIG}" ] ; then + echo "${NETRC_CONFIG}" > /root/.netrc +fi + + +exec $* diff --git a/build/make.docker.mk b/build/make.docker.mk new file mode 100644 index 0000000..8bb75ac --- /dev/null +++ b/build/make.docker.mk @@ -0,0 +1,128 @@ +# Copyright (c) 2019 AT&T Intellectual Property. +# Copyright (c) 2019 Nokia. +# +# 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. + +#------------------------------------------------------------------------------ +# +#------------------------------------------------------------------------------ +ifndef MAKE_DOCKER_TARGETS +MAKE_DOCKER_TARGETS:=1 + +.PHONY: docker-build docker-clean + + +docker-name_%: + @echo $($*_DCKR_FULLNAME) + +docker-build_%: + @(\ + test -z "$${SSH_PRIVATE_KEY}" && SSH_PRIVATE_KEY=$$(cat $${HOME}/.ssh/id_rsa);\ + docker build --target $* $($*_DCKR_B_OPTS) --build-arg SSH_PRIVATE_KEY="$${SSH_PRIVATE_KEY}" -t $($*_DCKR_FULLNAME) -f $($*_DCKR_FILE) . ;\ + ) + +docker-irun_%: + docker run $($*_DCKR_R_OPTS) $($*_DCKR_FULLNAME) /bin/bash + +docker-irun-mounted_%: + docker run $($*_DCKR_R_OPTS) -v $(shell pwd):/ws/go/src/${PACKAGEURL} --workdir "/ws/go/src/${PACKAGEURL}" $($*_DCKR_FULLNAME) /bin/bash + +docker-run_%: + docker run $($*_DCKR_R_OPTS) $($*_DCKR_FULLNAME) + +docker-run-redished_%: + @( \ + RETVAL=0;\ + docker network create --driver bridge $($*_DCKR_FULLNAME)-run_network;\ + docker run $($*_DCKR_R_OPTS) -d --name $($*_DCKR_FULLNAME)-run_redis --network $($*_DCKR_FULLNAME)-run_network redis;\ + docker run $($*_DCKR_R_OPTS) --name $($*_DCKR_FULLNAME)-run_xapp --network $($*_DCKR_FULLNAME)-run_network -e DBAAS_SERVICE_HOST=$($*_DCKR_FULLNAME)-run_redis $($*_DCKR_FULLNAME);\ + RETVAL=$$?;\ + docker stop $($*_DCKR_FULLNAME)-run_redis;\ + docker network rm $($*_DCKR_FULLNAME)-run_network;\ + exit $${RETVAL};\ + ) + +docker-clean_%: + docker rmi $($*_DCKR_FULLNAME) || true + + +.SECONDEXPANSION: +docker-build: DCKR_TARGETS:= +docker-build: $$(DCKR_TARGETS) + +.SECONDEXPANSION: +docker-clean: DCKR_TARGETS:= +docker-clean: $$(DCKR_TARGETS) + +endif + +#------------------------------------------------------------------------------ +# +#------------------------------------------------------------------------------ + +ifndef DCKR_FILE +DCKR_FILE:="Dockerfile" +endif + +ifndef BUILD_PREFIX +BUILD_PREFIX:="${USER}-" +endif + + +#------------------------------------------------------------------------------ +# +#------------------------------------------------------------------------------ + +ifndef $(DCKR_NAME)_DCKR_B_PREFIX +$(DCKR_NAME)_DCKR_B_PREFIX:=$(BUILD_PREFIX) +endif + +ifndef $(DCKR_NAME)_DCKR_FILE +$(DCKR_NAME)_DCKR_FILE:=$(DCKR_FILE) +endif + +$(DCKR_NAME)_DCKR_B_PREFIX:=$(subst /,_,$(shell echo $($(DCKR_NAME)_DCKR_B_PREFIX) | tr '[:upper:]' '[:lower:]')) + +$(DCKR_NAME)_DCKR_FULLNAME:=$($(DCKR_NAME)_DCKR_B_PREFIX)$(DCKR_NAME) + +$(DCKR_NAME)_DCKR_B_OPTS:=${DCKR_B_OPTS} +$(DCKR_NAME)_DCKR_B_OPTS:=$($(DCKR_NAME)_DCKR_B_OPTS) --network=host + +ifndef PACKAGEURL +$(DCKR_NAME)_DCKR_B_OPTS:=$($(DCKR_NAME)_DCKR_B_OPTS) --build-arg PACKAGEURL=${PACKAGEURL} +endif + +ifndef PACKAGEREPO +$(DCKR_NAME)_DCKR_B_OPTS:=$($(DCKR_NAME)_DCKR_B_OPTS) --build-arg PACKAGEREPO=${PACKAGEREPO} +endif + +ifndef BUILD_PREFIX +$(DCKR_NAME)_DCKR_B_OPTS:=$($(DCKR_NAME)_DCKR_B_OPTS) --build-arg BUILD_PREFIX=${BUILD_PREFIX} +endif + + +$(DCKR_NAME)_DCKR_R_OPTS:=${DCKR_R_OPTS} +$(DCKR_NAME)_DCKR_R_OPTS:=$($(DCKR_NAME)_DCKR_R_OPTS) --rm -i --net=host +$(DCKR_NAME)_DCKR_R_OPTS:=$($(DCKR_NAME)_DCKR_R_OPTS)$(shell test -t 0 && echo ' -t') +$(DCKR_NAME)_DCKR_R_OPTS:=$($(DCKR_NAME)_DCKR_R_OPTS)$(shell test -e /etc/localtime && echo ' -v /etc/localtime:/etc/localtime:ro') +$(DCKR_NAME)_DCKR_R_OPTS:=$($(DCKR_NAME)_DCKR_R_OPTS)$(shell test -e /var/run/docker.sock && echo ' -v /var/run/docker.sock:/var/run/docker.sock') +$(DCKR_NAME)_DCKR_R_OPTS:=$($(DCKR_NAME)_DCKR_R_OPTS)$(shell test -e ${HOME}/.docker && echo ' -v ${HOME}/.docker:/ws/.docker:ro') +$(DCKR_NAME)_DCKR_R_OPTS:=$($(DCKR_NAME)_DCKR_R_OPTS)$(shell test -e ${HOME}/.netrc && echo ' -v ${HOME}/.netrc:/ws/.netrc:ro') +$(DCKR_NAME)_DCKR_R_OPTS:=$($(DCKR_NAME)_DCKR_R_OPTS)$(shell test -e ${HOME}/.ssh && echo ' -v ${HOME}/.ssh:/ws/.ssh:ro') +$(DCKR_NAME)_DCKR_R_OPTS:=$($(DCKR_NAME)_DCKR_R_OPTS)$(shell test -e ${HOME}/.gitconfig && echo ' -v ${HOME}/.gitconfig:/ws/.gitconfig:ro') + + +docker-build: DCKR_TARGETS+=docker-build_$(DCKR_NAME) + +docker-clean: DCKR_TARGETS+=docker-clean_$(DCKR_NAME) + diff --git a/build/make.go.mk b/build/make.go.mk new file mode 100644 index 0000000..18d5d44 --- /dev/null +++ b/build/make.go.mk @@ -0,0 +1,100 @@ +# Copyright (c) 2019 AT&T Intellectual Property. +# Copyright (c) 2019 Nokia. +# +# 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. + + +#------------------------------------------------------------------------------ +# +#------------------------------------------------------------------------------ +#ROOT_DIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +ifndef ROOT_DIR +$(error ROOT_DIR NOT DEFINED) +endif +BUILD_DIR?=$(abspath $(ROOT_DIR)/build) +CACHE_DIR?=$(abspath $(BUILD_DIR)/cache) + + +#------------------------------------------------------------------------------ +# +#-------------------------------------------------------------------- ---------- +ifndef MAKE_GO_TARGETS +MAKE_GO_TARGETS:=1 + + +GOOS=$(shell go env GOOS) +GOCMD=go +GOBUILD=$(GOCMD) build -a -installsuffix cgo +GORUN=$(GOCMD) run -a -installsuffix cgo +GOCLEAN=$(GOCMD) clean +GOTEST=$(GOCMD) test -v +GOGET=$(GOCMD) get + +GOFILES:=$(shell find $(ROOT_DIR) -name '*.go' -not -name '*_test.go') +GOALLFILES:=$(shell find $(ROOT_DIR) -name '*.go') +GOMODFILES:=go.mod go.sum + +.PHONY: FORCE go-build go-test go-test-fmt go-fmt go-clean + + +FORCE: + + +$(CACHE_DIR)/%: $(GOFILES) $(GOMODFILES) + @echo "Building:\t$*" + GO111MODULE=on GO_ENABLED=0 GOOS=linux $(GOBUILD) -o $@ ./$* + + +$(CACHE_DIR)/%_test: $(GOALLFILES) $(GOMODFILES) + @echo "Testing:\t$*" + GO111MODULE=on GO_ENABLED=0 GOOS=linux $(GOTEST) -coverprofile $(COVEROUT) -c -o $@ ./$* + test -e $@ && (eval $(TESTENV) $@ -test.coverprofile $(COVEROUT) || false) || true + test -e $@ && (go tool cover -html=$(COVEROUT) -o $(COVERHTML) || false) || true + + +.SECONDEXPANSION: +go-build: XAPP_TARGETS:= +go-build: $$(XAPP_TARGETS) + +.SECONDEXPANSION: +go-test: XAPP_TARGETS:= +go-test: go-clean $$(XAPP_TARGETS) + +go-test-fmt: $(GOFILES) + @(RESULT="$$(gofmt -l $^)"; test -z "$${RESULT}" || (echo -e "gofmt failed:\n$${RESULT}" && false) ) + +go-fmt: $(GOFILES) + gofmt -w -s $^ + +go-clean: XAPP_TARGETS:= +go-clean: + @echo " > Cleaning build cache" + @-rm -rf $(XAPP_TARGETS)* 2> /dev/null + go clean 2> /dev/null + + +endif + +#------------------------------------------------------------------------------ +# +#-------------------------------------------------------------------- ---------- + +$(CACHE_DIR)/$(XAPP_ROOT)/$(XAPP_NAME)_test: COVEROUT:=$(abspath $(CACHE_DIR)/$(XAPP_ROOT)/$(XAPP_NAME)_cover.out) +$(CACHE_DIR)/$(XAPP_ROOT)/$(XAPP_NAME)_test: COVERHTML:=$(abspath $(CACHE_DIR)/$(XAPP_ROOT)/$(XAPP_NAME)_cover.html) +$(CACHE_DIR)/$(XAPP_ROOT)/$(XAPP_NAME)_test: TESTENV:=$(XAPP_TESTENV) + +go-build: XAPP_TARGETS+=$(CACHE_DIR)/$(XAPP_ROOT)/$(XAPP_NAME) +go-test: XAPP_TARGETS+=$(CACHE_DIR)/$(XAPP_ROOT)/$(XAPP_NAME)_test +go-clean: XAPP_TARGETS+=$(CACHE_DIR)/$(XAPP_ROOT)/$(XAPP_NAME) $(CACHE_DIR)/$(XAPP_ROOT)/$(XAPP_NAME)_test + diff --git a/build/user_entrypoint.sh b/build/user_entrypoint.sh new file mode 100644 index 0000000..be63d35 --- /dev/null +++ b/build/user_entrypoint.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# Copyright (c) 2019 AT&T Intellectual Property. +# Copyright (c) 2019 Nokia. +# +# 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. +# + +source /etc/os-release + +if [ -z ${CONT_USER} ]; then CONT_USER=builder ; fi +if [ -z ${CONT_UID} ]; then CONT_UID=$(stat -c "%u" $(readlink -f .) ); fi +if [ -z ${CONT_GROUP} ]; then CONT_GROUP=builder; fi +if [ -z ${CONT_GID} ]; then CONT_GID=$(stat -c "%g" $(readlink -f .) ); fi + +if [ $(id -u) -eq $CONT_UID ] || [ $(id -u) -ne 0 ] ; then + exec "$@" +fi + +if [ $(getent group ${CONT_GROUP}) ] || [ $(getent group ${CONT_GID}) ] ; then + echo "group conflict" + exit 0 +fi +if [ $(getent passwd ${CONT_USER}) ] || [ $(getent passwd ${CONT_UID}) ] ; then + echo "passwd conflict" + exit 0 +fi + +if [[ $ID == "ubuntu" ]] ; then + groupadd --gid ${CONT_GID} ${CONT_GROUP} || exit 10 + useradd --shell /bin/bash --uid ${CONT_UID} --gid ${CONT_GID} -o -d /ws $(test -d /ws && echo "-M" || echo "-m") --groups $CONT_GID ${CONT_USER}|| exit 11 +fi +if [[ $ID == "alpine" ]] ; then + addgroup -g ${CONT_GID} ${CONT_GROUP} || exit 10 + adduser -s /bin/bash -u ${CONT_UID} -G ${CONT_GROUP} -h /ws $(test -d /ws && echo "-H") -D ${CONT_USER} || exit 11 +fi + +chown ${CONT_UID}.${CONT_GID} /ws + +echo "${CONT_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +DOCKER_SOCKET=/var/run/docker.sock +if [ -S ${DOCKER_SOCKET} ]; then + DOCKER_GID=$(stat -c '%g' ${DOCKER_SOCKET}) + if [ $(getent group ${DOCKER_GID}) ]; then + if [[ $ID == "ubuntu" ]] ; then + usermod -aG $(getent group ${DOCKER_GID} | cut -d: -f1) ${CONT_USER} || exit 12 + fi + if [[ $ID == "alpine" ]] ; then + addgroup ${CONT_USER} $(getent group ${DOCKER_GID} | cut -d: -f1) || exit 12 + fi + else + if [[ $ID == "ubuntu" ]] ; then + groupadd -for -g ${DOCKER_GID} docker_${CONT_USER} || exit 13 + usermod -aG docker_${CONT_USER} ${CONT_USER} || exit 14 + fi + if [[ $ID == "alpine" ]] ; then + addgroup -g ${DOCKER_GID} docker_${CONT_USER} || exit 13 + addgroup ${CONT_USER} docker_${CONT_USER} || exit 14 + fi + fi +fi + +export USER=${CONT_USER} +export HOME=/ws + +mkdir -p /ws/go +chown -R ${CONT_UID}.${CONT_GID} /ws/go +export GOPATH="/ws/go" + +sudo -E -s -u ${CONT_USER} env "PATH=$PATH" "$@" diff --git a/cmd/appmgr/api.go b/cmd/appmgr/api.go index 0d3fef9..2e7ddc7 100755 --- a/cmd/appmgr/api.go +++ b/cmd/appmgr/api.go @@ -21,6 +21,7 @@ package main import ( "encoding/json" + "errors" "github.com/gorilla/mux" "github.com/spf13/viper" "log" @@ -30,12 +31,6 @@ import ( // API functions func (m *XappManager) Initialize(h Helmer) { - /* - m.sd = SubscriptionDispatcher{} - m.sd.Initialize() - m.helm = h - m.helm.Initialize() - */ m.router = mux.NewRouter().StrictSlash(true) resources := []Resource{ @@ -53,6 +48,10 @@ func (m *XappManager) Initialize(h Helmer) { {"GET", "/ric/v1/subscriptions/{id}", m.getSubscription}, {"DELETE", "/ric/v1/subscriptions/{id}", m.deleteSubscription}, {"PUT", "/ric/v1/subscriptions/{id}", m.updateSubscription}, + + {"GET", "/ric/v1/config", m.getConfig}, + {"POST", "/ric/v1/config", m.createConfig}, + {"DELETE", "/ric/v1/config", m.deleteConfig}, } for _, resource := range resources { @@ -158,15 +157,15 @@ func (m *XappManager) deployXapp(w http.ResponseWriter, r *http.Request) { return } - var xapp Xapp - if err := json.NewDecoder(r.Body).Decode(&xapp); err != nil { + var cm ConfigMetadata + if err := json.NewDecoder(r.Body).Decode(&cm); err != nil { mdclog(MdclogErr, "Invalid xapp data in request body - url="+r.URL.RequestURI()) respondWithError(w, http.StatusMethodNotAllowed, "Invalid xapp data!") return } defer r.Body.Close() - xapp, err := m.helm.Install(xapp.Name) + xapp, err := m.helm.Install(cm) if err != nil { respondWithError(w, http.StatusInternalServerError, err.Error()) return @@ -262,6 +261,36 @@ func (m *XappManager) notifyClients() { m.sd.notifyClients(xapps, "updated") } +func (m *XappManager) getConfig(w http.ResponseWriter, r *http.Request) { + respondWithJSON(w, http.StatusOK, UploadConfig()) +} + +func (m *XappManager) createConfig(w http.ResponseWriter, r *http.Request) { + var c XAppConfig + if parseConfig(w, r, &c) != nil { + return + } + + if err := CreateConfigMap(c); err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + respondWithJSON(w, http.StatusCreated, nil) +} + +func (m *XappManager) deleteConfig(w http.ResponseWriter, r *http.Request) { + var c XAppConfig + if parseConfig(w, r, &c) != nil { + return + } + + if _, err := DeleteConfigMap(c); err != nil { + respondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + respondWithJSON(w, http.StatusNotFound, nil) +} + // Helper functions func respondWithError(w http.ResponseWriter, code int, message string) { respondWithJSON(w, code, map[string]string{"error": message}) @@ -284,3 +313,14 @@ func getResourceId(r *http.Request, w http.ResponseWriter, pattern string) (id s } return } + +func parseConfig(w http.ResponseWriter, r *http.Request, req *XAppConfig) error { + if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil { + mdclog(MdclogErr, "Invalid request payload - url="+r.URL.RequestURI()) + respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload") + return errors.New("Invalid payload") + } + defer r.Body.Close() + + return nil +} diff --git a/cmd/appmgr/api_test.go b/cmd/appmgr/api_test.go index 7b9691c..2648dee 100755 --- a/cmd/appmgr/api_test.go +++ b/cmd/appmgr/api_test.go @@ -56,7 +56,7 @@ func (h *MockedHelmer) List() (names []string, err error) { return names, helmError } -func (h *MockedHelmer) Install(name string) (Xapp, error) { +func (h *MockedHelmer) Install(m ConfigMetadata) (Xapp, error) { return xapp, helmError } diff --git a/cmd/appmgr/db.go b/cmd/appmgr/db.go index 6f772c0..723fa10 100755 --- a/cmd/appmgr/db.go +++ b/cmd/appmgr/db.go @@ -33,7 +33,7 @@ type DB struct { func (d *DB) Create() { ns := viper.GetString("db.sessionNamespace") - d.session = sdl.Create(ns) + d.session = sdl.NewSdlInstance(ns, sdl.NewDatabase()) // Test DB connection, and wait until ready! for { diff --git a/cmd/appmgr/desc.go b/cmd/appmgr/desc.go new file mode 100755 index 0000000..c3a27a3 --- /dev/null +++ b/cmd/appmgr/desc.go @@ -0,0 +1,267 @@ +/* +================================================================================== + Copyright (c) 2019 AT&T Intellectual Property. + Copyright (c) 2019 Nokia + + 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. +================================================================================== +*/ + +package main + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/spf13/viper" + "github.com/xeipuuv/gojsonschema" + "io/ioutil" + "log" + "os" + "path" + "regexp" + "strings" + "time" +) + +type ConfigMetadata struct { + Name string `json:"name"` + ConfigName string `json:"configName, omitempty"` + Namespace string `json:"namespace, omitempty"` +} + +type XAppConfig struct { + Metadata ConfigMetadata `json:"metadata"` + Descriptor interface{} `json:"descriptor, omitempty"` + Configuration interface{} `json:"config, omitempty"` +} + +type ConfigMap struct { + Kind string `json:"kind"` + ApiVersion string `json:"apiVersion"` + Data interface{} `json:"data"` + Metadata CMMetadata `json:"metadata"` +} + +type CMMetadata struct { + Name string `json:"name"` + Namespace string `json:"namespace"` +} + +func UploadConfig() (cfg []XAppConfig) { + for _, name := range GetNamesFromHelmRepo() { + if name == "appmgr" { + continue + } + + c := XAppConfig{ + Metadata: ConfigMetadata{Name: name, Namespace: "ricxapp", ConfigName: name + "-appconfig"}, + } + + err := ReadSchema(name, &c) + if err != nil { + continue + } + + err = ReadConfigMap(name, "ricxapp", &c.Configuration) + if err != nil { + log.Println("No active configMap found, using default!") + } + + cfg = append(cfg, c) + } + return +} + +func ReadSchema(name string, c *XAppConfig) (err error) { + if err = FetchChart(name); err != nil { + return + } + + tarDir := viper.GetString("xapp.tarDir") + err = ReadFile(path.Join(tarDir, name, viper.GetString("xapp.schema")), &c.Descriptor) + if err != nil { + return + } + + err = ReadFile(path.Join(tarDir, name, viper.GetString("xapp.config")), &c.Configuration) + if err != nil { + return + } + + if err = os.RemoveAll(path.Join(tarDir, name)); err != nil { + log.Println("RemoveAll failed", err) + } + + return +} + +func ReadConfigMap(name string, ns string, c *interface{}) (err error) { + args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s-appconfig", ns, name) + configMapJson, err := KubectlExec(args) + if err != nil { + return + } + + err = json.Unmarshal([]byte(configMapJson), &c) + if err != nil { + return + } + + return +} + +func ApplyConfigMap(r XAppConfig) (err error) { + cm := ConfigMap{ + Kind: "ConfigMap", + ApiVersion: "v1", + Metadata: CMMetadata{Name: r.Metadata.Name, Namespace: r.Metadata.Namespace}, + Data: r.Configuration, + } + + cmJson, err := json.Marshal(cm) + if err != nil { + log.Println("Config marshalling failed: ", err) + return + } + + cmFile := viper.GetString("xapp.tmpConfig") + err = ioutil.WriteFile(cmFile, cmJson, 0644) + if err != nil { + log.Println("WriteFile failed: ", err) + return + } + + cmd := " create configmap -n %s %s --from-file=%s -o json --dry-run | kubectl apply -f -" + args := fmt.Sprintf(cmd, r.Metadata.Namespace, r.Metadata.ConfigName, cmFile) + _, err = KubectlExec(args) + if err != nil { + return + } + log.Println("Configmap changes created!") + + return +} + +func CreateConfigMap(r XAppConfig) (err error) { + if err = Validate(r); err != nil { + return + } + return ApplyConfigMap(r) +} + +func DeleteConfigMap(r XAppConfig) (cm interface{}, err error) { + err = ReadConfigMap(r.Metadata.Name, r.Metadata.Namespace, &cm) + if err == nil { + args := fmt.Sprintf(" delete configmap --namespace=%s %s", r.Metadata.Namespace, r.Metadata.ConfigName) + _, err = KubectlExec(args) + } + return +} + +func PurgeConfigMap(m ConfigMetadata) (cm interface{}, err error) { + if m.ConfigName == "" { + m.ConfigName = m.Name + "-appconfig" + } + return DeleteConfigMap(XAppConfig{Metadata: m}) +} + +func RestoreConfigMap(m ConfigMetadata, cm interface{}) (err error) { + if m.ConfigName == "" { + m.ConfigName = m.Name + "-appconfig" + } + time.Sleep(time.Duration(10 * time.Second)) + + return ApplyConfigMap(XAppConfig{Metadata: m, Configuration: cm}) +} + +func GetNamesFromHelmRepo() (names []string) { + rname := viper.GetString("helm.repo-name") + + cmdArgs := strings.Join([]string{"search ", rname}, "") + out, err := HelmExec(cmdArgs) + if err != nil { + return + } + + re := regexp.MustCompile(rname + `/.*`) + result := re.FindAllStringSubmatch(string(out), -1) + if result != nil { + var tmp string + for _, v := range result { + fmt.Sscanf(v[0], "%s", &tmp) + names = append(names, strings.Split(tmp, "/")[1]) + } + } + return names +} + +func Validate(req XAppConfig) (err error) { + c := XAppConfig{} + err = ReadSchema(req.Metadata.Name, &c) + if err != nil { + log.Printf("No schema file found for '%s', aborting ...", req.Metadata.Name) + return err + } + + schemaLoader := gojsonschema.NewGoLoader(c.Descriptor) + documentLoader := gojsonschema.NewGoLoader(req.Configuration) + + log.Println("Starting validation ...") + result, err := gojsonschema.Validate(schemaLoader, documentLoader) + if err != nil { + log.Println("Validation failed: ", err) + return + } + + log.Println("validation done ...", err, result.Valid()) + if result.Valid() == false { + log.Println("The document is not valid, Errors: ", result.Errors()) + s := make([]string, 3) + for i, desc := range result.Errors() { + s = append(s, fmt.Sprintf(" (%d): %s.\n", i, desc.String())) + } + return errors.New(strings.Join(s, " ")) + } + return +} + +func ReadFile(name string, data interface{}) (err error) { + f, err := ioutil.ReadFile(name) + if err != nil { + log.Printf("Reading '%s' file failed: %v", name, err) + return + } + + err = json.Unmarshal(f, &data) + if err != nil { + log.Printf("Unmarshalling '%s' file failed: %v", name, err) + return + } + + return +} + +func FetchChart(name string) (err error) { + tarDir := viper.GetString("xapp.tarDir") + repo := viper.GetString("helm.repo-name") + fetchArgs := fmt.Sprintf("--untar --untardir %s %s/%s", tarDir, repo, name) + + _, err = HelmExec(strings.Join([]string{"fetch ", fetchArgs}, "")) + return +} + +func GetMessages(name string) (msgs MessageTypes, err error) { + log.Println("Fetching tx/rx messages for: ", name) + return +} diff --git a/cmd/appmgr/helm.go b/cmd/appmgr/helm.go index 453ff29..0f463d2 100755 --- a/cmd/appmgr/helm.go +++ b/cmd/appmgr/helm.go @@ -24,12 +24,10 @@ import ( "errors" "fmt" "github.com/spf13/viper" - "gopkg.in/yaml.v2" "io/ioutil" "log" "os" "os/exec" - "path" "regexp" "strconv" "strings" @@ -39,27 +37,19 @@ import ( var execCommand = exec.Command func Exec(args string) (out []byte, err error) { - cmd := execCommand("/bin/sh", "-c", strings.Join([]string{"helm", args}, " ")) - - if !strings.HasSuffix(os.Args[0], ".test") { - out, err = cmd.CombinedOutput() - if err != nil { - mdclog(MdclogErr, formatLog("Command failed", args, err.Error())) - } - return out, err - } + cmd := execCommand("/bin/sh", "-c", args) var stdout bytes.Buffer var stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr - log.Printf("Running command: %v", cmd) - for i := 0; i < 3; i++ { + log.Println("Running command: ", cmd) + for i := 0; i < viper.GetInt("helm.retry"); i++ { err = cmd.Run() if err != nil { mdclog(MdclogErr, formatLog("Command failed, retrying", args, err.Error()+stderr.String())) - time.Sleep(time.Duration(5) * time.Second) + time.Sleep(time.Duration(2) * time.Second) continue } break @@ -73,6 +63,14 @@ func Exec(args string) (out []byte, err error) { return stdout.Bytes(), errors.New(stderr.String()) } +func HelmExec(args string) (out []byte, err error) { + return Exec(strings.Join([]string{"helm", args}, " ")) +} + +func KubectlExec(args string) (out []byte, err error) { + return Exec(strings.Join([]string{"kubectl", args}, " ")) +} + func (h *Helm) Initialize() { if h.initDone == true { return @@ -95,11 +93,12 @@ func (h *Helm) Initialize() { mdclog(MdclogErr, formatLog("Helm repo addition failed, retyring ...", "", "")) time.Sleep(time.Duration(10) * time.Second) } + h.initDone = true } func (h *Helm) Run(args string) (out []byte, err error) { - return Exec(args) + return HelmExec(args) } // API functions @@ -110,7 +109,7 @@ func (h *Helm) Init() (out []byte, err error) { return out, err } - return Exec(strings.Join([]string{"init -c"}, "")) + return HelmExec(strings.Join([]string{"init -c"}, "")) } func (h *Helm) AddRepo() (out []byte, err error) { @@ -138,24 +137,29 @@ func (h *Helm) AddRepo() (out []byte, err error) { // Get helm repo address from values.yaml repo := viper.GetString("helm.repo") - return Exec(strings.Join([]string{"repo add ", rname, " ", repo, username, pwd}, "")) + return HelmExec(strings.Join([]string{"repo add ", rname, " ", repo, username, pwd}, "")) } -func (h *Helm) Install(name string) (xapp Xapp, err error) { +func (h *Helm) Install(m ConfigMetadata) (xapp Xapp, err error) { out, err := h.Run(strings.Join([]string{"repo update "}, "")) if err != nil { return } - rname := viper.GetString("helm.repo-name") + m.Namespace = getNamespace(m.Namespace) + cm, cmErr := PurgeConfigMap(m) - ns := getNamespaceArgs() - out, err = h.Run(strings.Join([]string{"install ", rname, "/", name, " --name ", name, ns}, "")) + ns := " --namespace=" + m.Namespace + rname := viper.GetString("helm.repo-name") + out, err = h.Run(strings.Join([]string{"install ", rname, "/", m.Name, " --name ", m.Name, ns}, "")) if err != nil { return } - return h.ParseStatus(name, string(out)) + if cmErr == nil { + cmErr = RestoreConfigMap(m, cm) + } + return h.ParseStatus(m.Name, string(out)) } func (h *Helm) Status(name string) (xapp Xapp, err error) { @@ -181,8 +185,8 @@ func (h *Helm) StatusAll() (xapps []Xapp, err error) { func (h *Helm) List() (names []string, err error) { - ns := getNamespaceArgs() - out, err := h.Run(strings.Join([]string{"list --all --output yaml ", ns}, "")) + ns := getNamespace("") + out, err := h.Run(strings.Join([]string{"list --all --output yaml --namespace=", ns}, "")) if err != nil { mdclog(MdclogErr, formatLog("Listing deployed xapps failed", "", err.Error())) return @@ -214,45 +218,10 @@ func (h *Helm) Fetch(name, tarDir string) error { } // Helper functions -func (h *Helm) GetMessages(name string) (msgs MessageTypes, err error) { - tarDir := viper.GetString("xapp.tarDir") - if tarDir == "" { - tarDir = "/tmp" - } - - if h.Fetch(name, tarDir); err != nil { - mdclog(MdclogWarn, formatLog("Fetch chart failed", "", err.Error())) - return - } - - return h.ParseMessages(name, tarDir, viper.GetString("xapp.msg_type_file")) - -} - -func (h *Helm) ParseMessages(name string, chartDir, msgFile string) (msgs MessageTypes, err error) { - yamlFile, err := ioutil.ReadFile(path.Join(chartDir, name, msgFile)) - if err != nil { - mdclog(MdclogWarn, formatLog("ReadFile failed", "", err.Error())) - return - } - - err = yaml.Unmarshal(yamlFile, &msgs) - if err != nil { - mdclog(MdclogWarn, formatLog("Unmarshal failed", "", err.Error())) - return - } - - if err = os.RemoveAll(path.Join(chartDir, name)); err != nil { - mdclog(MdclogWarn, formatLog("RemoveAll failed", "", err.Error())) - } - - return -} - func (h *Helm) GetVersion(name string) (version string) { - ns := getNamespaceArgs() - out, err := h.Run(strings.Join([]string{"list --output yaml ", name, ns}, "")) + ns := getNamespace("") + out, err := h.Run(strings.Join([]string{"list --output yaml --namespace=", ns, " ", name}, "")) if err != nil { return } @@ -336,10 +305,10 @@ func (h *Helm) ParseStatus(name string, out string) (xapp Xapp, err error) { xapp.Version = h.GetVersion(name) xapp.Status = h.GetState(out) - types, err := h.GetMessages(name) + types, err := GetMessages(name) if err != nil { // xAPP can still be deployed if the msg_type file is missing. - mdclog(MdclogWarn, formatLog("method GetMessages Failed....", "", err.Error())) + mdclog(MdclogWarn, formatLog("GetMessages Failed....", "", err.Error())) //Set err back to nil, so it does not cause issues in called functions. err = nil @@ -376,12 +345,16 @@ func addTillerEnv() (err error) { return err } -func getNamespaceArgs() string { +func getNamespace(namespace string) string { + if namespace != "" { + return namespace + } + ns := viper.GetString("xapp.namespace") if ns == "" { ns = "ricxapp" } - return " --namespace=" + ns + return ns } func formatLog(text string, args string, err string) string { diff --git a/cmd/appmgr/logger.go b/cmd/appmgr/logger.go index 591fa47..877bcbe 100755 --- a/cmd/appmgr/logger.go +++ b/cmd/appmgr/logger.go @@ -39,7 +39,7 @@ import ( func mdclog(severity C.mdclog_severity_t, msg string) { msg = fmt.Sprintf("%s:: %s ", time.Now().Format("2019-01-02 15:04:05"), msg) - C.mdclog_mdc_add(C.CString("XM"), C.CString("1.0.0")) + C.mdclog_mdc_add(C.CString("XM"), C.CString("1.0.1")) C.xAppMgr_mdclog_write(severity, C.CString(msg)) } diff --git a/cmd/appmgr/subscriptions_test.go b/cmd/appmgr/subscriptions_test.go index 8861af0..5d79d00 100755 --- a/cmd/appmgr/subscriptions_test.go +++ b/cmd/appmgr/subscriptions_test.go @@ -175,7 +175,7 @@ func TestPublishXappAction(t *testing.T) { } func TestTeardown(t *testing.T) { - db := sdl.Create(viper.GetString("db.sessionNamespace")) + db := sdl.NewSdlInstance(viper.GetString("db.sessionNamespace"), sdl.NewDatabase()) db.RemoveAll() } diff --git a/cmd/appmgr/types.go b/cmd/appmgr/types.go index a3599fe..243179d 100755 --- a/cmd/appmgr/types.go +++ b/cmd/appmgr/types.go @@ -38,10 +38,12 @@ type Resource struct { } type Xapp struct { - Name string `json:"name"` - Status string `json:"status"` - Version string `json:"version"` - Instances []XappInstance `json:"instances"` + Name string `json:"name"` + ConfigName string `json:"configName, omitempty"` + Namespace string `json:"namespace, omitempty"` + Status string `json:"status"` + Version string `json:"version"` + Instances []XappInstance `json:"instances"` } type XappInstance struct { @@ -63,7 +65,7 @@ type XappManager struct { type Helmer interface { Initialize() - Install(name string) (xapp Xapp, err error) + Install(m ConfigMetadata) (xapp Xapp, err error) Status(name string) (xapp Xapp, err error) StatusAll() (xapps []Xapp, err error) List() (xapps []string, err error) diff --git a/config/appmgr.yaml b/config/appmgr.yaml index 6be3be7..34db29e 100755 --- a/config/appmgr.yaml +++ b/config/appmgr.yaml @@ -17,17 +17,20 @@ "host": ":8080" "helm": "host": "192.168.0.12:31807" - "repo": "/opt/ric/dummy-xapp-chart" - "repo-name": "dummy" + "repo": "http://192.168.0.6/charts" + "repo-name": "helm-repo" "secrets": "username": "admin" "password": "ric" "helm-username-file": "./helm_repo_username" "helm-password-file": "./helm_repo_password" + "retry": 1 "xapp": "namespace": "ricxapp" "tarDir": "/tmp" - "msg_type_file": "msg_type.yaml" + "schema": "descriptors/schema.json" + "config": "config/config-file.json" + "tmpConfig": "/tmp/config-file.json" "db": "sessionNamespace": "XMSession" "host": ":6379" diff --git a/go.mod b/go.mod index 43c6a6f..8c5298a 100755 --- a/go.mod +++ b/go.mod @@ -4,13 +4,15 @@ go 1.12 require ( gerrit.oran-osc.org/r/ric-plt/sdlgo v0.0.0 + github.com/BurntSushi/toml v0.3.1 // indirect github.com/fsnotify/fsnotify v1.4.7 github.com/gorilla/mux v1.7.1 - github.com/mitchellh/mapstructure v1.1.2 github.com/orcaman/concurrent-map v0.0.0-20190314100340-2693aad1ed75 github.com/segmentio/ksuid v1.0.2 github.com/spf13/viper v1.3.2 - gopkg.in/yaml.v2 v2.2.2 + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.1.0 ) -replace gerrit.oran-osc.org/r/ric-plt/sdlgo => ./internal/sdlgo +replace gerrit.oran-osc.org/r/ric-plt/sdlgo => gerrit.oran-osc.org/r/ric-plt/sdlgo.git v0.0.1 diff --git a/go.sum b/go.sum index 1661c23..e1a2985 100755 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +gerrit.oran-osc.org/r/ric-plt/sdlgo.git v0.0.1 h1:l2dl31r++3xhgCumTzwvuo0/F415eqU4aFk/uDQ4WnM= +gerrit.oran-osc.org/r/ric-plt/sdlgo.git v0.0.1/go.mod h1:LVhkNS82IofJTBK/VYPKiYed9MG/3OFwvWC6MGSDw1w= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -5,23 +7,28 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4= github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU= github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/orcaman/concurrent-map v0.0.0-20190314100340-2693aad1ed75 h1:IV56VwUb9Ludyr7s53CMuEh4DdTnnQtEPLEgLyJ0kHI= github.com/orcaman/concurrent-map v0.0.0-20190314100340-2693aad1ed75/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= @@ -41,13 +48,23 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg= +github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1YthsFqr/5mxHduZW2A= @@ -56,7 +73,9 @@ golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= diff --git a/helm_chart/appmgr/values.yaml b/helm_chart/appmgr/values.yaml index d27d020..8b2fc04 100755 --- a/helm_chart/appmgr/values.yaml +++ b/helm_chart/appmgr/values.yaml @@ -19,7 +19,7 @@ # Modify this section to point to Docker image repository image: #repository: "snapshot.docker.ranco-dev-tools.eastus.cloudapp.azure.com:10001" - repository: "k8s-cluster-docker-helm-repo:5000" + repository: "192.168.0.6:5000" #repositoryCred: # user: docker @@ -31,7 +31,7 @@ image: # xAppmanager Docker image name and tag name: appmgr - tag: 1.0.0 + tag: 1.0.1 #nameOverride: "" #fullnameOverride: "" @@ -58,7 +58,7 @@ appconfig: "host": ":8080" "helm": # Remote helm repo URL. UPDATE this as required. - "repo": "https://k8s-cluster-docker-helm-repo/helm_charts" + "repo": "http://192.168.0.6/charts" # Repo name referred within the appmgr "repo-name": "helm-repo" @@ -71,12 +71,14 @@ appconfig: # helm username and password files "helm-username-file": "/opt/ric/secret/helm_repo_username" "helm-password-file": "/opt/ric/secret/helm_repo_password" + "retry": 1 "xapp": #Namespace to install xAPPs "namespace": "ricxapp" - - #File containing xAPP message types - "msg_type_file": "msg_type.yaml" + "tarDir": "/tmp" + "schema": "descriptors/schema.json" + "config": "config/config-file.json" + "tmpConfig": "/tmp/config-file.json" # To be provided as env variables appenv: -- 2.16.6