# See the License for the specific language governing permissions and
# limitations under the License.
-BUILD_DIR=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
-ROOT_DIR:=$(abspath $(BUILD_DIR)/..)
-
-BUILD_PREFIX?="${USER}-"
+#------------------------------------------------------------------------------
+#
+#-------------------------------------------------------------------- ----------
+ROOT_DIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
+BUILD_DIR:=$(abspath $(ROOT_DIR)/build)
-XAPP_MGR:=appmgr
-XAPP_MGR_DOCKER:=${BUILD_PREFIX}appmgr
+PACKAGEURL:="gerrit.oran-osc.org/r/ric-plt/appmgr"
+HELMVERSION:=v2.13.0-rc.1
-GOSRC := $(abspath $(BUILD_DIR)/../src)
-GOFILES := $(GOSRC)/*.go
+#------------------------------------------------------------------------------
+#
+#-------------------------------------------------------------------- ----------
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
GOTEST=$(GOCMD) test -v -coverprofile $(COVEROUT)
GOGET=$(GOCMD) get
-HELMVERSION:=v2.13.0-rc.1
+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)
+
+CMDS:=$(BUILD_DIR)/appmgr
#------------------------------------------------------------------------------
#
#-------------------------------------------------------------------- ----------
-.PHONY: FORCE build deps run unit-test test-pkg test clean docker-base-build docker-base-clean docker-build docker-run docker-clean docker-test-build docker-test-run-unittest docker-test-run-sanity docker-test-run docker-test-clean
-
-.DEFAULT: build
+ .DEFAULT: build
default: build
+.PHONY: FORCE
+
FORCE:
#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
-XAPP_MGR_DOCKER:=$(shell echo $(XAPP_MGR_DOCKER) | tr '[:upper:]' '[:lower:]')
+$(CMDS): $(GOFILES)
+ GO111MODULE=on GO_ENABLED=0 GOOS=linux $(GOBUILD) -o $@ ./cmd/$(shell basename "$@")
-#XAPP_MGR_DOCKER:=$(subst /,_,${XAPP_MGR_DOCKER})
-#------------------------------------------------------------------------------
-#
-#------------------------------------------------------------------------------
+$(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_DIR)$(XAPP_MGR): deps ${wildcard $(GOFILES)}
- GO_ENABLED=0 GOOS=linux $(GOBUILD) -o $(BUILD_DIR)$(XAPP_MGR) $(GOFILES)
-build: $(BUILD_DIR)$(XAPP_MGR)
+build: $(CMDS)
-deps: ${wildcard $(GOFILES)}
- cd $(GOSRC) && $(GOGET)
-run: $(BUILD_DIR)$(XAPP_MGR)
- $(BUILD_DIR)$(XAPP_MGR) -host-addr="localhost:8080" -helm-host="localhost:31807" -helm-chart="./"
+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 $^
-unit-test:
- cd $(GOSRC) && $(GOTEST)
- go tool cover -html=$(COVEROUT) -o $(COVERHTML)
clean:
@echo " > Cleaning build cache"
- @-rm -rf $(XAPP_MGR) 2> /dev/null
+ @-rm -rf $(CMDS)* 2> /dev/null
go clean 2> /dev/null
#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
-DCKR_BUILD_OPTS:=${DCKR_BUILD_OPTS} --network=host --build-arg HELMVERSION=${HELMVERSION}
+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')
#
#------------------------------------------------------------------------------
docker-name:
- @echo $(XAPP_MGR_DOCKER)
+ @echo $(DCKR_NAME)
docker-build:
- docker build --target release ${DCKR_BUILD_OPTS} -t $(XAPP_MGR_DOCKER) -f Dockerfile ../.
+ docker build --target release ${DCKR_BUILD_OPTS} -t $(DCKR_NAME) -f $(DCKR_FILE) .
docker-run:
- docker run ${DCKR_RUN_OPTS} -v /opt/ric:/opt/ric -p 8080:8080 $(XAPP_MGR_DOCKER)
+ docker run ${DCKR_RUN_OPTS} -v /opt/ric:/opt/ric -p 8080:8080 $(DCKR_NAME)
docker-clean:
- docker rmi $(XAPP_MGR_DOCKER)
+ docker rmi $(DCKR_NAME)
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
docker-test-build:
- docker build --target test_unit ${DCKR_BUILD_OPTS} -t ${XAPP_MGR_DOCKER}-test_unit -f Dockerfile ../.
- docker build --target test_sanity ${DCKR_BUILD_OPTS} -t ${XAPP_MGR_DOCKER}-test_sanity -f Dockerfile ../.
+ 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:
- docker run ${DCKR_RUN_OPTS} ${XAPP_MGR_DOCKER}-test_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-sanity:
- docker run ${DCKR_RUN_OPTS} ${XAPP_MGR_DOCKER}-test_sanity
-docker-test-run: docker-test-run-sanity docker-test-run-unit
+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 ${XAPP_MGR_DOCKER}-test_unit
- docker rmi -f ${XAPP_MGR_DOCKER}-test_sanity
+ docker rmi -f ${DCKR_NAME}-test_unit
+ docker rmi -f ${DCKR_NAME}-test_sanity
+ docker rmi -f ${DCKR_NAME}-test_fmt
## Prerequisites
Make sure that following tools are properly installed and configured
* GO (golang) development and runtime tools
+* mdclog (com/log)
* Docker
* Kubernates and related tools (kubectl and helm)
* Xapp Docker repo (either local or remote)
* Xapp Helm charts
-* com/log
+* ...
## Building go binary and docker container for xApp Manager
```sh
-# Change to build-folder and run following command
+# Run following command. Make sure that mdclog is installed and found in the standard library path
make docker-build
```
## Running xApp Manager unit tests
```sh
-# Change to build-folder and run following command
+# Run following command
make test
```
## Running xApp Manager locally
```sh
# Now run the xApp manager
-./xapp_mgr -f ../config/appmgr.yaml
+build/appmgr -f config/appmgr.yaml
```
# Running Docker container of xApp manager
# Additional info
```sh
Todo
-```
+```
\ No newline at end of file
}
},
"host": "hostname",
- "basePath": "/ric/v1/xapps",
+ "basePath": "/ric/v1",
"schemes": [
"https",
"http"
],
"paths": {
- "/ric/v1/health": {
+ "/health": {
"get": {
"summary": "Health check of xApp Manager",
"operationId": "getHealth",
}
}
},
- "/ric/v1/xapps": {
+ "/xapps": {
"post": {
"summary": "Deploy a xapp",
"operationId": "deployXapp",
}
}
},
- "/ric/v1/xapps/{xAppName}": {
+ "/xapps/{xAppName}": {
"get": {
"summary": "Returns the status of a given xapp",
"operationId": "getXappByName",
}
}
},
- "/ric/v1/xapps/{xAppName}/instances/{xAppInstanceName}": {
+ "/xapps/{xAppName}/instances/{xAppInstanceName}": {
"get": {
"summary": "Returns the status of a given xapp",
"operationId": "getXappInstanceByName",
}
}
},
- "/ric/v1/subscriptions": {
+ "/subscriptions": {
"post": {
"summary": "Subscribe event",
"operationId": "addSubscription",
}
}
},
- "/ric/v1/subscriptions/{subscriptionId}": {
+ "/subscriptions/{subscriptionId}": {
"get": {
"summary": "Returns the information of subscription",
"operationId": "getSubscriptionById",
--- /dev/null
+/*
+==================================================================================
+ 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"
+ "github.com/gorilla/mux"
+ "github.com/spf13/viper"
+ "log"
+ "net/http"
+)
+
+// 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{
+ {"GET", "/ric/v1/health/alive", m.getHealthStatus},
+ {"GET", "/ric/v1/health/ready", m.getHealthStatus},
+
+ {"GET", "/ric/v1/xapps", m.getAllXapps},
+ {"GET", "/ric/v1/xapps/{name}", m.getXappByName},
+ {"GET", "/ric/v1/xapps/{name}/instances/{id}", m.getXappInstanceByName},
+ {"POST", "/ric/v1/xapps", m.deployXapp},
+ {"DELETE", "/ric/v1/xapps/{name}", m.undeployXapp},
+
+ {"GET", "/ric/v1/subscriptions", m.getSubscriptions},
+ {"POST", "/ric/v1/subscriptions", m.addSubscription},
+ {"GET", "/ric/v1/subscriptions/{id}", m.getSubscription},
+ {"DELETE", "/ric/v1/subscriptions/{id}", m.deleteSubscription},
+ {"PUT", "/ric/v1/subscriptions/{id}", m.updateSubscription},
+ }
+
+ for _, resource := range resources {
+ handler := Logger(resource.HandlerFunc)
+ //handler = m.serviceChecker(handler)
+ m.router.Methods(resource.Method).Path(resource.Url).Handler(handler)
+ }
+
+ go m.finalize(h)
+}
+
+func (m *XappManager) finalize(h Helmer) {
+ m.sd = SubscriptionDispatcher{}
+ m.sd.Initialize()
+
+ m.helm = h
+ m.helm.Initialize()
+
+ m.notifyClients()
+ m.ready = true
+}
+
+func (m *XappManager) serviceChecker(inner http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.RequestURI() == "/ric/v1/health/alive" || m.ready == true {
+ inner.ServeHTTP(w, r)
+ } else {
+ respondWithJSON(w, http.StatusServiceUnavailable, nil)
+ }
+ })
+}
+
+// API: XAPP handlers
+func (m *XappManager) getHealthStatus(w http.ResponseWriter, r *http.Request) {
+ respondWithJSON(w, http.StatusOK, nil)
+}
+
+func (m *XappManager) Run() {
+ host := viper.GetString("local.host")
+ if host == "" {
+ host = ":8080"
+ }
+ log.Printf("Xapp manager started ... serving on %s\n", host)
+
+ log.Fatal(http.ListenAndServe(host, m.router))
+}
+
+func (m *XappManager) getXappByName(w http.ResponseWriter, r *http.Request) {
+ xappName, ok := getResourceId(r, w, "name")
+ if ok != true {
+ return
+ }
+
+ if xapp, err := m.helm.Status(xappName); err == nil {
+ respondWithJSON(w, http.StatusOK, xapp)
+ } else {
+ respondWithError(w, http.StatusNotFound, err.Error())
+ }
+}
+
+func (m *XappManager) getXappInstanceByName(w http.ResponseWriter, r *http.Request) {
+ xappName, ok := getResourceId(r, w, "name")
+ if ok != true {
+ return
+ }
+
+ xapp, err := m.helm.Status(xappName)
+ if err != nil {
+ respondWithError(w, http.StatusNotFound, err.Error())
+ return
+ }
+
+ xappInstanceName, ok := getResourceId(r, w, "id")
+ if ok != true {
+ return
+ }
+
+ for _, v := range xapp.Instances {
+ if v.Name == xappInstanceName {
+ respondWithJSON(w, http.StatusOK, v)
+ return
+ }
+ }
+ mdclog(MdclogErr, "Xapp instance not found - url="+r.URL.RequestURI())
+
+ respondWithError(w, http.StatusNotFound, "Xapp instance not found")
+}
+
+func (m *XappManager) getAllXapps(w http.ResponseWriter, r *http.Request) {
+ xapps, err := m.helm.StatusAll()
+ if err != nil {
+ respondWithError(w, http.StatusInternalServerError, err.Error())
+ return
+ }
+
+ respondWithJSON(w, http.StatusOK, xapps)
+}
+
+func (m *XappManager) deployXapp(w http.ResponseWriter, r *http.Request) {
+ if r.Body == nil {
+ mdclog(MdclogErr, "No xapp data found in request body - url="+r.URL.RequestURI())
+ respondWithError(w, http.StatusMethodNotAllowed, "No xapp data!")
+ return
+ }
+
+ var xapp Xapp
+ if err := json.NewDecoder(r.Body).Decode(&xapp); 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)
+ if err != nil {
+ respondWithError(w, http.StatusInternalServerError, err.Error())
+ return
+ }
+
+ respondWithJSON(w, http.StatusCreated, xapp)
+
+ m.sd.Publish(xapp, EventType("created"))
+}
+
+func (m *XappManager) undeployXapp(w http.ResponseWriter, r *http.Request) {
+ xappName, ok := getResourceId(r, w, "name")
+ if ok != true {
+ return
+ }
+
+ xapp, err := m.helm.Delete(xappName)
+ if err != nil {
+ respondWithError(w, http.StatusInternalServerError, err.Error())
+ return
+ }
+
+ respondWithJSON(w, http.StatusNoContent, nil)
+
+ m.sd.Publish(xapp, EventType("deleted"))
+}
+
+// API: resthook handlers
+func (m *XappManager) getSubscriptions(w http.ResponseWriter, r *http.Request) {
+ respondWithJSON(w, http.StatusOK, m.sd.GetAll())
+}
+
+func (m *XappManager) getSubscription(w http.ResponseWriter, r *http.Request) {
+ if id, ok := getResourceId(r, w, "id"); ok == true {
+ if s, ok := m.sd.Get(id); ok {
+ respondWithJSON(w, http.StatusOK, s)
+ } else {
+ mdclog(MdclogErr, "Subscription not found - url="+r.URL.RequestURI())
+ respondWithError(w, http.StatusNotFound, "Subscription not found")
+ }
+ }
+}
+
+func (m *XappManager) deleteSubscription(w http.ResponseWriter, r *http.Request) {
+ if id, ok := getResourceId(r, w, "id"); ok == true {
+ if _, ok := m.sd.Delete(id); ok {
+ respondWithJSON(w, http.StatusNoContent, nil)
+ } else {
+ mdclog(MdclogErr, "Subscription not found - url="+r.URL.RequestURI())
+ respondWithError(w, http.StatusNotFound, "Subscription not found")
+ }
+ }
+}
+
+func (m *XappManager) addSubscription(w http.ResponseWriter, r *http.Request) {
+ var req SubscriptionReq
+ 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
+ }
+ defer r.Body.Close()
+
+ respondWithJSON(w, http.StatusCreated, m.sd.Add(req))
+}
+
+func (m *XappManager) updateSubscription(w http.ResponseWriter, r *http.Request) {
+ if id, ok := getResourceId(r, w, "id"); ok == true {
+ var req SubscriptionReq
+ 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
+ }
+ defer r.Body.Close()
+
+ if s, ok := m.sd.Update(id, req); ok {
+ respondWithJSON(w, http.StatusOK, s)
+ } else {
+ mdclog(MdclogErr, "Subscription not found - url="+r.URL.RequestURI())
+ respondWithError(w, http.StatusNotFound, "Subscription not found")
+ }
+ }
+}
+
+func (m *XappManager) notifyClients() {
+ xapps, err := m.helm.StatusAll()
+ if err != nil {
+ mdclog(MdclogInfo, "Couldn't fetch xapps status information"+err.Error())
+ return
+ }
+
+ m.sd.notifyClients(xapps, "updated")
+}
+
+// Helper functions
+func respondWithError(w http.ResponseWriter, code int, message string) {
+ respondWithJSON(w, code, map[string]string{"error": message})
+}
+
+func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(code)
+ if payload != nil {
+ response, _ := json.Marshal(payload)
+ w.Write(response)
+ }
+}
+
+func getResourceId(r *http.Request, w http.ResponseWriter, pattern string) (id string, ok bool) {
+ if id, ok = mux.Vars(r)[pattern]; ok != true {
+ mdclog(MdclogErr, "Couldn't resolve name/id from the request URL")
+ respondWithError(w, http.StatusMethodNotAllowed, "Couldn't resolve name/id from the request URL")
+ return
+ }
+ return
+}
--- /dev/null
+/*
+==================================================================================
+ 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 (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "github.com/gorilla/mux"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "reflect"
+ "strconv"
+ "testing"
+ "time"
+)
+
+var x XappManager
+var xapp Xapp
+var xapps []Xapp
+var helmError error
+
+type MockedHelmer struct {
+}
+
+func (sd *MockedHelmer) Initialize() {
+}
+
+func (h *MockedHelmer) Status(name string) (Xapp, error) {
+ return xapp, helmError
+}
+
+func (h *MockedHelmer) StatusAll() ([]Xapp, error) {
+ return xapps, helmError
+}
+
+func (h *MockedHelmer) List() (names []string, err error) {
+ return names, helmError
+}
+
+func (h *MockedHelmer) Install(name string) (Xapp, error) {
+ return xapp, helmError
+}
+
+func (h *MockedHelmer) Delete(name string) (Xapp, error) {
+ return xapp, helmError
+}
+
+// Test cases
+func TestMain(m *testing.M) {
+ loadConfig()
+
+ xapp = Xapp{}
+ xapps = []Xapp{}
+
+ h := MockedHelmer{}
+ x = XappManager{}
+ x.Initialize(&h)
+
+ // Just run on the background (for coverage)
+ go x.Run()
+ x.ready = true
+
+ time.Sleep(time.Duration(2 * time.Second))
+
+ code := m.Run()
+ os.Exit(code)
+}
+
+func TestGetHealthCheck(t *testing.T) {
+ req, _ := http.NewRequest("GET", "/ric/v1/health/ready", nil)
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusOK, response.Code)
+}
+
+func TestGetAppsReturnsEmpty(t *testing.T) {
+ req, _ := http.NewRequest("GET", "/ric/v1/xapps", nil)
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusOK, response.Code)
+ if body := response.Body.String(); body != "[]" {
+ t.Errorf("handler returned unexpected body: got %v want []", body)
+ }
+}
+
+func TestCreateXApp(t *testing.T) {
+ xapp = generateXapp("dummy-xapp", "started", "1.0", "dummy-xapp-1234-5678", "running", "127.0.0.1", "9999")
+
+ payload := []byte(`{"name":"dummy-xapp"}`)
+ req, _ := http.NewRequest("POST", "/ric/v1/xapps", bytes.NewBuffer(payload))
+ response := executeRequest(req)
+
+ checkResponseData(t, response, http.StatusCreated, false)
+}
+
+func TestGetAppsReturnsListOfXapps(t *testing.T) {
+ xapps = append(xapps, xapp)
+ req, _ := http.NewRequest("GET", "/ric/v1/xapps", nil)
+ response := executeRequest(req)
+
+ checkResponseData(t, response, http.StatusOK, true)
+}
+
+func TestGetAppByIdReturnsGivenXapp(t *testing.T) {
+ req, _ := http.NewRequest("GET", "/ric/v1/xapps/"+xapp.Name, nil)
+ response := executeRequest(req)
+
+ checkResponseData(t, response, http.StatusOK, false)
+}
+
+func TestGetAppInstanceByIdReturnsGivenXapp(t *testing.T) {
+ req, _ := http.NewRequest("GET", "/ric/v1/xapps/"+xapp.Name+"/instances/dummy-xapp-1234-5678", nil)
+ response := executeRequest(req)
+
+ var ins XappInstance
+ checkResponseCode(t, http.StatusOK, response.Code)
+ json.NewDecoder(response.Body).Decode(&ins)
+
+ if !reflect.DeepEqual(ins, xapp.Instances[0]) {
+ t.Errorf("handler returned unexpected body: got: %v, expected: %v", ins, xapp.Instances[0])
+ }
+}
+
+func TestDeleteAppRemovesGivenXapp(t *testing.T) {
+ req, _ := http.NewRequest("DELETE", "/ric/v1/xapps/"+xapp.Name, nil)
+ response := executeRequest(req)
+
+ checkResponseData(t, response, http.StatusNoContent, false)
+
+ // Xapp not found from the Redis DB
+ helmError = errors.New("Not found")
+
+ req, _ = http.NewRequest("GET", "/ric/v1/xapps/"+xapp.Name, nil)
+ response = executeRequest(req)
+ checkResponseCode(t, http.StatusNotFound, response.Code)
+}
+
+// Error handling
+func TestGetXappReturnsError(t *testing.T) {
+ helmError = errors.New("Not found")
+
+ req, _ := http.NewRequest("GET", "/ric/v1/xapps/invalidXappName", nil)
+ response := executeRequest(req)
+ checkResponseCode(t, http.StatusNotFound, response.Code)
+}
+
+func TestGetXappInstanceReturnsError(t *testing.T) {
+ helmError = errors.New("Some error")
+
+ req, _ := http.NewRequest("GET", "/ric/v1/xapps/"+xapp.Name+"/instances/invalidXappName", nil)
+ response := executeRequest(req)
+ checkResponseCode(t, http.StatusNotFound, response.Code)
+}
+
+func TestGetXappListReturnsError(t *testing.T) {
+ helmError = errors.New("Internal error")
+
+ req, _ := http.NewRequest("GET", "/ric/v1/xapps", nil)
+ response := executeRequest(req)
+ checkResponseCode(t, http.StatusInternalServerError, response.Code)
+}
+
+func TestCreateXAppWithoutXappData(t *testing.T) {
+ req, _ := http.NewRequest("POST", "/ric/v1/xapps", nil)
+ response := executeRequest(req)
+ checkResponseData(t, response, http.StatusMethodNotAllowed, false)
+}
+
+func TestCreateXAppWithInvalidXappData(t *testing.T) {
+ body := []byte("Invalid JSON data ...")
+
+ req, _ := http.NewRequest("POST", "/ric/v1/xapps", bytes.NewBuffer(body))
+ response := executeRequest(req)
+ checkResponseData(t, response, http.StatusMethodNotAllowed, false)
+}
+
+func TestCreateXAppReturnsError(t *testing.T) {
+ helmError = errors.New("Not found")
+
+ payload := []byte(`{"name":"dummy-xapp"}`)
+ req, _ := http.NewRequest("POST", "/ric/v1/xapps", bytes.NewBuffer(payload))
+ response := executeRequest(req)
+
+ checkResponseData(t, response, http.StatusInternalServerError, false)
+}
+
+func TestDeleteXappListReturnsError(t *testing.T) {
+ helmError = errors.New("Internal error")
+
+ req, _ := http.NewRequest("DELETE", "/ric/v1/xapps/invalidXappName", nil)
+ response := executeRequest(req)
+ checkResponseCode(t, http.StatusInternalServerError, response.Code)
+}
+
+// Helper functions
+type fn func(w http.ResponseWriter, r *http.Request)
+
+func executeRequest(req *http.Request) *httptest.ResponseRecorder {
+ rr := httptest.NewRecorder()
+
+ vars := map[string]string{
+ "id": "1",
+ }
+ req = mux.SetURLVars(req, vars)
+
+ x.router.ServeHTTP(rr, req)
+
+ return rr
+}
+
+func checkResponseCode(t *testing.T, expected, actual int) {
+ if expected != actual {
+ t.Errorf("Expected response code %d. Got %d\n", expected, actual)
+ }
+}
+
+func checkResponseData(t *testing.T, response *httptest.ResponseRecorder, expectedHttpStatus int, isList bool) {
+ expectedData := xapp
+
+ checkResponseCode(t, expectedHttpStatus, response.Code)
+ if isList == true {
+ jsonResp := []Xapp{}
+ json.NewDecoder(response.Body).Decode(&jsonResp)
+
+ if !reflect.DeepEqual(jsonResp[0], expectedData) {
+ t.Errorf("handler returned unexpected body: %v", jsonResp)
+ }
+ } else {
+ json.NewDecoder(response.Body).Decode(&xapp)
+
+ if !reflect.DeepEqual(xapp, expectedData) {
+ t.Errorf("handler returned unexpected body: got: %v, expected: %v", xapp, expectedData)
+ }
+ }
+}
+
+func generateXapp(name, status, ver, iname, istatus, ip, port string) (x Xapp) {
+ x.Name = name
+ x.Status = status
+ x.Version = ver
+ p, _ := strconv.Atoi(port)
+ instance := XappInstance{
+ Name: iname,
+ Status: istatus,
+ Ip: ip,
+ Port: p,
+ TxMessages: []string{"RIC_E2_TERMINATION_HC_REQUEST", "RIC_E2_MANAGER_HC_REQUEST"},
+ RxMessages: []string{"RIC_E2_TERMINATION_HC_RESPONSE", "RIC_E2_MANAGER_HC_RESPONSE"},
+ }
+ x.Instances = append(x.Instances, instance)
+
+ return
+}
package main
import (
- "flag"
- "log"
- "github.com/spf13/viper"
- "github.com/fsnotify/fsnotify"
+ "flag"
+ "github.com/fsnotify/fsnotify"
+ "github.com/spf13/viper"
+ "log"
)
-const DEFAULT_CONFIG_FILE = "../config/appmgr.yaml"
+const DEFAULT_CONFIG_FILE = "config/appmgr.yaml"
func parseCmd() string {
- var fileName *string
- fileName = flag.String("f", DEFAULT_CONFIG_FILE, "Specify the configuration file.")
- flag.Parse()
+ var fileName *string
+ fileName = flag.String("f", DEFAULT_CONFIG_FILE, "Specify the configuration file.")
+ flag.Parse()
- return *fileName
+ return *fileName
}
func loadConfig() {
- viper.SetConfigFile(parseCmd())
+ viper.SetConfigFile(parseCmd())
- if err := viper.ReadInConfig(); err != nil {
- log.Fatalf("Error reading config file, %s", err)
- }
- log.Printf("Using config file: %s\n", viper.ConfigFileUsed())
+ if err := viper.ReadInConfig(); err != nil {
+ log.Fatalf("Error reading config file, %s", err)
+ }
+ log.Printf("Using config file: %s\n", viper.ConfigFileUsed())
- // Watch for config file changes and re-read data ...
- watch()
+ // Watch for config file changes and re-read data ...
+ watch()
}
func watch() {
- viper.WatchConfig()
- viper.OnConfigChange(func(e fsnotify.Event) {
- log.Println("config file changed ", e.Name)
- })
+ viper.WatchConfig()
+ viper.OnConfigChange(func(e fsnotify.Event) {
+ log.Println("config file changed ", e.Name)
+ })
}
--- /dev/null
+/*
+==================================================================================
+ 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"
+ sdl "gerrit.oran-osc.org/r/ric-plt/sdlgo"
+ cmap "github.com/orcaman/concurrent-map"
+ "github.com/spf13/viper"
+ "time"
+)
+
+type DB struct {
+ session *sdl.SdlInstance
+}
+
+func (d *DB) Create() {
+ ns := viper.GetString("db.sessionNamespace")
+ d.session = sdl.Create(ns)
+
+ // Test DB connection, and wait until ready!
+ for {
+ if _, err := d.session.GetAll(); err == nil {
+ return
+ }
+ mdclog(MdclogErr, "Database connection not ready, waiting ...")
+ time.Sleep(time.Duration(5 * time.Second))
+ }
+}
+
+func (d *DB) StoreSubscriptions(m cmap.ConcurrentMap) {
+ for v := range m.Iter() {
+ s := v.Val.(Subscription)
+
+ data, err := json.Marshal(s.req)
+ if err != nil {
+ mdclog(MdclogErr, "json.marshal failed: "+err.Error())
+ return
+ }
+
+ if err := d.session.Set(s.req.Id, data); err != nil {
+ mdclog(MdclogErr, "DB.session.Set failed: "+err.Error())
+ }
+ }
+}
+
+func (d *DB) RestoreSubscriptions() (m cmap.ConcurrentMap) {
+ m = cmap.New()
+
+ keys, err := d.session.GetAll()
+ if err != nil {
+ mdclog(MdclogErr, "DB.session.GetAll failed: "+err.Error())
+ return
+ }
+
+ for _, key := range keys {
+ value, err := d.session.Get([]string{key})
+ if err != nil {
+ mdclog(MdclogErr, "DB.session.Get failed: "+err.Error())
+ return
+ }
+
+ var item SubscriptionReq
+ if err = json.Unmarshal([]byte(value[key].(string)), &item); err != nil {
+ mdclog(MdclogErr, "json.Unmarshal failed: "+err.Error())
+ return
+ }
+
+ resp := SubscriptionResp{key, 0, item.EventType}
+ m.Set(key, Subscription{item, resp})
+ }
+
+ return m
+}
--- /dev/null
+/*
+==================================================================================
+ 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 (
+ "bytes"
+ "errors"
+ "fmt"
+ "github.com/spf13/viper"
+ "gopkg.in/yaml.v2"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+)
+
+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
+ }
+
+ 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++ {
+ err = cmd.Run()
+ if err != nil {
+ mdclog(MdclogErr, formatLog("Command failed, retrying", args, err.Error()+stderr.String()))
+ time.Sleep(time.Duration(5) * time.Second)
+ continue
+ }
+ break
+ }
+
+ if err == nil && !strings.HasSuffix(os.Args[0], ".test") {
+ mdclog(MdclogDebug, formatLog("command success", stdout.String(), ""))
+ return stdout.Bytes(), nil
+ }
+
+ return stdout.Bytes(), errors.New(stderr.String())
+}
+
+func (h *Helm) Initialize() {
+ if h.initDone == true {
+ return
+ }
+
+ for {
+ if _, err := h.Init(); err == nil {
+ mdclog(MdclogDebug, formatLog("Helm init done successfully!", "", ""))
+ break
+ }
+ mdclog(MdclogErr, formatLog("helm init failed, retyring ...", "", ""))
+ time.Sleep(time.Duration(10) * time.Second)
+ }
+
+ for {
+ if _, err := h.AddRepo(); err == nil {
+ mdclog(MdclogDebug, formatLog("Helm repo added successfully", "", ""))
+ break
+ }
+ 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)
+}
+
+// API functions
+func (h *Helm) Init() (out []byte, err error) {
+
+ // Add Tiller address as environment variable
+ if err := addTillerEnv(); err != nil {
+ return out, err
+ }
+
+ return Exec(strings.Join([]string{"init -c"}, ""))
+}
+
+func (h *Helm) AddRepo() (out []byte, err error) {
+
+ // Get helm repo user name and password from files mounted by secret object
+ credFile, err := ioutil.ReadFile(viper.GetString("helm.helm-username-file"))
+ if err != nil {
+ mdclog(MdclogErr, formatLog("helm_repo_username ReadFile failed", "", err.Error()))
+ return
+ }
+
+ username := " --username " + string(credFile)
+
+ credFile, err = ioutil.ReadFile(viper.GetString("helm.helm-password-file"))
+ if err != nil {
+ mdclog(MdclogErr, formatLog("helm_repo_password ReadFile failed", "", err.Error()))
+ return
+ }
+
+ pwd := " --password " + string(credFile)
+
+ // Get internal helm repo name
+ rname := viper.GetString("helm.repo-name")
+
+ // Get helm repo address from values.yaml
+ repo := viper.GetString("helm.repo")
+
+ return Exec(strings.Join([]string{"repo add ", rname, " ", repo, username, pwd}, ""))
+}
+
+func (h *Helm) Install(name string) (xapp Xapp, err error) {
+ out, err := h.Run(strings.Join([]string{"repo update "}, ""))
+ if err != nil {
+ return
+ }
+
+ rname := viper.GetString("helm.repo-name")
+
+ ns := getNamespaceArgs()
+ out, err = h.Run(strings.Join([]string{"install ", rname, "/", name, " --name ", name, ns}, ""))
+ if err != nil {
+ return
+ }
+
+ return h.ParseStatus(name, string(out))
+}
+
+func (h *Helm) Status(name string) (xapp Xapp, err error) {
+
+ out, err := h.Run(strings.Join([]string{"status ", name}, ""))
+ if err != nil {
+ mdclog(MdclogErr, formatLog("Getting xapps status", "", err.Error()))
+ return
+ }
+
+ return h.ParseStatus(name, string(out))
+}
+
+func (h *Helm) StatusAll() (xapps []Xapp, err error) {
+ xappNameList, err := h.List()
+ if err != nil {
+ mdclog(MdclogErr, formatLog("Helm list failed", "", err.Error()))
+ return
+ }
+
+ return h.parseAllStatus(xappNameList)
+}
+
+func (h *Helm) List() (names []string, err error) {
+
+ ns := getNamespaceArgs()
+ out, err := h.Run(strings.Join([]string{"list --all --output yaml ", ns}, ""))
+ if err != nil {
+ mdclog(MdclogErr, formatLog("Listing deployed xapps failed", "", err.Error()))
+ return
+ }
+
+ return h.GetNames(string(out))
+}
+
+func (h *Helm) Delete(name string) (xapp Xapp, err error) {
+ xapp, err = h.Status(name)
+ if err != nil {
+ mdclog(MdclogErr, formatLog("Fetching xapp status failed", "", err.Error()))
+ return
+ }
+
+ _, err = h.Run(strings.Join([]string{"del --purge ", name}, ""))
+ return xapp, err
+}
+
+func (h *Helm) Fetch(name, tarDir string) error {
+ if strings.HasSuffix(os.Args[0], ".test") {
+ return nil
+ }
+
+ rname := viper.GetString("helm.repo-name") + "/"
+
+ _, err := h.Run(strings.Join([]string{"fetch --untar --untardir ", tarDir, " ", rname, name}, ""))
+ return err
+}
+
+// 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}, ""))
+ if err != nil {
+ return
+ }
+
+ var re = regexp.MustCompile(`AppVersion: .*`)
+ ver := re.FindStringSubmatch(string(out))
+ if ver != nil {
+ version = strings.Split(ver[0], ": ")[1]
+ version, _ = strconv.Unquote(version)
+ }
+
+ return
+}
+
+func (h *Helm) GetState(out string) (status string) {
+ re := regexp.MustCompile(`STATUS: .*`)
+ result := re.FindStringSubmatch(string(out))
+ if result != nil {
+ status = strings.ToLower(strings.Split(result[0], ": ")[1])
+ }
+
+ return
+}
+
+func (h *Helm) GetAddress(out string) (ip, port string) {
+ var tmp string
+ re := regexp.MustCompile(`ClusterIP.*`)
+ addr := re.FindStringSubmatch(string(out))
+ if addr != nil {
+ fmt.Sscanf(addr[0], "%s %s %s %s", &tmp, &ip, &tmp, &port)
+ }
+
+ return
+}
+
+func (h *Helm) GetNames(out string) (names []string, err error) {
+ re := regexp.MustCompile(`Name: .*`)
+ result := re.FindAllStringSubmatch(out, -1)
+ if result == nil {
+ return
+ }
+
+ for _, v := range result {
+ xappName := strings.Split(v[0], ": ")[1]
+ if strings.Contains(xappName, "appmgr") == false {
+ names = append(names, xappName)
+ }
+ }
+ return names, nil
+}
+
+func (h *Helm) FillInstanceData(name string, out string, xapp *Xapp, msgs MessageTypes) {
+ ip, port := h.GetAddress(out)
+
+ var tmp string
+ r := regexp.MustCompile(`(?s)\/Pod.*?\/Service`)
+ result := r.FindStringSubmatch(string(out))
+ if result == nil {
+ return
+ }
+
+ re := regexp.MustCompile(name + "-(\\d+).*")
+ resources := re.FindAllStringSubmatch(string(result[0]), -1)
+ if resources != nil {
+ for _, v := range resources {
+ var x XappInstance
+ fmt.Sscanf(v[0], "%s %s %s", &x.Name, &tmp, &x.Status)
+ x.Status = strings.ToLower(x.Status)
+ x.Ip = ip
+ x.Port, _ = strconv.Atoi(strings.Split(port, "/")[0])
+ x.TxMessages = msgs.TxMessages
+ x.RxMessages = msgs.RxMessages
+ xapp.Instances = append(xapp.Instances, x)
+ }
+ }
+}
+
+func (h *Helm) ParseStatus(name string, out string) (xapp Xapp, err error) {
+
+ xapp.Name = name
+ xapp.Version = h.GetVersion(name)
+ xapp.Status = h.GetState(out)
+
+ types, err := h.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()))
+
+ //Set err back to nil, so it does not cause issues in called functions.
+ err = nil
+ }
+
+ h.FillInstanceData(name, out, &xapp, types)
+
+ return
+}
+
+func (h *Helm) parseAllStatus(names []string) (xapps []Xapp, err error) {
+ xapps = []Xapp{}
+
+ for _, name := range names {
+ x, err := h.Status(name)
+ if err == nil {
+ xapps = append(xapps, x)
+ }
+ }
+
+ return
+}
+
+func addTillerEnv() (err error) {
+
+ service := viper.GetString("helm.tiller-service")
+ namespace := viper.GetString("helm.tiller-namespace")
+ port := viper.GetString("helm.tiller-port")
+
+ if err = os.Setenv("HELM_HOST", service+"."+namespace+":"+port); err != nil {
+ mdclog(MdclogErr, formatLog("Tiller Env Setting Failed", "", err.Error()))
+ }
+
+ return err
+}
+
+func getNamespaceArgs() string {
+ ns := viper.GetString("xapp.namespace")
+ if ns == "" {
+ ns = "ricxapp"
+ }
+ return " --namespace=" + ns
+}
+
+func formatLog(text string, args string, err string) string {
+ return fmt.Sprintf("Helm: %s: args=%s err=%s\n", text, args, err)
+}
*/
package main
+
/*
#cgo CFLAGS: -I/usr/local/include
#cgo LDFLAGS: -lmdclog
import "C"
import (
- "net/http"
- "time"
- "fmt"
+ "fmt"
+ "net/http"
+ "time"
)
func mdclog(severity C.mdclog_severity_t, msg string) {
- msg = fmt.Sprintf("%s:: %s ", time.Now().Format("2019-01-02 15:04:05"), msg)
+ 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.xAppMgr_mdclog_write(severity, C.CString(msg))
+}
- C.mdclog_mdc_add(C.CString("XM"), C.CString("1.0.0"))
- C.xAppMgr_mdclog_write(severity, C.CString(msg))
+func mdclogSetLevel(severity C.mdclog_severity_t) {
+ C.mdclog_level_set(severity)
}
func Logger(inner http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- inner.ServeHTTP(w, r)
- s := fmt.Sprintf("Logger: method=%s url=%s", r.Method, r.URL.RequestURI())
- mdclog(C.MDCLOG_DEBUG, s)
- })
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ inner.ServeHTTP(w, r)
+ s := fmt.Sprintf("Logger: method=%s url=%s", r.Method, r.URL.RequestURI())
+ mdclog(C.MDCLOG_DEBUG, s)
+ })
}
package main
func main() {
- loadConfig()
+ mdclogSetLevel(MdclogDebug)
- m := XappManager{}
- m.Initialize(&Helm{})
+ loadConfig()
- m.Run()
+ m := XappManager{}
+ m.Initialize(&Helm{})
+
+ m.Run()
}
--- /dev/null
+/*
+==================================================================================
+ 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 (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "github.com/segmentio/ksuid"
+ "net/http"
+ "time"
+)
+
+func (sd *SubscriptionDispatcher) Initialize() {
+ sd.client = &http.Client{}
+
+ sd.db = &DB{}
+ sd.db.Create()
+ sd.subscriptions = sd.db.RestoreSubscriptions()
+}
+
+func (sd *SubscriptionDispatcher) Add(sr SubscriptionReq) (resp SubscriptionResp) {
+ key := ksuid.New().String()
+ resp = SubscriptionResp{key, 0, sr.EventType}
+ sr.Id = key
+
+ sd.subscriptions.Set(key, Subscription{sr, resp})
+ sd.db.StoreSubscriptions(sd.subscriptions)
+
+ mdclog(MdclogInfo, fmt.Sprintf("Sub: New subscription added: key=%s value=%v", key, sr))
+ return
+}
+
+func (sd *SubscriptionDispatcher) GetAll() (hooks []SubscriptionReq) {
+ hooks = []SubscriptionReq{}
+ for v := range sd.subscriptions.IterBuffered() {
+ hooks = append(hooks, v.Val.(Subscription).req)
+ }
+
+ return hooks
+}
+
+func (sd *SubscriptionDispatcher) Get(id string) (SubscriptionReq, bool) {
+ if v, found := sd.subscriptions.Get(id); found {
+ mdclog(MdclogInfo, fmt.Sprintf("Subscription id=%s found: %v", id, v.(Subscription).req))
+
+ return v.(Subscription).req, found
+ }
+ return SubscriptionReq{}, false
+}
+
+func (sd *SubscriptionDispatcher) Delete(id string) (SubscriptionReq, bool) {
+ if v, found := sd.subscriptions.Get(id); found {
+ mdclog(MdclogInfo, fmt.Sprintf("Subscription id=%s found: %v ... deleting", id, v.(Subscription).req))
+
+ sd.subscriptions.Remove(id)
+ sd.db.StoreSubscriptions(sd.subscriptions)
+
+ return v.(Subscription).req, found
+ }
+ return SubscriptionReq{}, false
+}
+
+func (sd *SubscriptionDispatcher) Update(id string, sr SubscriptionReq) (SubscriptionReq, bool) {
+ if s, found := sd.subscriptions.Get(id); found {
+ mdclog(MdclogInfo, fmt.Sprintf("Subscription id=%s found: %v ... updating", id, s.(Subscription).req))
+
+ sr.Id = id
+ sd.subscriptions.Set(id, Subscription{sr, s.(Subscription).resp})
+ sd.db.StoreSubscriptions(sd.subscriptions)
+
+ return sr, found
+ }
+ return SubscriptionReq{}, false
+}
+
+func (sd *SubscriptionDispatcher) Publish(x Xapp, et EventType) {
+ sd.notifyClients([]Xapp{x}, et)
+}
+
+func (sd *SubscriptionDispatcher) notifyClients(xapps []Xapp, et EventType) {
+ if len(xapps) == 0 || len(sd.subscriptions) == 0 {
+ mdclog(MdclogInfo, fmt.Sprintf("Nothing to publish [%d:%d]", len(xapps), len(sd.subscriptions)))
+ return
+ }
+
+ sd.Seq = sd.Seq + 1
+ for v := range sd.subscriptions.Iter() {
+ go sd.notify(xapps, et, v.Val.(Subscription), sd.Seq)
+ }
+}
+
+func (sd *SubscriptionDispatcher) notify(xapps []Xapp, et EventType, s Subscription, seq int) error {
+ notif := []SubscriptionNotif{}
+ notif = append(notif, SubscriptionNotif{Id: s.req.Id, Version: seq, EventType: string(et), XappData: xapps})
+
+ jsonData, err := json.Marshal(notif)
+ if err != nil {
+ mdclog(MdclogInfo, fmt.Sprintf("json.Marshal failed: %v", err))
+ return err
+ }
+
+ // Execute the request with retry policy
+ return sd.retry(s, func() error {
+ resp, err := http.Post(s.req.TargetUrl, "application/json", bytes.NewBuffer(jsonData))
+ if err != nil {
+ mdclog(MdclogInfo, fmt.Sprintf("Posting to subscription failed: %v", err))
+ return err
+ }
+
+ if resp.StatusCode != http.StatusOK {
+ mdclog(MdclogInfo, fmt.Sprintf("Client returned error code: %d", resp.StatusCode))
+ return err
+ }
+
+ mdclog(MdclogInfo, fmt.Sprintf("subscription to '%s' dispatched, response code: %d \n", s.req.TargetUrl, resp.StatusCode))
+ return nil
+ })
+}
+
+func (sd *SubscriptionDispatcher) retry(s Subscription, fn func() error) error {
+ if err := fn(); err != nil {
+ // Todo: use exponential backoff, or similar mechanism
+ if s.req.MaxRetries--; s.req.MaxRetries > 0 {
+ time.Sleep(time.Duration(s.req.RetryTimer) * time.Second)
+ return sd.retry(s, fn)
+ }
+ sd.subscriptions.Remove(s.req.Id)
+ return err
+ }
+ return nil
+}
--- /dev/null
+/*
+==================================================================================
+ 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 (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ sdl "gerrit.oran-osc.org/r/ric-plt/sdlgo"
+ "github.com/spf13/viper"
+ "log"
+ "net"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+)
+
+var resp SubscriptionResp
+
+// Test cases
+func TestNoSubscriptionsFound(t *testing.T) {
+ req, _ := http.NewRequest("GET", "/ric/v1/subscriptions", nil)
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusOK, response.Code)
+ if body := response.Body.String(); body != "[]" {
+ t.Errorf("handler returned unexpected body: got %v want []", body)
+ }
+}
+
+func TestAddNewSubscription(t *testing.T) {
+ payload := []byte(`{"maxRetries": 3, "retryTimer": 5, "eventType":"Created", "targetUrl": "http://localhost:8087/xapps_handler"}`)
+ req, _ := http.NewRequest("POST", "/ric/v1/subscriptions", bytes.NewBuffer(payload))
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusCreated, response.Code)
+
+ json.NewDecoder(response.Body).Decode(&resp)
+ if resp.Version != 0 {
+ t.Errorf("Creating new subscription failed: %v", resp)
+ }
+}
+
+func TestGettAllSubscriptions(t *testing.T) {
+ req, _ := http.NewRequest("GET", "/ric/v1/subscriptions", nil)
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusOK, response.Code)
+
+ var subscriptions []SubscriptionReq
+ json.NewDecoder(response.Body).Decode(&subscriptions)
+
+ verifySubscription(t, subscriptions[0], "http://localhost:8087/xapps_handler", 3, 5, "Created")
+}
+
+func TestGetSingleSubscription(t *testing.T) {
+ req, _ := http.NewRequest("GET", "/ric/v1/subscriptions/"+resp.Id, nil)
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusOK, response.Code)
+
+ var subscription SubscriptionReq
+ json.NewDecoder(response.Body).Decode(&subscription)
+
+ verifySubscription(t, subscription, "http://localhost:8087/xapps_handler", 3, 5, "Created")
+}
+
+func TestUpdateSingleSubscription(t *testing.T) {
+ payload := []byte(`{"maxRetries": 11, "retryTimer": 22, "eventType":"Deleted", "targetUrl": "http://localhost:8088/xapps_handler"}`)
+
+ req, _ := http.NewRequest("PUT", "/ric/v1/subscriptions/"+resp.Id, bytes.NewBuffer(payload))
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusOK, response.Code)
+
+ var res SubscriptionResp
+ json.NewDecoder(response.Body).Decode(&res)
+ if res.Version != 0 {
+ t.Errorf("handler returned unexpected data: %v", resp)
+ }
+
+ // Check that the subscription is updated properly
+ req, _ = http.NewRequest("GET", "/ric/v1/subscriptions/"+resp.Id, nil)
+ response = executeRequest(req)
+ checkResponseCode(t, http.StatusOK, response.Code)
+
+ var subscription SubscriptionReq
+ json.NewDecoder(response.Body).Decode(&subscription)
+
+ verifySubscription(t, subscription, "http://localhost:8088/xapps_handler", 11, 22, "Deleted")
+}
+
+func TestDeleteSingleSubscription(t *testing.T) {
+ req, _ := http.NewRequest("DELETE", "/ric/v1/subscriptions/"+resp.Id, nil)
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusNoContent, response.Code)
+
+ // Check that the subscription is removed properly
+ req, _ = http.NewRequest("GET", "/ric/v1/subscriptions/"+resp.Id, nil)
+ response = executeRequest(req)
+ checkResponseCode(t, http.StatusNotFound, response.Code)
+}
+
+func TestDeleteSingleSubscriptionFails(t *testing.T) {
+ req, _ := http.NewRequest("DELETE", "/ric/v1/subscriptions/invalidSubscriptionId", nil)
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusNotFound, response.Code)
+}
+
+func TestAddSingleSubscriptionFailsBodyEmpty(t *testing.T) {
+ req, _ := http.NewRequest("POST", "/ric/v1/subscriptions/"+resp.Id, nil)
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusMethodNotAllowed, response.Code)
+}
+
+func TestUpdateeSingleSubscriptionFailsBodyEmpty(t *testing.T) {
+ req, _ := http.NewRequest("PUT", "/ric/v1/subscriptions/"+resp.Id, nil)
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusMethodNotAllowed, response.Code)
+}
+
+func TestUpdateeSingleSubscriptionFailsInvalidId(t *testing.T) {
+ payload := []byte(`{"maxRetries": 11, "retryTimer": 22, "eventType":"Deleted", "targetUrl": "http://localhost:8088/xapps_handler"}`)
+
+ req, _ := http.NewRequest("PUT", "/ric/v1/subscriptions/invalidSubscriptionId"+resp.Id, bytes.NewBuffer(payload))
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusNotFound, response.Code)
+}
+
+func TestPublishXappAction(t *testing.T) {
+ payload := []byte(`{"maxRetries": 3, "retryTimer": 5, "eventType":"Created", "targetUrl": "http://127.0.0.1:8888"}`)
+ req, _ := http.NewRequest("POST", "/ric/v1/subscriptions", bytes.NewBuffer(payload))
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusCreated, response.Code)
+
+ // Create a RestApi server (simulating RM)
+ ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintln(w, "Hello, XM!")
+ }))
+
+ l, err := net.Listen("tcp", "127.0.0.1:8888")
+ if err != nil {
+ log.Fatal(err)
+ }
+ ts.Listener.Close()
+ ts.Listener = l
+ ts.Start()
+
+ defer ts.Close()
+
+ x.sd.Publish(xapp, EventType("created"))
+}
+
+func TestTeardown(t *testing.T) {
+ db := sdl.Create(viper.GetString("db.sessionNamespace"))
+ db.RemoveAll()
+}
+
+func verifySubscription(t *testing.T, subscription SubscriptionReq, url string, retries int, timer int, event string) {
+ if subscription.TargetUrl != url {
+ t.Errorf("Unexpected url: got=%s expected=%s", subscription.TargetUrl, url)
+ }
+
+ if subscription.MaxRetries != retries {
+ t.Errorf("Unexpected retries: got=%d expected=%d", subscription.MaxRetries, retries)
+ }
+
+ if subscription.RetryTimer != timer {
+ t.Errorf("Unexpected timer: got=%d expected=%d", subscription.RetryTimer, timer)
+ }
+
+ if subscription.EventType != event {
+ t.Errorf("Unexpected event type: got=%s expected=%s", subscription.EventType, event)
+ }
+}
--- /dev/null
+/*
+==================================================================================
+ 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 (
+ "github.com/gorilla/mux"
+ cmap "github.com/orcaman/concurrent-map"
+ "net/http"
+)
+
+type CmdOptions struct {
+ hostAddr *string
+ helmHost *string
+ helmChartPath *string
+}
+
+type Resource struct {
+ Method string
+ Url string
+ HandlerFunc http.HandlerFunc
+}
+
+type Xapp struct {
+ Name string `json:"name"`
+ Status string `json:"status"`
+ Version string `json:"version"`
+ Instances []XappInstance `json:"instances"`
+}
+
+type XappInstance struct {
+ Name string `json:"name"`
+ Status string `json:"status"`
+ Ip string `json:"ip"`
+ Port int `json:"port"`
+ TxMessages []string `json:"txMessages"`
+ RxMessages []string `json:"rxMessages"`
+}
+
+type XappManager struct {
+ router *mux.Router
+ helm Helmer
+ sd SubscriptionDispatcher
+ opts CmdOptions
+ ready bool
+}
+
+type Helmer interface {
+ Initialize()
+ Install(name string) (xapp Xapp, err error)
+ Status(name string) (xapp Xapp, err error)
+ StatusAll() (xapps []Xapp, err error)
+ List() (xapps []string, err error)
+ Delete(name string) (xapp Xapp, err error)
+}
+
+type Helm struct {
+ host string
+ chartPath string
+ initDone bool
+}
+
+type SubscriptionReq struct {
+ Id string `json:"id"`
+ TargetUrl string `json:"targetUrl"`
+ EventType string `json:"eventType"`
+ MaxRetries int `json:"maxRetries"`
+ RetryTimer int `json:"retryTimer"`
+}
+
+type SubscriptionResp struct {
+ Id string `json:"id"`
+ Version int `json:"version"`
+ EventType string `json:"eventType"`
+}
+
+type SubscriptionNotif struct {
+ Id string `json:"id"`
+ Version int `json:"version"`
+ EventType string `json:"eventType"`
+ XappData []Xapp `json:"xapp"`
+}
+
+type Subscription struct {
+ req SubscriptionReq
+ resp SubscriptionResp
+}
+
+type SubscriptionDispatcher struct {
+ client *http.Client
+ subscriptions cmap.ConcurrentMap
+ db *DB
+ Seq int
+}
+
+type MessageTypes struct {
+ TxMessages []string `yaml:"txMessages"`
+ RxMessages []string `yaml:"rxMessages"`
+}
+
+type EventType string
+
+const (
+ Created EventType = "created"
+ Updated EventType = "updated"
+ Deleted EventType = "deleted"
+)
+
+const (
+ MdclogErr = 1 //! Error level log entry
+ MdclogWarn = 2 //! Warning level log entry
+ MdclogInfo = 3 //! Info level log entry
+ MdclogDebug = 4 //! Debug level log entry
+)
"helm":
"host": "192.168.0.12:31807"
"repo": "/opt/ric/dummy-xapp-chart"
+ "repo-name": "dummy"
"secrets":
"username": "admin"
"password": "ric"
"xapp":
"namespace": "ricxapp"
"tarDir": "/tmp"
+ "msg_type_file": "msg_type.yaml"
"db":
+ "sessionNamespace": "XMSession"
"host": ":6379"
"prot": "tcp"
"maxIdle": 80
- "maxActive": 12000
\ No newline at end of file
+ "maxActive": 12000
RUN apt-get update -y && \
apt-get install -y wget
-#RUN sed -i -e 's,http://archive.ubuntu.com/ubuntu,mirror://mirrors.ubuntu.com/mirrors.txt,' /etc/apt/sources.list
-#RUN sed -i -e 's,http://security.ubuntu.com/ubuntu,mirror://mirrors.ubuntu.com/mirrors.txt,' /etc/apt/sources.list
-#RUN sed -i -e 's,http://archive.ubuntu.com/ubuntu,http://mirrors.nic.funet.fi/ubuntu,' /etc/apt/sources.list
-#RUN sed -i -e 's,http://security.ubuntu.com/ubuntu,http://mirrors.nic.funet.fi/ubuntu,' /etc/apt/sources.list
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
&& cd log/ ; ./autogen.sh ; ./configure ; make ; make install \
&& ldconfig
-
#----------------------------------------------------------
#
#----------------------------------------------------------
FROM ubuntubase as builder
-ARG HELMVERSION
+ARG PACKAGEURL=gerrit.oran-osc.org/r/ric-plt/appmgr
+ARG HELMVERSION=v2.13.0-rc.1
#
# helm
#
-# xapp_manager codes
+# appmgr codes
#
-RUN mkdir -p /go/src/appmgr
+RUN mkdir -p /go/src/${PACKAGEURL}
ENV GOPATH="/go"
#
&& go get gopkg.in/yaml.v2
-COPY . /go/src/appmgr
+COPY . /go/src/${PACKAGEURL}
#
# build
#
-RUN make -C /go/src/appmgr/build deps
-
-RUN make -C /go/src/appmgr/build build
+RUN make -C /go/src/${PACKAGEURL} build
#----------------------------------------------------------
#
#----------------------------------------------------------
FROM builder as test_unit
-WORKDIR "/go/src/appmgr"
-CMD ["make","-C","build", "unit-test"]
+ARG PACKAGEURL=gerrit.oran-osc.org/r/ric-plt/appmgr
+WORKDIR "/go/src/${PACKAGEURL}"
+CMD ["make","test"]
+#----------------------------------------------------------
+#
+#----------------------------------------------------------
+FROM builder as test_fmt
+ARG PACKAGEURL=gerrit.oran-osc.org/r/ric-plt/appmgr
+WORKDIR "/go/src/${PACKAGEURL}"
+CMD ["make","test-fmt"]
+
#----------------------------------------------------------
#
#----------------------------------------------------------
FROM builder as test_sanity
-WORKDIR "/go/src/appmgr"
-CMD ["jq","-s",".", "rest_api/xapp_manager_rest_api.json"]
+ARG PACKAGEURL=gerrit.oran-osc.org/r/ric-plt/appmgr
+WORKDIR "/go/src/${PACKAGEURL}"
+CMD ["jq","-s",".", "api/appmgr_rest_api.json"]
+
#----------------------------------------------------------
#
#----------------------------------------------------------
FROM ubuntu:16.04 as release
+ARG PACKAGEURL=gerrit.oran-osc.org/r/ric-plt/appmgr
RUN apt-get update -y \
&& apt-get install -y sudo openssl ca-certificates ca-cacert \
RUN mkdir -p /opt/xAppManager \
&& chmod -R 755 /opt/xAppManager
-COPY --from=builder /go/src/appmgr/build/appmgr /opt/xAppManager/appmgr
-#COPY --from=builder /go/src/appmgr/config/appmgr.yaml /opt/etc/xAppManager/config-file.yaml
+COPY --from=builder /go/src/${PACKAGEURL}/build/appmgr /opt/xAppManager/appmgr
+#COPY --from=builder /go/src/${PACKAGEURL}/config/appmgr.yaml /opt/etc/xAppManager/config-file.yaml
-COPY build/docker-entrypoint.sh /opt/xAppManager/
+COPY docker/docker-entrypoint.sh /opt/xAppManager/
WORKDIR /opt/xAppManager
--- /dev/null
+module gerrit.oran-osc.org/r/ric-plt/appmgr
+
+go 1.12
+
+require (
+ gerrit.oran-osc.org/r/ric-plt/sdlgo v0.0.0
+ 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
+)
+
+replace gerrit.oran-osc.org/r/ric-plt/sdlgo => ./internal/sdlgo
--- /dev/null
+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=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+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/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/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/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/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+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=
+github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/segmentio/ksuid v1.0.2 h1:9yBfKyw4ECGTdALaF09Snw3sLJmYIX6AbPJrAy6MrDc=
+github.com/segmentio/ksuid v1.0.2/go.mod h1:BXuJDr2byAiHuQaQtSKoXh1J0YmUDurywOXgB2w+OSU=
+github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+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/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/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+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/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+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=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+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/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+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=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
name: {{ .Release.Name }}-appenv
livenessProbe:
httpGet:
- path: {{ .Values.service.health_check_endpoint }}
+ path: {{ .Values.service.health_alive_check_endpoint }}
port: 8080
initialDelaySeconds: 5
- periodSeconds: 15
+ periodSeconds: 5
+ failureThreshold: 3
readinessProbe:
httpGet:
- path: {{ .Values.service.health_check_endpoint }}
+ path: {{ .Values.service.health_ready_check_endpoint }}
port: 8080
initialDelaySeconds: 5
- periodSeconds: 15
+ periodSeconds: 5
+ failureThreshold: 3
restartPolicy: Always
resources:
{{- toYaml .Values.resources | nindent 12 }}
#repositoryCred:
# user: docker
# password: docker
- pullPolicy: IfNotPresent
+ pullPolicy: Always
# This section describes xAppManager
replicaCount: 1
port: 8080
nodePort: 30218
name: appmgr-service
- health_check_endpoint: ric/v1/health
+ health_alive_check_endpoint: ric/v1/health/alive
+ health_ready_check_endpoint: ric/v1/health/ready
# config
# Path referred in appmgr for retrieving configuration details
"helm-password-file": "/opt/ric/secret/helm_repo_password"
"xapp":
#Namespace to install xAPPs
- "namespace": ricxapp
+ "namespace": "ricxapp"
+
+ #File containing xAPP message types
+ "msg_type_file": "msg_type.yaml"
# To be provided as env variables
appenv:
--- /dev/null
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ 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.
--- /dev/null
+package sdlgo_test
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "testing"
+
+ "gitlabe1.ext.net.nokia.com/ric_dev/sdlgo"
+)
+
+type singleBenchmark struct {
+ key string
+ keySize int
+ valueSize int
+}
+
+type multiBenchmark struct {
+ keyBase string
+ keyCount int
+ keySize int
+ valueSize int
+}
+
+func (bm singleBenchmark) String(oper string) string {
+ return fmt.Sprintf("op = %s key=%d value=%d", oper, bm.keySize, bm.valueSize)
+}
+
+func (bm multiBenchmark) String(oper string) string {
+ return fmt.Sprintf("op = %s keycnt=%d key=%d value=%d", oper, bm.keyCount, bm.keySize, bm.valueSize)
+}
+func BenchmarkSet(b *testing.B) {
+ benchmarks := []singleBenchmark{
+ {"a", 10, 64},
+ {"b", 10, 1024},
+ {"c", 10, 64 * 1024},
+ {"d", 10, 1024 * 1024},
+ {"e", 10, 10 * 1024 * 1024},
+
+ {"f", 100, 64},
+ {"g", 100, 1024},
+ {"h", 100, 64 * 1024},
+ {"i", 100, 1024 * 1024},
+ {"j", 100, 10 * 1024 * 1024},
+ }
+
+ for _, bm := range benchmarks {
+ b.Run(bm.String("set"), func(b *testing.B) {
+ key := strings.Repeat(bm.key, bm.keySize)
+ value := strings.Repeat("1", bm.valueSize)
+ sdl := sdlgo.Create("namespace")
+
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ err := sdl.Set(key, value)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+ })
+ }
+}
+
+func BenchmarkGet(b *testing.B) {
+ benchmarks := []singleBenchmark{
+ {"a", 10, 64},
+ {"b", 10, 1024},
+ {"c", 10, 64 * 1024},
+ {"d", 10, 1024 * 1024},
+ {"e", 10, 10 * 1024 * 1024},
+
+ {"f", 100, 64},
+ {"g", 100, 1024},
+ {"h", 100, 64 * 1024},
+ {"i", 100, 1024 * 1024},
+ {"j", 100, 10 * 1024 * 1024},
+ }
+
+ for _, bm := range benchmarks {
+ b.Run(bm.String("Get"), func(b *testing.B) {
+ key := strings.Repeat(bm.key, bm.keySize)
+ value := strings.Repeat("1", bm.valueSize)
+ sdl := sdlgo.Create("namespace")
+ if err := sdl.Set(key, value); err != nil {
+ b.Fatal(err)
+ }
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _, err := sdl.Get([]string{key})
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+ })
+ }
+}
+
+func BenchmarkMultiSet(b *testing.B) {
+ benchmarks := []multiBenchmark{
+ {"a", 2, 10, 64},
+ {"b", 10, 10, 64},
+ {"c", 100, 10, 64},
+ {"d", 1000, 10, 64},
+ {"e", 5000, 10, 64},
+
+ {"f", 2, 100, 64},
+ {"g", 10, 100, 64},
+ {"h", 100, 100, 64},
+ {"i", 1000, 100, 64},
+ {"j", 5000, 100, 64},
+ }
+
+ for _, bm := range benchmarks {
+ b.Run(bm.String("mset"), func(b *testing.B) {
+ sdl := sdlgo.Create("namespace")
+ value := strings.Repeat("1", bm.valueSize)
+ keyVals := make([]string, 0)
+ for i := 0; i < bm.keyCount; i++ {
+ key := strings.Repeat(bm.keyBase+strconv.Itoa(i), bm.keySize)
+ keyVals = append(keyVals, key, value)
+ }
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ err := sdl.Set(keyVals)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+ })
+ }
+}
+
+func BenchmarkMultiGet(b *testing.B) {
+ benchmarks := []multiBenchmark{
+ {"a", 2, 10, 64},
+ {"b", 10, 10, 64},
+ {"c", 100, 10, 64},
+ {"d", 1000, 10, 64},
+ {"e", 5000, 10, 64},
+
+ {"f", 2, 100, 64},
+ {"g", 10, 100, 64},
+ {"h", 100, 100, 64},
+ {"i", 1000, 100, 64},
+ {"j", 5000, 100, 64},
+ }
+
+ for _, bm := range benchmarks {
+ b.Run(bm.String("gset"), func(b *testing.B) {
+ sdl := sdlgo.Create("namespace")
+ keyVals := make([]string, 0)
+ for i := 0; i < bm.keyCount; i++ {
+ key := strings.Repeat(bm.keyBase+strconv.Itoa(i), bm.keySize)
+ keyVals = append(keyVals, key)
+ }
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _, err := sdl.Get(keyVals)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+ })
+ }
+}
--- /dev/null
+module gerrit.oran-osc.org/r/ric-plt/sdlgo
+
+go 1.12
+
+require (
+ github.com/go-redis/redis v6.15.2+incompatible
+ github.com/onsi/ginkgo v1.8.0 // indirect
+ github.com/onsi/gomega v1.5.0 // indirect
+ github.com/stretchr/testify v1.3.0
+)
+
+replace gerrit.oran-osc.org/r/ric-plt/sdlgo/internal/sdlgoredis => ./internal/sdlgoredis
--- /dev/null
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/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/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+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.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+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 h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+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 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
--- /dev/null
+package sdlgoredis
+
+import (
+ "os"
+
+ "github.com/go-redis/redis"
+)
+
+type DB struct {
+ client *redis.Client
+}
+
+func Create() *DB {
+ hostname := os.Getenv("DBAAS_SERVICE_HOST")
+ if hostname == "" {
+ hostname = "localhost"
+ }
+ port := os.Getenv("DBAAS_SERVICE_PORT")
+ if port == "" {
+ port = "6379"
+ }
+ redisAddress := hostname + ":" + port
+ client := redis.NewClient(&redis.Options{
+ Addr: redisAddress,
+ Password: "", // no password set
+ DB: 0, // use default DB
+ PoolSize: 20,
+ })
+
+ db := DB{
+ client: client,
+ }
+
+ return &db
+}
+
+func (db *DB) Close() error {
+ return db.Close()
+}
+
+func (db *DB) MSet(pairs ...interface{}) error {
+ return db.client.MSet(pairs...).Err()
+}
+
+func (db *DB) MGet(keys []string) ([]interface{}, error) {
+ val, err := db.client.MGet(keys...).Result()
+ return val, err
+}
+
+func (db *DB) Del(keys []string) error {
+ _, err := db.client.Del(keys...).Result()
+ return err
+}
+
+func (db *DB) Keys(pattern string) ([]string, error) {
+ val, err := db.client.Keys(pattern).Result()
+ return val, err
+}
--- /dev/null
+/*
+ Copyright (c) 2019 AT&T Intellectual Property.
+ Copyright (c) 2018-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 sdlgo
+
+import (
+ "reflect"
+ "strings"
+
+ "gerrit.oran-osc.org/r/ric-plt/sdlgo/internal/sdlgoredis"
+)
+
+type Idatabase interface {
+ MSet(pairs ...interface{}) error
+ MGet(keys []string) ([]interface{}, error)
+ Close() error
+ Del(keys []string) error
+ Keys(key string) ([]string, error)
+}
+
+type SdlInstance struct {
+ NameSpace string
+ NsPrefix string
+ Idatabase
+}
+
+func Create(NameSpace string) *SdlInstance {
+ db := sdlgoredis.Create()
+ s := SdlInstance{
+ NameSpace: NameSpace,
+ NsPrefix: "{" + NameSpace + "},",
+ Idatabase: db,
+ }
+
+ return &s
+}
+
+func (s *SdlInstance) Close() error {
+ return s.Close()
+}
+
+func (s *SdlInstance) setNamespaceToKeys(pairs ...interface{}) []interface{} {
+ var retVal []interface{}
+ for i, v := range pairs {
+ if i%2 == 0 {
+ reflectType := reflect.TypeOf(v)
+ switch reflectType.Kind() {
+ case reflect.Slice:
+ x := reflect.ValueOf(v)
+ for i2 := 0; i2 < x.Len(); i2++ {
+ if i2%2 == 0 {
+ retVal = append(retVal, s.NsPrefix+x.Index(i2).Interface().(string))
+ } else {
+ retVal = append(retVal, x.Index(i2).Interface())
+ }
+ }
+ case reflect.Array:
+ x := reflect.ValueOf(v)
+ for i2 := 0; i2 < x.Len(); i2++ {
+ if i2%2 == 0 {
+ retVal = append(retVal, s.NsPrefix+x.Index(i2).Interface().(string))
+ } else {
+ retVal = append(retVal, x.Index(i2).Interface())
+ }
+ }
+ default:
+ retVal = append(retVal, s.NsPrefix+v.(string))
+ }
+ } else {
+ retVal = append(retVal, v)
+ }
+ }
+ return retVal
+}
+
+func (s *SdlInstance) Set(pairs ...interface{}) error {
+ if len(pairs) == 0 {
+ return nil
+ }
+
+ keyAndData := s.setNamespaceToKeys(pairs...)
+ err := s.MSet(keyAndData...)
+ return err
+}
+
+func (s *SdlInstance) Get(keys []string) (map[string]interface{}, error) {
+ m := make(map[string]interface{})
+ if len(keys) == 0 {
+ return m, nil
+ }
+
+ var keysWithNs []string
+ for _, v := range keys {
+ keysWithNs = append(keysWithNs, s.NsPrefix+v)
+ }
+ val, err := s.MGet(keysWithNs)
+ if err != nil {
+ return m, err
+ }
+ for i, v := range val {
+ m[keys[i]] = v
+ }
+ return m, err
+}
+
+func (s *SdlInstance) SetIf(key string, oldData, newData interface{}) {
+ panic("SetIf not implemented\n")
+}
+
+func (s *SdlInstance) SetIfiNotExists(key string, data interface{}) {
+ panic("SetIfiNotExists not implemented\n")
+}
+
+func (s *SdlInstance) Remove(keys []string) error {
+ if len(keys) == 0 {
+ return nil
+ }
+
+ var keysWithNs []string
+ for _, v := range keys {
+ keysWithNs = append(keysWithNs, s.NsPrefix+v)
+ }
+ err := s.Del(keysWithNs)
+ return err
+}
+
+func (s *SdlInstance) RemoveIf(key string, data interface{}) {
+ panic("RemoveIf not implemented\n")
+}
+
+func (s *SdlInstance) GetAll() ([]string, error) {
+ keys, err := s.Keys(s.NsPrefix + "*")
+ var retVal []string = nil
+ if err != nil {
+ return retVal, err
+ }
+ for _, v := range keys {
+ retVal = append(retVal, strings.Split(v, s.NsPrefix)[1])
+ }
+ return retVal, err
+}
+
+func (s *SdlInstance) RemoveAll() error {
+ keys, err := s.Keys(s.NsPrefix + "*")
+ if err != nil {
+ return err
+ }
+ if keys != nil {
+ err = s.Del(keys)
+ }
+ return err
+}
--- /dev/null
+package sdlgo_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+ "gerrit.oran-osc.org/r/ric-plt/sdlgo"
+)
+
+type mockDB struct {
+ mock.Mock
+}
+
+func (m *mockDB) MSet(pairs ...interface{}) error {
+ a := m.Called(pairs)
+ return a.Error(0)
+}
+
+func (m *mockDB) MGet(keys []string) ([]interface{}, error) {
+ a := m.Called(keys)
+ return a.Get(0).([]interface{}), a.Error(1)
+}
+
+func (m *mockDB) Close() error {
+ a := m.Called()
+ return a.Error(0)
+}
+
+func (m *mockDB) Del(keys []string) error {
+ a := m.Called(keys)
+ return a.Error(0)
+}
+
+func (m *mockDB) Keys(pattern string) ([]string, error) {
+ a := m.Called(pattern)
+ return a.Get(0).([]string), a.Error(1)
+}
+
+func setup() (*mockDB, *sdlgo.SdlInstance) {
+ m := new(mockDB)
+ i := &sdlgo.SdlInstance{
+ NameSpace: "namespace",
+ NsPrefix: "{namespace},",
+ Idatabase: m,
+ }
+ return m, i
+}
+
+func TestGetOneKey(t *testing.T) {
+ m, i := setup()
+
+ mgetExpected := []string{"{namespace},key"}
+ mReturn := []interface{}{"somevalue"}
+ mReturnExpected := make(map[string]interface{})
+ mReturnExpected["key"] = "somevalue"
+
+ m.On("MGet", mgetExpected).Return(mReturn, nil)
+ retVal, err := i.Get([]string{"key"})
+ assert.Nil(t, err)
+ assert.Equal(t, mReturnExpected, retVal)
+ m.AssertExpectations(t)
+}
+
+func TestGetSeveralKeys(t *testing.T) {
+ m, i := setup()
+
+ mgetExpected := []string{"{namespace},key1", "{namespace},key2", "{namespace},key3"}
+ mReturn := []interface{}{"somevalue1", 2, "someothervalue"}
+ mReturnExpected := make(map[string]interface{})
+ mReturnExpected["key1"] = "somevalue1"
+ mReturnExpected["key2"] = 2
+ mReturnExpected["key3"] = "someothervalue"
+
+ m.On("MGet", mgetExpected).Return(mReturn, nil)
+ retVal, err := i.Get([]string{"key1", "key2", "key3"})
+ assert.Nil(t, err)
+ assert.Equal(t, mReturnExpected, retVal)
+ m.AssertExpectations(t)
+}
+
+func TestGetSeveralKeysSomeFail(t *testing.T) {
+ m, i := setup()
+
+ mgetExpected := []string{"{namespace},key1", "{namespace},key2", "{namespace},key3"}
+ mReturn := []interface{}{"somevalue1", nil, "someothervalue"}
+ mReturnExpected := make(map[string]interface{})
+ mReturnExpected["key1"] = "somevalue1"
+ mReturnExpected["key2"] = nil
+ mReturnExpected["key3"] = "someothervalue"
+
+ m.On("MGet", mgetExpected).Return(mReturn, nil)
+ retVal, err := i.Get([]string{"key1", "key2", "key3"})
+ assert.Nil(t, err)
+ assert.Equal(t, mReturnExpected, retVal)
+ m.AssertExpectations(t)
+}
+
+func TestGetKeyReturnError(t *testing.T) {
+ m, i := setup()
+
+ mgetExpected := []string{"{namespace},key"}
+ mReturn := []interface{}{nil}
+ mReturnExpected := make(map[string]interface{})
+
+ m.On("MGet", mgetExpected).Return(mReturn, errors.New("Some error"))
+ retVal, err := i.Get([]string{"key"})
+ assert.NotNil(t, err)
+ assert.Equal(t, mReturnExpected, retVal)
+ m.AssertExpectations(t)
+}
+
+func TestGetEmptyList(t *testing.T) {
+ m, i := setup()
+
+ mgetExpected := []string{}
+
+ retval, err := i.Get([]string{})
+ assert.Nil(t, err)
+ assert.Len(t, retval, 0)
+ m.AssertNotCalled(t, "MGet", mgetExpected)
+}
+
+func TestWriteOneKey(t *testing.T) {
+ m, i := setup()
+
+ msetExpected := []interface{}{"{namespace},key1", "data1"}
+
+ m.On("MSet", msetExpected).Return(nil)
+ err := i.Set("key1", "data1")
+ assert.Nil(t, err)
+ m.AssertExpectations(t)
+}
+
+func TestWriteSeveralKeysSlice(t *testing.T) {
+ m, i := setup()
+
+ msetExpected := []interface{}{"{namespace},key1", "data1", "{namespace},key2", 22}
+
+ m.On("MSet", msetExpected).Return(nil)
+ err := i.Set([]interface{}{"key1", "data1", "key2", 22})
+ assert.Nil(t, err)
+ m.AssertExpectations(t)
+
+}
+
+func TestWriteSeveralKeysArray(t *testing.T) {
+ m, i := setup()
+
+ msetExpected := []interface{}{"{namespace},key1", "data1", "{namespace},key2", "data2"}
+
+ m.On("MSet", msetExpected).Return(nil)
+ err := i.Set([4]string{"key1", "data1", "key2", "data2"})
+ assert.Nil(t, err)
+ m.AssertExpectations(t)
+}
+
+func TestWriteFail(t *testing.T) {
+ m, i := setup()
+
+ msetExpected := []interface{}{"{namespace},key1", "data1"}
+
+ m.On("MSet", msetExpected).Return(errors.New("Some error"))
+ err := i.Set("key1", "data1")
+ assert.NotNil(t, err)
+ m.AssertExpectations(t)
+}
+
+func TestWriteEmptyList(t *testing.T) {
+ m, i := setup()
+
+ msetExpected := []interface{}{}
+ err := i.Set()
+ assert.Nil(t, err)
+ m.AssertNotCalled(t, "MSet", msetExpected)
+}
+
+func TestRemoveSuccessfully(t *testing.T) {
+ m, i := setup()
+
+ msetExpected := []string{"{namespace},key1", "{namespace},key2"}
+ m.On("Del", msetExpected).Return(nil)
+
+ err := i.Remove([]string{"key1", "key2"})
+ assert.Nil(t, err)
+ m.AssertExpectations(t)
+}
+
+func TestRemoveFail(t *testing.T) {
+ m, i := setup()
+
+ msetExpected := []string{"{namespace},key"}
+ m.On("Del", msetExpected).Return(errors.New("Some error"))
+
+ err := i.Remove([]string{"key"})
+ assert.NotNil(t, err)
+ m.AssertExpectations(t)
+}
+
+func TestRemoveEmptyList(t *testing.T) {
+ m, i := setup()
+
+ err := i.Remove([]string{})
+ assert.Nil(t, err)
+ m.AssertNotCalled(t, "Del", []string{})
+}
+
+func TestGetAllSuccessfully(t *testing.T) {
+ m, i := setup()
+
+ mKeysExpected := string("{namespace},*")
+ mReturnExpected := []string{"{namespace},key1", "{namespace},key2"}
+ expectedReturn := []string{"key1", "key2"}
+ m.On("Keys", mKeysExpected).Return(mReturnExpected, nil)
+ retVal, err := i.GetAll()
+ assert.Nil(t, err)
+ assert.Equal(t, expectedReturn, retVal)
+ m.AssertExpectations(t)
+}
+
+func TestGetAllFail(t *testing.T) {
+ m, i := setup()
+
+ mKeysExpected := string("{namespace},*")
+ mReturnExpected := []string{}
+ m.On("Keys", mKeysExpected).Return(mReturnExpected, errors.New("some error"))
+ retVal, err := i.GetAll()
+ assert.NotNil(t, err)
+ assert.Nil(t, retVal)
+ assert.Equal(t, len(retVal), 0)
+ m.AssertExpectations(t)
+}
+
+func TestGetAllReturnEmpty(t *testing.T) {
+ m, i := setup()
+
+ mKeysExpected := string("{namespace},*")
+ var mReturnExpected []string = nil
+ m.On("Keys", mKeysExpected).Return(mReturnExpected, nil)
+ retVal, err := i.GetAll()
+ assert.Nil(t, err)
+ assert.Nil(t, retVal)
+ assert.Equal(t, len(retVal), 0)
+ m.AssertExpectations(t)
+
+}
+
+func TestRemoveAllSuccessfully(t *testing.T) {
+ m, i := setup()
+
+ mKeysExpected := string("{namespace},*")
+ mKeysReturn := []string{"{namespace},key1", "{namespace},key2"}
+ mDelExpected := mKeysReturn
+ m.On("Keys", mKeysExpected).Return(mKeysReturn, nil)
+ m.On("Del", mDelExpected).Return(nil)
+ err := i.RemoveAll()
+ assert.Nil(t, err)
+ m.AssertExpectations(t)
+}
+
+func TestRemoveAllNoKeysFound(t *testing.T) {
+ m, i := setup()
+
+ mKeysExpected := string("{namespace},*")
+ var mKeysReturn []string = nil
+ m.On("Keys", mKeysExpected).Return(mKeysReturn, nil)
+ m.AssertNumberOfCalls(t, "Del", 0)
+ err := i.RemoveAll()
+ assert.Nil(t, err)
+ m.AssertExpectations(t)
+}
+
+func TestRemoveAllKeysReturnError(t *testing.T) {
+ m, i := setup()
+
+ mKeysExpected := string("{namespace},*")
+ var mKeysReturn []string = nil
+ m.On("Keys", mKeysExpected).Return(mKeysReturn, errors.New("Some error"))
+ m.AssertNumberOfCalls(t, "Del", 0)
+ err := i.RemoveAll()
+ assert.NotNil(t, err)
+ m.AssertExpectations(t)
+}
+
+func TestRemoveAllDelReturnError(t *testing.T) {
+ m, i := setup()
+
+ mKeysExpected := string("{namespace},*")
+ mKeysReturn := []string{"{namespace},key1", "{namespace},key2"}
+ mDelExpected := mKeysReturn
+ m.On("Keys", mKeysExpected).Return(mKeysReturn, nil)
+ m.On("Del", mDelExpected).Return(errors.New("Some Error"))
+ err := i.RemoveAll()
+ assert.NotNil(t, err)
+ m.AssertExpectations(t)
+}
if [ "x$1" != "x" ]; then
urlpath="$urlpath/$1"
if rest DELETE $urlpath; then
- # Currently xapp_manager returns an empty result if
+ # Currently appmgr returns an empty result if
# undeploy is succesfull. Don't reformat file if empty.
if [ -s $resultfile ]; then
json_reformat < $resultfile
fi
if [ $status = 0 ]; then
if rest DELETE $urlpath; then
- # Currently xapp_manager returns an empty result if
+ # Currently appmgr returns an empty result if
# delete is succesfull. Don't reformat file if empty.
if [ -s $resultfile ]; then
json_reformat < $resultfile
+++ /dev/null
-/*
-==================================================================================
- 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"
- "github.com/gorilla/mux"
- "github.com/spf13/viper"
- "log"
- "net/http"
-)
-
-// API functions
-
-func (m *XappManager) Initialize(h Helmer) *mux.Router {
- m.sd = SubscriptionDispatcher{}
- m.sd.Initialize()
- m.helm = h
- m.router = mux.NewRouter().StrictSlash(true)
-
- resources := []Resource{
- Resource{"GET", "/ric/v1/health", m.getHealthStatus},
-
- Resource{"GET", "/ric/v1/xapps", m.getAllXapps},
- Resource{"GET", "/ric/v1/xapps/{name}", m.getXappByName},
- Resource{"GET", "/ric/v1/xapps/{name}/instances/{id}", m.getXappInstanceByName},
- Resource{"POST", "/ric/v1/xapps", m.deployXapp},
- Resource{"DELETE", "/ric/v1/xapps/{name}", m.undeployXapp},
-
- Resource{"GET", "/ric/v1/subscriptions", m.getSubscriptions},
- Resource{"POST", "/ric/v1/subscriptions", m.addSubscription},
- Resource{"GET", "/ric/v1/subscriptions/{id}", m.getSubscription},
- Resource{"DELETE", "/ric/v1/subscriptions/{id}", m.deleteSubscription},
- Resource{"PUT", "/ric/v1/subscriptions/{id}", m.updateSubscription},
- }
-
- for _, resource := range resources {
- handler := Logger(resource.HandlerFunc)
- m.router.Methods(resource.Method).Path(resource.Url).Handler(handler)
- }
-
- return m.router
-}
-
-// Health monitoring
-func (m *XappManager) getHealthStatus(w http.ResponseWriter, r *http.Request) {
- respondWithJSON(w, http.StatusOK, nil)
-}
-
-// API: XAPP handlers
-func (m *XappManager) Run() {
- host := viper.GetString("local.host")
- if host == "" {
- host = ":8080"
- }
- log.Printf("Xapp manager started ... serving on %s\n", host)
-
- log.Fatal(http.ListenAndServe(host, m.router))
-}
-
-func (m *XappManager) getXappByName(w http.ResponseWriter, r *http.Request) {
- xappName, ok := getResourceId(r, w, "name")
- if ok != true {
- return
- }
-
- if xapp, err := m.helm.Status(xappName); err == nil {
- respondWithJSON(w, http.StatusOK, xapp)
- } else {
- respondWithError(w, http.StatusNotFound, err.Error())
- }
-}
-
-func (m *XappManager) getXappInstanceByName(w http.ResponseWriter, r *http.Request) {
- xappName, ok := getResourceId(r, w, "name")
- if ok != true {
- return
- }
-
- xapp, err := m.helm.Status(xappName)
- if err != nil {
- respondWithError(w, http.StatusNotFound, err.Error())
- return
- }
-
- xappInstanceName, ok := getResourceId(r, w, "id")
- if ok != true {
- return
- }
-
- for _, v := range xapp.Instances {
- if v.Name == xappInstanceName {
- respondWithJSON(w, http.StatusOK, v)
- return
- }
- }
- mdclog(Mdclog_err, "Xapp instance not found - url=" + r.URL.RequestURI())
-
- respondWithError(w, http.StatusNotFound, "Xapp instance not found")
-}
-
-func (m *XappManager) getAllXapps(w http.ResponseWriter, r *http.Request) {
- xapps, err := m.helm.StatusAll()
- if err != nil {
- respondWithError(w, http.StatusInternalServerError, err.Error())
- return
- }
-
- respondWithJSON(w, http.StatusOK, xapps)
-}
-
-func (m *XappManager) deployXapp(w http.ResponseWriter, r *http.Request) {
- if r.Body == nil {
- mdclog(Mdclog_err, "No xapp data found in request body - url=" + r.URL.RequestURI())
- respondWithError(w, http.StatusMethodNotAllowed, "No xapp data!")
- return
- }
-
- var xapp Xapp
- if err := json.NewDecoder(r.Body).Decode(&xapp); err != nil {
- mdclog(Mdclog_err, "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)
- if err != nil {
- respondWithError(w, http.StatusInternalServerError, err.Error())
- return
- }
-
- respondWithJSON(w, http.StatusCreated, xapp)
-
- m.sd.Publish(xapp, EventType("created"))
-}
-
-func (m *XappManager) undeployXapp(w http.ResponseWriter, r *http.Request) {
- xappName, ok := getResourceId(r, w, "name")
- if ok != true {
- return
- }
-
- xapp, err := m.helm.Delete(xappName)
- if err != nil {
- respondWithError(w, http.StatusInternalServerError, err.Error())
- return
- }
-
- respondWithJSON(w, http.StatusNoContent, nil)
-
- m.sd.Publish(xapp, EventType("deleted"))
-}
-
-// API: resthook handlers
-func (m *XappManager) getSubscriptions(w http.ResponseWriter, r *http.Request) {
- respondWithJSON(w, http.StatusOK, m.sd.GetAll())
-}
-
-func (m *XappManager) getSubscription(w http.ResponseWriter, r *http.Request) {
- if id, ok := getResourceId(r, w, "id"); ok == true {
- if s, ok := m.sd.Get(id); ok {
- respondWithJSON(w, http.StatusOK, s)
- } else {
- mdclog(Mdclog_err, "Subscription not found - url=" + r.URL.RequestURI())
- respondWithError(w, http.StatusNotFound, "Subscription not found")
- }
- }
-}
-
-func (m *XappManager) deleteSubscription(w http.ResponseWriter, r *http.Request) {
- if id, ok := getResourceId(r, w, "id"); ok == true {
- if _, ok := m.sd.Delete(id); ok {
- respondWithJSON(w, http.StatusNoContent, nil)
- } else {
- mdclog(Mdclog_err, "Subscription not found - url=" + r.URL.RequestURI())
- respondWithError(w, http.StatusNotFound, "Subscription not found")
- }
- }
-}
-
-func (m *XappManager) addSubscription(w http.ResponseWriter, r *http.Request) {
- var req SubscriptionReq
- if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
- mdclog(Mdclog_err, "Invalid request payload - url=" + r.URL.RequestURI())
- respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
- return
- }
- defer r.Body.Close()
-
- respondWithJSON(w, http.StatusCreated, m.sd.Add(req))
-}
-
-func (m *XappManager) updateSubscription(w http.ResponseWriter, r *http.Request) {
- if id, ok := getResourceId(r, w, "id"); ok == true {
- var req SubscriptionReq
- if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
- mdclog(Mdclog_err, "Invalid request payload - url=" + r.URL.RequestURI())
- respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
- return
- }
- defer r.Body.Close()
-
- if s, ok := m.sd.Update(id, req); ok {
- respondWithJSON(w, http.StatusOK, s)
- } else {
- mdclog(Mdclog_err, "Subscription not found - url=" + r.URL.RequestURI())
- respondWithError(w, http.StatusNotFound, "Subscription not found")
- }
- }
-}
-
-// Helper functions
-func respondWithError(w http.ResponseWriter, code int, message string) {
- respondWithJSON(w, code, map[string]string{"error": message})
-}
-
-func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(code)
- if payload != nil {
- response, _ := json.Marshal(payload)
- w.Write(response)
- }
-}
-
-func getResourceId(r *http.Request, w http.ResponseWriter, pattern string) (id string, ok bool) {
- if id, ok = mux.Vars(r)[pattern]; ok != true {
- mdclog(Mdclog_err, "Couldn't resolve name/id from the request URL")
- respondWithError(w, http.StatusMethodNotAllowed, "Couldn't resolve name/id from the request URL")
- return
- }
- return
-}
+++ /dev/null
-/*
-==================================================================================
- 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 (
- "net/http"
- "net/http/httptest"
- "testing"
- "reflect"
- "strconv"
- "os"
- "bytes"
- "errors"
- "encoding/json"
- "github.com/gorilla/mux"
-)
-
-var x XappManager
-var xapp Xapp
-var xapps []Xapp
-var helmError error
-
-type MockedHelmer struct {
-}
-
-func (h *MockedHelmer) Status(name string) (Xapp, error) {
- return xapp, helmError
-}
-
-func (h *MockedHelmer) StatusAll() ([]Xapp, error) {
- return xapps, helmError
-}
-
-func (h *MockedHelmer) List() (names []string, err error) {
- return names, helmError
-}
-
-func (h *MockedHelmer) Install(name string) (Xapp, error) {
- return xapp, helmError
-}
-
-func (h *MockedHelmer) Delete(name string) (Xapp, error) {
- return xapp, helmError
-}
-
-// Test cases
-func TestMain(m *testing.M) {
- loadConfig();
-
- xapp = Xapp{}
- xapps = []Xapp{}
-
- h := MockedHelmer{}
- x = XappManager{}
- x.Initialize(&h)
-
- // Just run on the background (for coverage)
- go x.Run()
-
- code := m.Run()
- os.Exit(code)
-}
-
-func TestGetHealthCheck(t *testing.T) {
- req, _ := http.NewRequest("GET", "/ric/v1/health", nil)
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusOK, response.Code)
-}
-
-func TestGetAppsReturnsEmpty(t *testing.T) {
- req, _ := http.NewRequest("GET", "/ric/v1/xapps", nil)
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusOK, response.Code)
- if body := response.Body.String(); body != "[]" {
- t.Errorf("handler returned unexpected body: got %v want []", body)
- }
-}
-
-func TestCreateXApp(t *testing.T) {
- xapp = generateXapp("dummy-xapp", "started", "1.0", "dummy-xapp-1234-5678", "running", "127.0.0.1", "9999")
-
- payload := []byte(`{"name":"dummy-xapp"}`)
- req, _ := http.NewRequest("POST", "/ric/v1/xapps", bytes.NewBuffer(payload))
- response := executeRequest(req)
-
- checkResponseData(t, response, http.StatusCreated, false)
-}
-
-func TestGetAppsReturnsListOfXapps(t *testing.T) {
- xapps = append(xapps, xapp)
- req, _ := http.NewRequest("GET", "/ric/v1/xapps", nil)
- response := executeRequest(req)
-
- checkResponseData(t, response, http.StatusOK, true)
-}
-
-func TestGetAppByIdReturnsGivenXapp(t *testing.T) {
- req, _ := http.NewRequest("GET", "/ric/v1/xapps/" + xapp.Name, nil)
- response := executeRequest(req)
-
- checkResponseData(t, response, http.StatusOK, false)
-}
-
-func TestGetAppInstanceByIdReturnsGivenXapp(t *testing.T) {
- req, _ := http.NewRequest("GET", "/ric/v1/xapps/" + xapp.Name + "/instances/dummy-xapp-1234-5678", nil)
- response := executeRequest(req)
-
- var ins XappInstance
- checkResponseCode(t, http.StatusOK, response.Code)
- json.NewDecoder(response.Body).Decode(&ins)
-
- if !reflect.DeepEqual(ins, xapp.Instances[0]) {
- t.Errorf("handler returned unexpected body: got: %v, expected: %v", ins, xapp.Instances[0])
- }
-}
-
-func TestDeleteAppRemovesGivenXapp(t *testing.T) {
- req, _ := http.NewRequest("DELETE", "/ric/v1/xapps/" + xapp.Name, nil)
- response := executeRequest(req)
-
- checkResponseData(t, response, http.StatusNoContent, false)
-
- // Xapp not found from the Redis DB
- helmError = errors.New("Not found")
-
- req, _ = http.NewRequest("GET", "/ric/v1/xapps/" + xapp.Name, nil)
- response = executeRequest(req)
- checkResponseCode(t, http.StatusNotFound, response.Code)
-}
-
-// Error handling
-func TestGetXappReturnsError(t *testing.T) {
- helmError = errors.New("Not found")
-
- req, _ := http.NewRequest("GET", "/ric/v1/xapps/invalidXappName", nil)
- response := executeRequest(req)
- checkResponseCode(t, http.StatusNotFound, response.Code)
-}
-
-func TestGetXappInstanceReturnsError(t *testing.T) {
- helmError = errors.New("Some error")
-
- req, _ := http.NewRequest("GET", "/ric/v1/xapps/" + xapp.Name + "/instances/invalidXappName", nil)
- response := executeRequest(req)
- checkResponseCode(t, http.StatusNotFound, response.Code)
-}
-
-func TestGetXappListReturnsError(t *testing.T) {
- helmError = errors.New("Internal error")
-
- req, _ := http.NewRequest("GET", "/ric/v1/xapps", nil)
- response := executeRequest(req)
- checkResponseCode(t, http.StatusInternalServerError, response.Code)
-}
-
-func TestCreateXAppWithoutXappData(t *testing.T) {
- req, _ := http.NewRequest("POST", "/ric/v1/xapps", nil)
- response := executeRequest(req)
- checkResponseData(t, response, http.StatusMethodNotAllowed, false)
-}
-
-func TestCreateXAppWithInvalidXappData(t *testing.T) {
- body := []byte("Invalid JSON data ...")
-
- req, _ := http.NewRequest("POST", "/ric/v1/xapps", bytes.NewBuffer(body))
- response := executeRequest(req)
- checkResponseData(t, response, http.StatusMethodNotAllowed, false)
-}
-
-func TestCreateXAppReturnsError(t *testing.T) {
- helmError = errors.New("Not found")
-
- payload := []byte(`{"name":"dummy-xapp"}`)
- req, _ := http.NewRequest("POST", "/ric/v1/xapps", bytes.NewBuffer(payload))
- response := executeRequest(req)
-
- checkResponseData(t, response, http.StatusInternalServerError, false)
-}
-
-func TestDeleteXappListReturnsError(t *testing.T) {
- helmError = errors.New("Internal error")
-
- req, _ := http.NewRequest("DELETE", "/ric/v1/xapps/invalidXappName", nil)
- response := executeRequest(req)
- checkResponseCode(t, http.StatusInternalServerError, response.Code)
-}
-
-// Helper functions
-type fn func(w http.ResponseWriter, r *http.Request)
-
-func executeRequest(req *http.Request) *httptest.ResponseRecorder {
- rr := httptest.NewRecorder()
-
- vars := map[string]string{
- "id": "1",
- }
- req = mux.SetURLVars(req, vars)
-
- x.router.ServeHTTP(rr, req)
-
- return rr
-}
-
-func checkResponseCode(t *testing.T, expected, actual int) {
- if expected != actual {
- t.Errorf("Expected response code %d. Got %d\n", expected, actual)
- }
-}
-
-func checkResponseData(t *testing.T, response *httptest.ResponseRecorder, expectedHttpStatus int, isList bool) {
- expectedData := xapp
-
- checkResponseCode(t, expectedHttpStatus, response.Code)
- if isList == true {
- jsonResp := []Xapp{}
- json.NewDecoder(response.Body).Decode(&jsonResp)
-
- if !reflect.DeepEqual(jsonResp[0], expectedData) {
- t.Errorf("handler returned unexpected body: %v", jsonResp)
- }
- } else {
- json.NewDecoder(response.Body).Decode(&xapp)
-
- if !reflect.DeepEqual(xapp, expectedData) {
- t.Errorf("handler returned unexpected body: got: %v, expected: %v", xapp, expectedData)
- }
- }
-}
-
-func generateXapp(name, status, ver, iname, istatus, ip, port string) (x Xapp) {
- x.Name = name
- x.Status = status
- x.Version = ver
- p , _ := strconv.Atoi(port)
- instance := XappInstance{
- Name: iname,
- Status: istatus,
- Ip: ip,
- Port: p,
- TxMessages: []string{"RIC_E2_TERMINATION_HC_REQUEST", "RIC_E2_MANAGER_HC_REQUEST"},
- RxMessages: []string{"RIC_E2_TERMINATION_HC_RESPONSE", "RIC_E2_MANAGER_HC_RESPONSE"},
- }
- x.Instances = append(x.Instances, instance)
-
- return
-}
+++ /dev/null
-/*
-==================================================================================
- 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 (
- "fmt"
- "log"
- "os"
- "os/exec"
- "regexp"
- "strconv"
- "strings"
- "github.com/spf13/viper"
- "gopkg.in/yaml.v2"
- "io/ioutil"
- "path"
-)
-
-var execCommand = exec.Command
-
-func Exec(args string) (out []byte, err error) {
- cmd := execCommand("/bin/sh", "-c", strings.Join([]string{"helm", args}, " "))
-
- // In testing environment, don't print command traces ...
- if !strings.HasSuffix(os.Args[0], ".test") {
- log.Printf("Running command: %v", cmd)
- }
-
- out, err = cmd.CombinedOutput()
- if err != nil {
- mdclog(Mdclog_err, formatLog("Command failed", args, err.Error()))
- return out, err
- }
-
- if !strings.HasSuffix(os.Args[0], ".test") {
- mdclog(Mdclog_debug, formatLog("command success", string(out), ""))
- }
-
- return out, err
-}
-
-func (h *Helm) Run(args string) (out []byte, err error) {
- if h.initDone == false {
- if _, err := h.Init(); err != nil {
- mdclog(Mdclog_err, formatLog("helm init failed", args, err.Error()))
- return out, err
- }
- mdclog(Mdclog_debug, formatLog("Helm init done successfully!", args, ""))
-
- // Add helm repo
- if _, err := h.AddRepo(); err != nil {
- mdclog(Mdclog_err, formatLog("Helm repo addition failed", args, err.Error()))
- return out, err
- }
-
- mdclog(Mdclog_debug, formatLog("Helm repo added successfully", string(out), ""))
- h.initDone = true
- }
-
- return Exec(args)
-}
-
-// API functions
-func (h *Helm) Init() (out []byte, err error) {
-
- // Add Tiller address as environment variable
- if err := addTillerEnv(); err != nil {
- return out, err
- }
-
- return Exec(strings.Join([]string{"init -c"}, ""))
-}
-
-func (h *Helm) AddRepo() (out []byte, err error) {
-
- // Get helm repo user name and password from files mounted by secret object
- credFile, err := ioutil.ReadFile(viper.GetString("helm.helm-username-file"))
- if err != nil {
- mdclog(Mdclog_err, formatLog("helm_repo_username ReadFile failed", "", err.Error()))
- return
- }
-
- username := " --username " + string(credFile)
-
- credFile, err = ioutil.ReadFile(viper.GetString("helm.helm-password-file"))
- if err != nil {
- mdclog(Mdclog_err, formatLog("helm_repo_password ReadFile failed", "", err.Error()))
- return
- }
-
- pwd := " --password " + string(credFile)
-
- // Get internal helm repo name
- rname := viper.GetString("helm.repo-name")
-
- // Get helm repo address from values.yaml
- repo := viper.GetString("helm.repo")
-
- return Exec(strings.Join([]string{"repo add ", rname, " ", repo, username, pwd}, ""))
-}
-
-func (h *Helm) Install(name string) (xapp Xapp, err error) {
- out, err := h.Run(strings.Join([]string{"repo update "}, ""))
- if err != nil {
- return
- }
-
- rname := viper.GetString("helm.repo-name")
-
- ns := getNamespaceArgs()
- out, err = h.Run(strings.Join([]string{"install ", rname, "/", name, " --name ", name, ns}, ""))
- if err != nil {
- return
- }
-
- return h.ParseStatus(name, string(out))
-}
-
-func (h *Helm) Status(name string) (xapp Xapp, err error) {
-
- out, err := h.Run(strings.Join([]string{"status ", name}, ""))
- if err != nil {
- mdclog(Mdclog_err, formatLog("Getting xapps status", "", err.Error()))
- return
- }
-
- return h.ParseStatus(name, string(out))
-}
-
-func (h *Helm) StatusAll() (xapps []Xapp, err error) {
- xappNameList, err := h.List()
- if err != nil {
- mdclog(Mdclog_err, formatLog("Helm list failed", "", err.Error()))
- return
- }
-
- return h.parseAllStatus(xappNameList)
-}
-
-func (h *Helm) List() (names []string, err error) {
-
- ns := getNamespaceArgs()
- out, err := h.Run(strings.Join([]string{"list --all --output yaml ", ns}, ""))
- if err != nil {
- mdclog(Mdclog_err, formatLog("Listing deployed xapps failed", "", err.Error()))
- return
- }
-
- return h.GetNames(string(out))
-}
-
-func (h *Helm) Delete(name string) (xapp Xapp, err error) {
- xapp, err = h.Status(name)
- if err != nil {
- mdclog(Mdclog_err, formatLog("Fetching xapp status failed", "", err.Error()))
- return
- }
-
- _, err = h.Run(strings.Join([]string{"del --purge ", name}, ""))
- return xapp, err
-}
-
-func (h *Helm) Fetch(name , tarDir string) (error) {
- if strings.HasSuffix(os.Args[0], ".test") {
- return nil
- }
-
- rname := viper.GetString("helm.repo-name") + "/"
-
- _, err := h.Run(strings.Join([]string{"fetch --untar --untardir ", tarDir, " ", rname, name}, ""))
- return err
-}
-
-// 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(Mdclog_err, formatLog("Fetch chart failed", "", err.Error()))
- return
- }
-
- return h.ParseMessages(name, tarDir, "msg_type.yaml")
-
-}
-
-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(Mdclog_err, formatLog("ReadFile failed", "", err.Error()))
- return
- }
-
- err = yaml.Unmarshal(yamlFile, &msgs)
- if err != nil {
- mdclog(Mdclog_err, formatLog("Unmarshal failed", "", err.Error()))
- return
- }
-
- if err = os.RemoveAll(path.Join(chartDir, name)); err != nil {
- mdclog(Mdclog_err, 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}, ""))
- if err != nil {
- return
- }
-
- var re = regexp.MustCompile(`AppVersion: .*`)
- ver := re.FindStringSubmatch(string(out))
- if ver != nil {
- version = strings.Split(ver[0], ": ")[1]
- version, _ = strconv.Unquote(version)
- }
-
- return
-}
-
-func (h *Helm) GetState(out string) (status string) {
- re := regexp.MustCompile(`STATUS: .*`)
- result := re.FindStringSubmatch(string(out))
- if result != nil {
- status = strings.ToLower(strings.Split(result[0], ": ")[1])
- }
-
- return
-}
-
-func (h *Helm) GetAddress(out string) (ip, port string) {
- var tmp string
- re := regexp.MustCompile(`ClusterIP.*`)
- addr := re.FindStringSubmatch(string(out))
- if addr != nil {
- fmt.Sscanf(addr[0], "%s %s %s %s", &tmp, &ip, &tmp, &port)
- }
-
- return
-}
-
-func (h *Helm) GetNames(out string) (names []string, err error) {
- re := regexp.MustCompile(`Name: .*`)
- result := re.FindAllStringSubmatch(out, -1)
- if result == nil {
- return
- }
-
- for _, v := range result {
- xappName := strings.Split(v[0], ": ")[1]
- if strings.Contains(xappName, "appmgr") == false {
- names = append(names, xappName)
- }
- }
- return names, nil
-}
-
-func (h *Helm) FillInstanceData(name string, out string, xapp *Xapp, msgs MessageTypes) {
- ip, port := h.GetAddress(out)
-
- var tmp string
- r := regexp.MustCompile(`(?s)\/Pod.*?\/Service`)
- result := r.FindStringSubmatch(string(out))
- if result == nil {
- return
- }
-
- re := regexp.MustCompile(name + "-(\\d+).*")
- resources := re.FindAllStringSubmatch(string(result[0]), -1)
- if resources != nil {
- for _, v := range resources {
- var x XappInstance
- fmt.Sscanf(v[0], "%s %s %s", &x.Name, &tmp, &x.Status)
- x.Status = strings.ToLower(x.Status)
- x.Ip = ip
- x.Port, _ = strconv.Atoi(strings.Split(port, "/")[0])
- x.TxMessages = msgs.TxMessages
- x.RxMessages = msgs.RxMessages
- xapp.Instances = append(xapp.Instances, x)
- }
- }
-}
-
-func (h *Helm) ParseStatus(name string, out string) (xapp Xapp, err error) {
- types, err := h.GetMessages(name)
- if (err != nil) {
- return
- }
-
- xapp.Name = name
- xapp.Version = h.GetVersion(name)
- xapp.Status = h.GetState(out)
- h.FillInstanceData(name, out, &xapp, types)
-
- return
-}
-
-func (h *Helm) parseAllStatus(names []string) (xapps []Xapp, err error) {
- xapps = []Xapp{}
-
- for _, name := range names {
- x, err := h.Status(name)
- if err == nil {
- xapps = append(xapps, x)
- }
- }
-
- return
-}
-
-func addTillerEnv() (err error) {
-
- service := viper.GetString("helm.tiller-service")
- namespace := viper.GetString("helm.tiller-namespace")
- port := viper.GetString("helm.tiller-port")
-
- if err = os.Setenv("HELM_HOST", service + "." + namespace + ":" + port); err != nil {
- mdclog(Mdclog_err, formatLog("Tiller Env Setting Failed", "", err.Error()))
- }
-
- return err
-}
-
-func getNamespaceArgs() (string) {
- ns := viper.GetString("xapp.namespace")
- if ns == "" {
- ns = "ricxapp"
- }
- return " --namespace=" + ns
-}
-
-func formatLog(text string, args string, err string) (string) {
- return fmt.Sprintf("Helm: %s: args=%s err=%s\n", text, args, err)
-}
-
+++ /dev/null
-/*
-==================================================================================
- 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 (
- "fmt"
- "os"
- "os/exec"
- "strconv"
- "testing"
- "reflect"
- "github.com/spf13/viper"
- "io/ioutil"
- "path"
-)
-
-var helmStatusOutput = `
-LAST DEPLOYED: Sat Mar 9 06:50:45 2019
-NAMESPACE: default
-STATUS: DEPLOYED
-
-RESOURCES:
-==> v1/Pod(related)
-NAME READY STATUS RESTARTS AGE
-dummy-xapp-8984fc9fd-bkcbp 1/1 Running 0 55m
-dummy-xapp-8984fc9fd-l6xch 1/1 Running 0 55m
-dummy-xapp-8984fc9fd-pp4hg 1/1 Running 0 55m
-
-==> v1/Service
-NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
-dummy-xapp-dummy-xapp-chart ClusterIP 10.102.184.212 <none> 80/TCP 55m
-
-==> v1beta1/Deployment
-NAME READY UP-TO-DATE AVAILABLE AGE
-dummy-xapp 3/3 3 3 55m
-`
-
-var helListOutput = `Next: ""
-Releases:
-- AppVersion: "1.0"
- Chart: dummy-xapp-chart-0.1.0
- Name: dummy-xapp
- Namespace: default
- Revision: 1
- Status: DEPLOYED
- Updated: Mon Mar 11 06:55:05 2019
-- AppVersion: "2.0"
- Chart: dummy-xapp-chart-0.1.0
- Name: dummy-xapp2
- Namespace: default
- Revision: 1
- Status: DEPLOYED
- Updated: Mon Mar 11 06:55:05 2019
-- AppVersion: "1.0"
- Chart: appmgr-0.0.1
- Name: appmgr
- Namespace: default
- Revision: 1
- Status: DEPLOYED
- Updated: Sun Mar 24 07:17:00 2019`
-
-var mockedExitStatus = 0
-var mockedStdout string
-var h = Helm{}
-
-func fakeExecCommand(command string, args ...string) *exec.Cmd {
- cs := []string{"-test.run=TestExecCommandHelper", "--", command}
- cs = append(cs, args...)
-
- cmd := exec.Command(os.Args[0], cs...)
- es := strconv.Itoa(mockedExitStatus)
- cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1", "STDOUT=" + mockedStdout, "EXIT_STATUS=" + es}
-
- return cmd
-}
-
-func TestExecCommandHelper(t *testing.T) {
- if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
- return
- }
-
- fmt.Fprintf(os.Stdout, os.Getenv("STDOUT"))
- i, _ := strconv.Atoi(os.Getenv("EXIT_STATUS"))
- os.Exit(i)
-}
-
-func writeTestCreds() (err error) {
-
- // Write test entries to helm username and password files
- f, err := os.Create(viper.GetString("helm.helm-username-file"))
- if err != nil {
- return err
- }
-
- _, err = f.WriteString(viper.GetString("helm.secrets.username"))
- if err != nil {
- f.Close()
- return (err)
- }
- f.Close()
-
- f, err = os.Create(viper.GetString("helm.helm-password-file"))
- if err != nil {
- return err
- }
-
- _, err = f.WriteString(viper.GetString("helm.secrets.password"))
- if err != nil {
- f.Close()
- return (err)
- }
- f.Close()
- return
-}
-
-func TestHelmInit(t *testing.T) {
- mockedExitStatus = 0
- execCommand = fakeExecCommand
- defer func() { execCommand = exec.Command }()
-
- if err := writeTestCreds(); err != nil {
- t.Errorf("Writing test entries failed: %s", err)
- return
- }
-
- out, err := h.Init()
- if err != nil {
- t.Errorf("Helm init failed: %s %s", err, string(out))
- }
-}
-
-func TestHelmInstall(t *testing.T) {
- copyFile(t)
- mockedExitStatus = 0
- execCommand = fakeExecCommand
- mockedStdout = helmStatusOutput
- defer func() { execCommand = exec.Command }()
-
- xapp, err := h.Install("dummy-xapp")
- if err != nil {
- t.Errorf("Helm install failed: %v", err)
- }
-
- x := getXappData()
- xapp.Version = "1.0"
-
- if !reflect.DeepEqual(xapp, x) {
- t.Errorf("%v \n%v", xapp, x)
- }
-}
-
-func TestHelmStatus(t *testing.T) {
- copyFile(t)
- mockedExitStatus = 0
- mockedStdout = helmStatusOutput
- execCommand = fakeExecCommand
- defer func() { execCommand = exec.Command }()
-
- xapp, err := h.Status("dummy-xapp")
- if err != nil {
- t.Errorf("Helm status failed: %v", err)
- }
-
- x := getXappData()
- xapp.Version = "1.0"
-
- if !reflect.DeepEqual(xapp, x) {
- t.Errorf("%v \n%v", xapp, x)
- }
-}
-
-func TestHelmStatusAll(t *testing.T) {
- copyFile(t)
- mockedExitStatus = 0
- mockedStdout = helListOutput
- execCommand = fakeExecCommand
- defer func() { execCommand = exec.Command }()
-
- xapp, err := h.StatusAll()
- if err != nil {
- t.Errorf("Helm StatusAll failed: %v - %v", err, xapp)
- }
-
- // Todo: check the content
-}
-
-func TestHelmParseAllStatus(t *testing.T) {
- copyFile(t)
- mockedExitStatus = 0
- mockedStdout = helListOutput
- execCommand = fakeExecCommand
- defer func() { execCommand = exec.Command }()
-
- xapp, err := h.parseAllStatus([]string{"dummy-xapp", "dummy-xapp2"})
- if err != nil {
- t.Errorf("Helm parseAllStatus failed: %v - %v", err, xapp)
- }
-
- // Todo: check the content
-}
-
-func TestHelmDelete(t *testing.T) {
- copyFile(t)
- mockedExitStatus = 0
- mockedStdout = helListOutput
- execCommand = fakeExecCommand
- defer func() { execCommand = exec.Command }()
-
- xapp, err := h.Delete("dummy-xapp")
- if err != nil {
- t.Errorf("Helm delete failed: %v - %v", err, xapp)
- }
-
- // Todo: check the content
-}
-
-func TestHelmLists(t *testing.T) {
- mockedExitStatus = 0
- mockedStdout = helListOutput
- execCommand = fakeExecCommand
- defer func() { execCommand = exec.Command }()
-
- names, err := h.List()
- if err != nil {
- t.Errorf("Helm status failed: %v", err)
- }
-
- if !reflect.DeepEqual(names, []string{"dummy-xapp", "dummy-xapp2"}) {
- t.Errorf("Helm status failed: %v", err)
- }
-}
-
-func getXappData() (x Xapp) {
- x = generateXapp("dummy-xapp", "deployed", "1.0", "dummy-xapp-8984fc9fd-bkcbp", "running", "10.102.184.212", "80")
- x.Instances = append(x.Instances, x.Instances[0])
- x.Instances = append(x.Instances, x.Instances[0])
- x.Instances[1].Name = "dummy-xapp-8984fc9fd-l6xch"
- x.Instances[2].Name = "dummy-xapp-8984fc9fd-pp4hg"
-
- return x
-}
-
-
-func copyFile(t *testing.T) {
- tarDir := path.Join(viper.GetString("xapp.tarDir"), "dummy-xapp")
- err := os.MkdirAll(tarDir, 0777)
- if err != nil {
- t.Errorf("%v", err)
- }
-
- data, err := ioutil.ReadFile("../config/msg_type.yaml")
- if err != nil {
- t.Errorf("%v", err)
- }
-
- _ = ioutil.WriteFile(path.Join(tarDir, "msg_type.yaml"), data, 0644)
- if err != nil {
- t.Errorf("%v", err)
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
-==================================================================================
- 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 (
- "bytes"
- "log"
- "net/http"
- "encoding/json"
- "time"
- "github.com/segmentio/ksuid"
- cmap "github.com/orcaman/concurrent-map"
-)
-
-func (sd *SubscriptionDispatcher) Initialize() {
- sd.client = &http.Client{}
- sd.subscriptions = cmap.New()
-}
-
-func (sd *SubscriptionDispatcher) Add(sr SubscriptionReq) (resp SubscriptionResp) {
- key := ksuid.New().String()
- resp = SubscriptionResp{key, 0, sr.EventType}
- sr.Id = key
-
- sd.subscriptions.Set(key, Subscription{sr, resp})
-
- log.Printf("New subscription added: key=%s value=%v", key, sr)
- return
-}
-
-func (sd *SubscriptionDispatcher) GetAll() (hooks []SubscriptionReq) {
- hooks = []SubscriptionReq{}
- for v := range sd.subscriptions.IterBuffered() {
- hooks = append(hooks, v.Val.(Subscription).req)
- }
-
- return hooks
-}
-
-func (sd *SubscriptionDispatcher) Get(id string) (SubscriptionReq, bool) {
- if v, found := sd.subscriptions.Get(id); found {
- log.Printf("Subscription id=%s found: %v", id, v.(Subscription).req)
-
- return v.(Subscription).req, found
- }
- return SubscriptionReq{}, false
-}
-
-func (sd *SubscriptionDispatcher) Delete(id string) (SubscriptionReq, bool) {
- if v, found := sd.subscriptions.Get(id); found {
- log.Printf("Subscription id=%s found: %v ... deleting", id, v.(Subscription).req)
-
- sd.subscriptions.Remove(id)
- return v.(Subscription).req, found
- }
- return SubscriptionReq{}, false
-}
-
-func (sd *SubscriptionDispatcher) Update(id string, sr SubscriptionReq) (SubscriptionReq, bool) {
- if s, found := sd.subscriptions.Get(id); found {
- log.Printf("Subscription id=%s found: %v ... updating", id, s.(Subscription).req)
-
- sr.Id = id
- sd.subscriptions.Set(id, Subscription{sr, s.(Subscription).resp});
- return sr, found
- }
- return SubscriptionReq{}, false
-}
-
-func (sd *SubscriptionDispatcher) Publish(x Xapp, et EventType) {
- for v := range sd.subscriptions.Iter() {
- go sd.notify(x, et, v.Val.(Subscription))
- }
-}
-
-func (sd *SubscriptionDispatcher) notify(x Xapp, et EventType, s Subscription) error {
- notif := []SubscriptionNotif{}
- notif = append(notif, SubscriptionNotif{Id: s.req.Id, Version: s.resp.Version, EventType: string(et), XappData: x})
-
- jsonData, err := json.Marshal(notif)
- if err != nil {
- log.Panic(err)
- }
-
- // Execute the request with retry policy
- return sd.retry(s, func() error {
- resp, err := http.Post(s.req.TargetUrl, "application/json", bytes.NewBuffer(jsonData))
- if err != nil {
- log.Printf("Posting to subscription failed: %v", err)
- return err
- }
-
- if resp.StatusCode != http.StatusOK {
- log.Printf("Client returned error code: %d", resp.StatusCode)
- return err
- }
-
- log.Printf("subscription to '%s' dispatched, response code: %d \n", s.req.TargetUrl, resp.StatusCode)
- return nil
- })
-}
-
-func (sd *SubscriptionDispatcher) retry(s Subscription, fn func() error) error {
- if err := fn(); err != nil {
- // Todo: use exponential backoff, or similar mechanism
- if s.req.MaxRetries--; s.req.MaxRetries > 0 {
- time.Sleep(time.Duration(s.req.RetryTimer) * time.Second)
- return sd.retry(s, fn)
- }
- sd.subscriptions.Remove(s.req.Id);
- return err
- }
- return nil
-}
+++ /dev/null
-/*
-==================================================================================
- 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 (
- "net/http"
- "testing"
- "bytes"
- "encoding/json"
- "net/http/httptest"
- "net"
- "log"
- "fmt"
-)
-
-var resp SubscriptionResp
-
-// Test cases
-func TestNoSubscriptionsFound(t *testing.T) {
- req, _ := http.NewRequest("GET", "/ric/v1/subscriptions", nil)
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusOK, response.Code)
- if body := response.Body.String(); body != "[]" {
- t.Errorf("handler returned unexpected body: got %v want []", body)
- }
-}
-
-func TestAddNewSubscription(t *testing.T) {
- payload := []byte(`{"maxRetries": 3, "retryTimer": 5, "eventType":"Created", "targetUrl": "http://localhost:8087/xapps_handler"}`)
- req, _ := http.NewRequest("POST", "/ric/v1/subscriptions", bytes.NewBuffer(payload))
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusCreated, response.Code)
-
- json.NewDecoder(response.Body).Decode(&resp)
- if resp.Version != 0 {
- t.Errorf("Creating new subscription failed: %v", resp)
- }
-}
-
-func TestGettAllSubscriptions(t *testing.T) {
- req, _ := http.NewRequest("GET", "/ric/v1/subscriptions", nil)
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusOK, response.Code)
-
- var subscriptions []SubscriptionReq
- json.NewDecoder(response.Body).Decode(&subscriptions)
-
- verifySubscription(t, subscriptions[0], "http://localhost:8087/xapps_handler", 3, 5, "Created")
-}
-
-func TestGetSingleSubscription(t *testing.T) {
- req, _ := http.NewRequest("GET", "/ric/v1/subscriptions/" + resp.Id, nil)
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusOK, response.Code)
-
- var subscription SubscriptionReq
- json.NewDecoder(response.Body).Decode(&subscription)
-
- verifySubscription(t, subscription, "http://localhost:8087/xapps_handler", 3, 5, "Created")
-}
-
-func TestUpdateSingleSubscription(t *testing.T) {
- payload := []byte(`{"maxRetries": 11, "retryTimer": 22, "eventType":"Deleted", "targetUrl": "http://localhost:8088/xapps_handler"}`)
-
- req, _ := http.NewRequest("PUT", "/ric/v1/subscriptions/" + resp.Id, bytes.NewBuffer(payload))
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusOK, response.Code)
-
- var res SubscriptionResp
- json.NewDecoder(response.Body).Decode(&res)
- if res.Version != 0 {
- t.Errorf("handler returned unexpected data: %v", resp)
- }
-
- // Check that the subscription is updated properly
- req, _ = http.NewRequest("GET", "/ric/v1/subscriptions/" + resp.Id, nil)
- response = executeRequest(req)
- checkResponseCode(t, http.StatusOK, response.Code)
-
- var subscription SubscriptionReq
- json.NewDecoder(response.Body).Decode(&subscription)
-
- verifySubscription(t, subscription, "http://localhost:8088/xapps_handler", 11, 22, "Deleted")
-}
-
-func TestDeleteSingleSubscription(t *testing.T) {
- req, _ := http.NewRequest("DELETE", "/ric/v1/subscriptions/" + resp.Id, nil)
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusNoContent, response.Code)
-
- // Check that the subscription is removed properly
- req, _ = http.NewRequest("GET", "/ric/v1/subscriptions/" + resp.Id, nil)
- response = executeRequest(req)
- checkResponseCode(t, http.StatusNotFound, response.Code)
-}
-
-func TestDeleteSingleSubscriptionFails(t *testing.T) {
- req, _ := http.NewRequest("DELETE", "/ric/v1/subscriptions/invalidSubscriptionId" , nil)
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusNotFound, response.Code)
-}
-
-func TestAddSingleSubscriptionFailsBodyEmpty(t *testing.T) {
- req, _ := http.NewRequest("POST", "/ric/v1/subscriptions/" + resp.Id , nil)
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusMethodNotAllowed, response.Code)
-}
-
-func TestUpdateeSingleSubscriptionFailsBodyEmpty(t *testing.T) {
- req, _ := http.NewRequest("PUT", "/ric/v1/subscriptions/" + resp.Id , nil)
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusMethodNotAllowed, response.Code)
-}
-
-func TestUpdateeSingleSubscriptionFailsInvalidId(t *testing.T) {
- payload := []byte(`{"maxRetries": 11, "retryTimer": 22, "eventType":"Deleted", "targetUrl": "http://localhost:8088/xapps_handler"}`)
-
- req, _ := http.NewRequest("PUT", "/ric/v1/subscriptions/invalidSubscriptionId" + resp.Id, bytes.NewBuffer(payload))
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusNotFound, response.Code)
-}
-
-func TestPublishXappAction(t *testing.T) {
- payload := []byte(`{"maxRetries": 3, "retryTimer": 5, "eventType":"Created", "targetUrl": "http://127.0.0.1:8888"}`)
- req, _ := http.NewRequest("POST", "/ric/v1/subscriptions", bytes.NewBuffer(payload))
- response := executeRequest(req)
-
- checkResponseCode(t, http.StatusCreated, response.Code)
-
- // Create a RestApi server (simulating RM)
- ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintln(w, "Hello, XM!")
- }))
-
- l, err := net.Listen("tcp", "127.0.0.1:8888")
- if err != nil {
- log.Fatal(err)
- }
- ts.Listener.Close()
- ts.Listener = l
- ts.Start()
-
- defer ts.Close()
-
- x.sd.Publish(xapp, EventType("created"))
-}
-
-func verifySubscription(t *testing.T, subscription SubscriptionReq, url string, retries int, timer int, event string) {
- if subscription.TargetUrl != url {
- t.Errorf("Unexpected url: got=%s expected=%s", subscription.TargetUrl, url)
- }
-
- if subscription.MaxRetries != retries {
- t.Errorf("Unexpected retries: got=%d expected=%d", subscription.MaxRetries, retries)
- }
-
- if subscription.RetryTimer != timer {
- t.Errorf("Unexpected timer: got=%d expected=%d", subscription.RetryTimer, timer)
- }
-
- if subscription.EventType != event {
- t.Errorf("Unexpected event type: got=%s expected=%s", subscription.EventType, event)
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
-==================================================================================
- 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 (
- "github.com/gorilla/mux"
- cmap "github.com/orcaman/concurrent-map"
- "net/http"
-)
-
-type CmdOptions struct {
- hostAddr *string
- helmHost *string
- helmChartPath *string
-}
-
-type Resource struct {
- Method string
- Url string
- HandlerFunc http.HandlerFunc
-}
-
-type Xapp struct {
- Name string `json:"name"`
- Status string `json:"status"`
- Version string `json:"version"`
- Instances []XappInstance `json:"instances"`
-}
-
-type XappInstance struct {
- Name string `json:"name"`
- Status string `json:"status"`
- Ip string `json:"ip"`
- Port int `json:"port"`
- TxMessages []string `json:"txMessages"`
- RxMessages []string `json:"rxMessages"`
-}
-
-type XappManager struct {
- router *mux.Router
- helm Helmer
- sd SubscriptionDispatcher
- opts CmdOptions
-}
-
-type Helmer interface {
- Install(name string) (xapp Xapp, err error)
- Status(name string) (xapp Xapp, err error)
- StatusAll() (xapps []Xapp, err error)
- List() (xapps []string, err error)
- Delete(name string) (xapp Xapp, err error)
-}
-
-type Helm struct {
- host string
- chartPath string
- initDone bool
-}
-
-type SubscriptionReq struct {
- Id string `json:"id"`
- TargetUrl string `json:"targetUrl"`
- EventType string `json:"eventType"`
- MaxRetries int `json:"maxRetries"`
- RetryTimer int `json:"retryTimer"`
-}
-
-type SubscriptionResp struct {
- Id string `json:"id"`
- Version int `json:"version"`
- EventType string `json:"eventType"`
-}
-
-type SubscriptionNotif struct {
- Id string `json:"id"`
- Version int `json:"version"`
- EventType string `json:"eventType"`
- XappData Xapp `json:"xapp"`
-}
-
-type Subscription struct {
- req SubscriptionReq
- resp SubscriptionResp
-}
-
-type SubscriptionDispatcher struct {
- client *http.Client
- subscriptions cmap.ConcurrentMap
-}
-
-type MessageTypes struct {
- TxMessages []string `yaml:"txMessages"`
- RxMessages []string `yaml:"rxMessages"`
-}
-
-type EventType string
-
-const (
- Created EventType = "created"
- Updated EventType = "updated"
- Deleted EventType = "deleted"
-)
-
-const (
- Mdclog_err = 1 //! Error level log entry
- Mdclog_warn = 2 //! Warning level log entry
- Mdclog_info = 3 //! Info level log entry
- Mdclog_debug = 4 //! Debug level log entry
-)