Redesign and taking xapp-framework into use 36/5236/7
authorMohamed Abukar <abukar.mohamed@nokia.com>
Thu, 3 Dec 2020 16:20:05 +0000 (18:20 +0200)
committerMohamed Abukar <abukar.mohamed@nokia.com>
Tue, 8 Dec 2020 13:04:50 +0000 (15:04 +0200)
Change-Id: I600f667047ea2a1f946107546c32a3fb7e56b9f3
Signed-off-by: Mohamed Abukar <abukar.mohamed@nokia.com>
22 files changed:
Dockerfile [changed mode: 0644->0755]
build_vesmgr.sh
cmd/vesmgr/httpserver.go [deleted file]
cmd/vesmgr/httpserver_test.go [deleted file]
cmd/vesmgr/main.go [deleted file]
cmd/vesmgr/subscribexAPPNotifications.go [deleted file]
cmd/vesmgr/vesmgr.go [deleted file]
cmd/vesmgr/vesmgr_queryxappconfig_test.go [deleted file]
cmd/vesmgr/vesmgr_test.go [deleted file]
cmd/vespamgr/config.go [moved from cmd/vesmgr/config.go with 64% similarity]
cmd/vespamgr/config_test.go [moved from cmd/vesmgr/config_test.go with 70% similarity]
cmd/vespamgr/subprocess.go [moved from cmd/vesmgr/subprocess.go with 79% similarity, mode: 0755]
cmd/vespamgr/subprocess_test.go [moved from cmd/vesmgr/subprocess_test.go with 87% similarity, mode: 0755]
cmd/vespamgr/types.go [moved from cmd/vesmgr/vespaconf.go with 86% similarity, mode: 0755]
cmd/vespamgr/vespamgr.go [new file with mode: 0755]
cmd/vespamgr/vespamgr_test.go [moved from cmd/vesmgr/subscribexAPPNotifications_test.go with 51% similarity, mode: 0755]
config/config-file-ut.json [new file with mode: 0755]
config/config-file.json [new file with mode: 0755]
config/uta_rtg.rt [new file with mode: 0644]
container-tag.yaml
go.mod
go.sum

old mode 100644 (file)
new mode 100755 (executable)
index ec5fd57..c741132
 #   This source code is part of the near-RT RIC (RAN Intelligent Controller)
 #   platform project (RICP).
 #
-# Start from golang v1.12 base image
-FROM golang:1.12 as gobuild
+
+FROM golang:1.12 as gobuild-vespamgr
+
+# Install utilities
+RUN apt update && apt install -y iputils-ping net-tools curl sudo
 
 # Set the Working Directory for ves-agent inside the container
 RUN mkdir -p $GOPATH/src/VESPA
@@ -34,32 +37,30 @@ RUN export GOPATH=$HOME/go && \
     export PATH=$GOPATH/bin:$GOROOT/bin:$PATH && \
     go install -v ./ves-agent
 
-# Set the Working Directory for vesmgr inside the container
-RUN mkdir -p $GOPATH/src/vesmgr
-WORKDIR $GOPATH/src/vesmgr
-
-# Copy vesmgr to the Working Directory
-COPY $HOME/ .
+# Set the Working Directory for vespamgr inside the container
+RUN mkdir -p /go/src/vespamgr
+RUN mkdir -p /cfg
+COPY . /go/src/vespamgr
+WORKDIR /go/src/vespamgr
 
 RUN ./build_vesmgr.sh
 
-#################
-#
-# Second phase, copy compiled stuff to a runtime container
-
-# Ubuntu or something smaller?
+# Final, executable and deployable container
 FROM ubuntu:18.04
-# For trouble-shooting
-RUN apt-get update; apt-get install -y \
-    iputils-ping \
-    net-tools \
-    curl \
-    tcpdump
 
-# Create the configuration directory for ves agent
 RUN mkdir -p /etc/ves-agent
-COPY --from=gobuild root/go/bin /root/go/bin
+
+COPY --from=gobuild-vespamgr /usr/local/lib /usr/local/lib
+COPY --from=gobuild-vespamgr /root/go/bin /root/go/bin
+COPY --from=gobuild-vespamgr /root/go/bin/vespamgr /usr/local/bin/vesmgr
+COPY --from=gobuild-vespamgr /root/go/bin/vespamgr /vespamgr
+COPY --from=gobuild-vespamgr /go/src/vespamgr/config/* /cfg/
+
+RUN ldconfig
+
+ENV CFG_FILE=/cfg/config-file.json
+ENV RMR_SEED_RT=/cfg/uta_rtg.rt
 
 ENV PATH="/root/go/bin:${PATH}"
 
-ENTRYPOINT ["vesmgr"]
+ENTRYPOINT ["/vespamgr"]
index c1d2bf3..6166a6a 100755 (executable)
 #   platform project (RICP).
 #
 
-
-set -e
-set -x
-# Load modules
-GO111MODULE=on go mod download
+# Install RMR from deb packages at packagecloud.io
+rmr=rmr_4.1.2_amd64.deb
+wget --content-disposition  https://packagecloud.io/o-ran-sc/release/packages/debian/stretch/$rmr/download.deb
+sudo dpkg -i $rmr
+rm $rmr
+rmrdev=rmr-dev_4.1.2_amd64.deb
+wget --content-disposition https://packagecloud.io/o-ran-sc/release/packages/debian/stretch/$rmrdev/download.deb
+sudo dpkg -i $rmrdev
+rm $rmrdev
+
+# Required to find nng and rmr libs
+export LD_LIBRARY_PATH=/usr/local/lib
+
+# Go install, build, etc
 export GOPATH=$HOME/go
-export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
+export PATH=$GOPATH/bin:$PATH
+
+# xApp-framework stuff
+export CFG_FILE=$PWD/config/config-file-ut.json
+export RMR_SEED_RT=$PWD/config/uta_rtg.rt
+
+GO111MODULE=on GO_ENABLED=0 GOOS=linux
 
 # Run vesmgr UT
-go test ./...
+GO111MODULE=on go test -v -p 1 -cover -coverprofile=coverage.out ./...
 
 # setup version tag
 if [ -f container-tag.yaml ]
@@ -40,7 +55,5 @@ fi
 
 hash=$(git rev-parse --short HEAD || true)
 
-# Install vesmgr
-go install -ldflags "-X main.Version=$tag -X main.Hash=$hash" -v ./cmd/vesmgr
-
-
+# Install vespamgr
+go install -ldflags "-X main.Version=$tag -X main.Hash=$hash" -v $PWD/cmd/vespamgr
diff --git a/cmd/vesmgr/httpserver.go b/cmd/vesmgr/httpserver.go
deleted file mode 100644 (file)
index 1ac0999..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- *  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.
- *
- *  This source code is part of the near-RT RIC (RAN Intelligent Controller)
- *  platform project (RICP).
- *
- */
-
-package main
-
-import (
-       "fmt"
-       "io/ioutil"
-       "net"
-       "net/http"
-)
-
-// SupervisionURL is the url where kubernetes posts alive queries
-const SupervisionURL = "/supervision/"
-
-// HTTPServer is the VesMgr HTTP server struct
-type HTTPServer struct {
-       listener net.Listener
-}
-
-func (s *HTTPServer) init(address string) *HTTPServer {
-       var err error
-       s.listener, err = net.Listen("tcp", address)
-       if err != nil {
-               panic("Cannot listen:" + err.Error())
-       }
-       return s
-}
-
-func (s *HTTPServer) start(notifPath string, notifCh chan []byte, supCh chan chan string) {
-       go runHTTPServer(s.listener, notifPath, notifCh, supCh)
-}
-
-func (s *HTTPServer) addr() net.Addr {
-       return s.listener.Addr()
-}
-
-func runHTTPServer(listener net.Listener, xappNotifURL string, notifCh chan []byte, supervisionCh chan chan string) {
-
-       logger.Info("vesmgr http server serving at %s", listener.Addr())
-
-       http.HandleFunc(xappNotifURL, func(w http.ResponseWriter, r *http.Request) {
-
-               switch r.Method {
-               case "POST":
-                       logger.Info("httpServer: POST in %s", xappNotifURL)
-                       body, err := ioutil.ReadAll(r.Body)
-                       defer r.Body.Close()
-                       if err != nil {
-                               logger.Error("httpServer: Invalid body in POST request")
-                               return
-                       }
-                       notifCh <- body
-                       return
-               default:
-                       logger.Error("httpServer: Invalid method %s to %s", r.Method, r.URL.Path)
-                       http.Error(w, "405 method not allowed", http.StatusMethodNotAllowed)
-                       return
-               }
-       })
-
-       http.HandleFunc(SupervisionURL, func(w http.ResponseWriter, r *http.Request) {
-
-               switch r.Method {
-               case "GET":
-                       logger.Info("httpServer: GET supervision")
-                       supervisionAckCh := make(chan string)
-                       // send supervision to the main loop
-                       supervisionCh <- supervisionAckCh
-                       reply := <-supervisionAckCh
-                       logger.Info("httpServer: supervision ack from the main loop: %s", reply)
-                       fmt.Fprintf(w, reply)
-                       return
-               default:
-                       logger.Error("httpServer: invalid method %s to %s", r.Method, r.URL.Path)
-                       http.Error(w, "405 method not allowed", http.StatusMethodNotAllowed)
-                       return
-               }
-
-       })
-
-       http.Serve(listener, nil)
-}
diff --git a/cmd/vesmgr/httpserver_test.go b/cmd/vesmgr/httpserver_test.go
deleted file mode 100644 (file)
index 434c4c4..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- *  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.
- *
- *  This source code is part of the near-RT RIC (RAN Intelligent Controller)
- *  platform project (RICP).
- *
- */
-package main
-
-import (
-       "io/ioutil"
-       "net/http"
-       "os"
-       "strings"
-       "testing"
-
-       "github.com/stretchr/testify/suite"
-)
-
-type HTTPServerTestSuite struct {
-       suite.Suite
-       chNotif       chan []byte
-       chSupervision chan chan string
-       server        HTTPServer
-}
-
-// suite setup creates the HTTP server
-func (suite *HTTPServerTestSuite) SetupSuite() {
-       os.Unsetenv("http_proxy")
-       os.Unsetenv("HTTP_PROXY")
-       suite.chNotif = make(chan []byte)
-       suite.chSupervision = make(chan chan string)
-       suite.server = HTTPServer{}
-       suite.server.init(":0")
-       suite.server.start("/vesmgr_notif/", suite.chNotif, suite.chSupervision)
-}
-
-func (suite *HTTPServerTestSuite) TestHtppServerSupervisionInvalidOperation() {
-       resp, reply := suite.doPost("http://"+suite.server.addr().String()+SupervisionURL, "supervision")
-       suite.Equal("405 method not allowed\n", reply)
-       suite.Equal(405, resp.StatusCode)
-       suite.Equal("405 Method Not Allowed", resp.Status)
-}
-
-func (suite *HTTPServerTestSuite) doGet(url string) (*http.Response, string) {
-       resp, err := http.Get(url)
-       suite.Nil(err)
-
-       defer resp.Body.Close()
-       contents, err := ioutil.ReadAll(resp.Body)
-       suite.Nil(err)
-       return resp, string(contents)
-}
-
-func (suite *HTTPServerTestSuite) doPost(serverURL string, msg string) (*http.Response, string) {
-       resp, err := http.Post(serverURL, "data", strings.NewReader(msg))
-       suite.Nil(err)
-
-       defer resp.Body.Close()
-       contents, err := ioutil.ReadAll(resp.Body)
-       suite.Nil(err)
-       return resp, string(contents)
-}
-
-func replySupervision(chSupervision chan chan string, reply string) {
-       chSupervisionAck := <-chSupervision
-       chSupervisionAck <- reply
-}
-
-func (suite *HTTPServerTestSuite) TestHttpServerSupervision() {
-
-       // start the "main loop" to reply to the supervision to the HTTPServer
-       go replySupervision(suite.chSupervision, "I'm just fine")
-
-       resp, reply := suite.doGet("http://" + suite.server.addr().String() + SupervisionURL)
-
-       suite.Equal("I'm just fine", reply)
-       suite.Equal(200, resp.StatusCode)
-       suite.Equal("200 OK", resp.Status)
-}
-
-func (suite *HTTPServerTestSuite) TestHttpServerInvalidUrl() {
-       resp, reply := suite.doPost("http://"+suite.server.addr().String()+"/invalid_url", "foo")
-       suite.Equal("404 page not found\n", reply)
-       suite.Equal(404, resp.StatusCode)
-       suite.Equal("404 Not Found", resp.Status)
-}
-
-func readXAppNotification(chNotif chan []byte, ch chan []byte) {
-       notification := <-chNotif
-       ch <- notification
-}
-
-func (suite *HTTPServerTestSuite) TestHttpServerXappNotif() {
-       // start the "main loop" to receive the xAppNotification message from the HTTPServer
-       ch := make(chan []byte)
-       go readXAppNotification(suite.chNotif, ch)
-
-       resp, reply := suite.doPost("http://"+suite.server.addr().String()+"/vesmgr_notif/", "test data")
-       suite.Equal("", reply)
-       suite.Equal(200, resp.StatusCode)
-       suite.Equal("200 OK", resp.Status)
-       notification := <-ch
-       suite.Equal([]byte("test data"), notification)
-}
-
-func (suite *HTTPServerTestSuite) TestHttpServerXappNotifInvalidOperation() {
-       resp, reply := suite.doGet("http://" + suite.server.addr().String() + "/vesmgr_notif/")
-       suite.Equal("405 method not allowed\n", reply)
-       suite.Equal(405, resp.StatusCode)
-       suite.Equal("405 Method Not Allowed", resp.Status)
-}
-
-func TestHttpServerSuite(t *testing.T) {
-       suite.Run(t, new(HTTPServerTestSuite))
-}
diff --git a/cmd/vesmgr/main.go b/cmd/vesmgr/main.go
deleted file mode 100644 (file)
index f905da2..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- *  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.
- *
- *  This source code is part of the near-RT RIC (RAN Intelligent Controller)
- *  platform project (RICP).
- *
- */
-
-package main
-
-func main() {
-       vesmgr := VesMgr{}
-       vesmgr.Init(vesmgrXappNotifPort).Run()
-}
diff --git a/cmd/vesmgr/subscribexAPPNotifications.go b/cmd/vesmgr/subscribexAPPNotifications.go
deleted file mode 100644 (file)
index c87a0a3..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *  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.
- *
- *  This source code is part of the near-RT RIC (RAN Intelligent Controller)
- *  platform project (RICP).
- *
- */
-
-package main
-
-import (
-       "bytes"
-       "encoding/json"
-       "errors"
-       "fmt"
-       "io/ioutil"
-       "net/http"
-       "time"
-)
-
-// appmgr API
-const appmgrSubsPath = "/ric/v1/subscriptions"
-
-var errPostingFailed = errors.New("Posting subscriptions failed")
-var errWrongStatusCode = errors.New("Wrong subscriptions response StatusCode")
-
-func subscribexAppNotifications(targetURL string, subscriptions chan subscriptionNotification, timeout time.Duration, subsURL string) {
-       requestBody := []byte(fmt.Sprintf(`{"Data": {"maxRetries": 5, "retryTimer": 5, "eventType":"all", "targetUrl": "%v"}}`, targetURL))
-       req, err := http.NewRequest("POST", subsURL, bytes.NewBuffer(requestBody))
-       if err != nil {
-               logger.Error("Setting NewRequest failed: %s", err)
-               subscriptions <- subscriptionNotification{false, err, ""}
-               return
-       }
-       req.Header.Set("Content-Type", "application/json")
-       client := &http.Client{}
-       client.Timeout = time.Second * timeout
-       var subsID string
-       for {
-               subsID, err = subscribexAppNotificationsClientDo(req, client)
-               if err == nil {
-                       break
-               } else if err != errPostingFailed && err != errWrongStatusCode {
-                       subscriptions <- subscriptionNotification{false, err, ""}
-                       return
-               }
-               time.Sleep(5 * time.Second)
-       }
-       subscriptions <- subscriptionNotification{true, nil, subsID}
-}
-
-func subscribexAppNotificationsClientDo(req *http.Request, client *http.Client) (string, error) {
-       resp, err := client.Do(req)
-       if err != nil {
-               logger.Error("Posting subscriptions failed: %v", err)
-               return "", errPostingFailed
-       }
-       defer resp.Body.Close()
-       if resp.StatusCode == http.StatusCreated {
-               logger.Info("Subscriptions response StatusCode: %d", resp.StatusCode)
-               logger.Info("Subscriptions response headers: %s", resp.Header)
-               body, err := ioutil.ReadAll(resp.Body)
-               if err != nil {
-                       logger.Error("Subscriptions response Body read failed: %s", err)
-                       return "", err
-               }
-               logger.Info("Response Body: %s", body)
-               var result map[string]interface{}
-               if err := json.Unmarshal([]byte(body), &result); err != nil {
-                       logger.Error("json.Unmarshal failed: %s", err)
-                       return "", err
-               }
-               logger.Info("Subscription id from the response: %s", result["id"].(string))
-               return result["id"].(string), nil
-       }
-       logger.Error("Wrong subscriptions response StatusCode: %d", resp.StatusCode)
-       return "", errWrongStatusCode
-}
diff --git a/cmd/vesmgr/vesmgr.go b/cmd/vesmgr/vesmgr.go
deleted file mode 100755 (executable)
index 5086098..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- *  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.
- *
- *  This source code is part of the near-RT RIC (RAN Intelligent Controller)
- *  platform project (RICP).
- *
- */
-
-package main
-
-import (
-       "errors"
-       "fmt"
-       "io/ioutil"
-       "net"
-       "net/http"
-       "os"
-       "time"
-
-       mdcloggo "gerrit.o-ran-sc.org/r/com/golog.git"
-)
-
-var appmgrDomain string
-
-const appmgrXAppConfigPath = "/ric/v1/config"
-const appmgrPort = "8080"
-
-// VesMgr contains runtime information of the vesmgr process
-type VesMgr struct {
-       myIPAddress         string
-       chXAppSubscriptions chan subscriptionNotification
-       chXAppNotifications chan []byte
-       chSupervision       chan chan string
-       chVesagent          chan error
-       vesagent            cmdRunner
-       httpServer          HTTPServer
-}
-
-type subscriptionNotification struct {
-       subscribed bool
-       err        error
-       subsID     string
-}
-
-var logger *mdcloggo.MdcLogger
-
-// Version information, which is filled during compilation
-// Version tag of vesmgr container
-var Version string
-
-// Hash of the git commit used in building
-var Hash string
-
-const vesmgrXappNotifPort = "8080"
-const vesmgrXappNotifPath = "/vesmgr_xappnotif/"
-const timeoutPostXAppSubscriptions = 5
-const vespaConfigFile = "/etc/ves-agent/ves-agent.yaml"
-
-func init() {
-       logger, _ = mdcloggo.InitLogger("vesmgr")
-}
-
-func getMyIP() (myIP string, retErr error) {
-       addrs, err := net.InterfaceAddrs()
-       if err != nil {
-               logger.Error("net.InterfaceAddrs failed: %s", err.Error())
-               return "", err
-       }
-       for _, addr := range addrs {
-               // check the address type and if it is not a loopback take it
-               if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
-                       if ipnet.IP.To4() != nil {
-                               logger.Info("My IP Address: %s", ipnet.IP.String())
-                               return ipnet.IP.String(), nil
-                       }
-               }
-       }
-       return "", nil
-}
-
-func createConf(fname string, xappMetrics []byte) {
-       f, err := os.Create(fname)
-       if err != nil {
-               logger.Error("Cannot create vespa conf file: %s", err.Error())
-               os.Exit(1)
-       }
-       defer f.Close()
-
-       createVespaConfig(f, xappMetrics)
-       logger.Info("Vespa config created")
-}
-
-func (vesmgr *VesMgr) subscribeXAppNotifications() {
-       xappNotifURL := "http://" + vesmgr.myIPAddress + ":" + vesmgrXappNotifPort + vesmgrXappNotifPath
-       subsURL := "http://" + appmgrDomain + ":" + appmgrPort + appmgrSubsPath
-       go subscribexAppNotifications(xappNotifURL, vesmgr.chXAppSubscriptions, timeoutPostXAppSubscriptions, subsURL)
-       logger.Info("xApp notifications subscribed from %s", subsURL)
-}
-
-// Init initializes the vesmgr
-func (vesmgr *VesMgr) Init(listenPort string) *VesMgr {
-       logger.Info("vesmgrInit")
-       logger.Info("version: %s (%s)", Version, Hash)
-
-       var err error
-       if vesmgr.myIPAddress, err = getMyIP(); err != nil || vesmgr.myIPAddress == "" {
-               logger.Error("Cannot get myIPAddress: IP %s", vesmgr.myIPAddress)
-               panic("Cannot get my IP address")
-       }
-
-       var ok bool
-       appmgrDomain, ok = os.LookupEnv("VESMGR_APPMGRDOMAIN")
-       if ok {
-               logger.Info("Using appmgrdomain %s", appmgrDomain)
-       } else {
-               pltnamespace := os.Getenv("PLT_NAMESPACE")
-               if pltnamespace == "" {
-                       pltnamespace = "ricplt"
-               }
-               appmgrDomain = fmt.Sprintf("service-%s-appmgr-http.%s.svc.cluster.local", pltnamespace, pltnamespace)
-               logger.Info("Using default appmgrdomain %s", appmgrDomain)
-       }
-       vesmgr.chXAppSubscriptions = make(chan subscriptionNotification)
-       // Create notifications as buffered channel so that
-       // xappmgr does not block if we are stuck somewhere
-       vesmgr.chXAppNotifications = make(chan []byte, 10)
-       vesmgr.chSupervision = make(chan chan string)
-       vesmgr.chVesagent = make(chan error)
-       vesmgr.httpServer = HTTPServer{}
-       vesmgr.httpServer.init(vesmgr.myIPAddress + ":" + listenPort)
-       vesmgr.vesagent = makeRunner("ves-agent", "-i", os.Getenv("VESMGR_HB_INTERVAL"),
-               "-m", os.Getenv("VESMGR_MEAS_INTERVAL"), "--Measurement.Prometheus.Address",
-               os.Getenv("VESMGR_PROMETHEUS_ADDR"), "--AlertManager.Bind", os.Getenv("VESMGR_ALERTMANAGER_BIND_ADDR"),
-               "--Debug")
-       return vesmgr
-}
-
-func (vesmgr *VesMgr) startVesagent() {
-       vesmgr.vesagent.run(vesmgr.chVesagent)
-}
-
-func (vesmgr *VesMgr) killVespa() error {
-       logger.Info("Killing vespa")
-       err := vesmgr.vesagent.kill()
-       if err != nil {
-               logger.Error("Cannot kill vespa: %s", err.Error())
-               return err
-       }
-       return <-vesmgr.chVesagent // wait vespa exit
-}
-
-func queryXAppsConfig(appmgrURL string, timeout time.Duration) ([]byte, error) {
-       emptyConfig := []byte("{}")
-       logger.Info("query xAppConfig started, url %s", appmgrURL)
-       req, err := http.NewRequest("GET", appmgrURL, nil)
-       if err != nil {
-               logger.Error("Failed to create a HTTP request: %s", err)
-               return emptyConfig, err
-       }
-       req.Header.Set("Content-Type", "application/json")
-       client := &http.Client{}
-       client.Timeout = time.Second * timeout
-       resp, err := client.Do(req)
-       if err != nil {
-               logger.Error("Query xApp config failed: %s", err)
-               return emptyConfig, err
-       }
-       defer resp.Body.Close()
-       if resp.StatusCode == http.StatusOK {
-               body, err := ioutil.ReadAll(resp.Body)
-               if err != nil {
-                       logger.Error("Failed to read xApp config body: %s", err)
-                       return emptyConfig, err
-               }
-               logger.Info("query xAppConfig completed")
-               return body, nil
-       }
-       logger.Error("Error from xApp config query: %s", resp.Status)
-       return emptyConfig, errors.New(resp.Status)
-}
-
-func queryConf() (appConfig []byte, err error) {
-       for i := 0; i < 10; i++ {
-               appConfig, err = queryXAppsConfig("http://"+appmgrDomain+":"+appmgrPort+appmgrXAppConfigPath, 10*time.Second)
-               if len(appConfig) > 0 {
-                       break
-               }
-               time.Sleep(5 * time.Second)
-       }
-       return appConfig, err
-}
-
-func (vesmgr *VesMgr) emptyNotificationsChannel() {
-       for {
-               select {
-               case <-vesmgr.chXAppNotifications:
-                       // we don't care the content
-               default:
-                       return
-               }
-       }
-}
-
-func (vesmgr *VesMgr) servRequest() {
-       select {
-       case supervision := <-vesmgr.chSupervision:
-               logger.Info("vesmgr: supervision")
-               supervision <- "OK"
-       case xAppNotif := <-vesmgr.chXAppNotifications:
-               logger.Info("vesmgr: xApp notification")
-               logger.Info(string(xAppNotif))
-               vesmgr.emptyNotificationsChannel()
-               /*
-               * If xapp config query fails then we cannot create
-               * a new configuration and kill vespa.
-               * In that case we assume that
-               * the situation is fixed when the next
-               * xapp notif comes
-                */
-               xappConfig, err := queryConf()
-               if err == nil {
-                       vesmgr.killVespa()
-                       createConf(vespaConfigFile, xappConfig)
-                       vesmgr.startVesagent()
-               }
-       case err := <-vesmgr.chVesagent:
-               logger.Error("Vesagent exited: " + err.Error())
-               os.Exit(1)
-       }
-}
-
-func (vesmgr *VesMgr) waitSubscriptionLoop() {
-       for {
-               select {
-               case supervision := <-vesmgr.chSupervision:
-                       logger.Info("vesmgr: supervision")
-                       supervision <- "OK"
-               case isSubscribed := <-vesmgr.chXAppSubscriptions:
-                       if isSubscribed.err != nil {
-                               logger.Error("Failed to make xApp subscriptions, vesmgr exiting: %s", isSubscribed.err)
-                               os.Exit(1)
-                       }
-                       return
-               }
-       }
-}
-
-// Run the vesmgr process main loop
-func (vesmgr *VesMgr) Run() {
-       logger.Info("vesmgr main loop ready")
-
-       vesmgr.httpServer.start(vesmgrXappNotifPath, vesmgr.chXAppNotifications, vesmgr.chSupervision)
-
-       vesmgr.subscribeXAppNotifications()
-
-       vesmgr.waitSubscriptionLoop()
-
-       xappConfig, _ := queryConf()
-
-       createConf(vespaConfigFile, xappConfig)
-
-       vesmgr.startVesagent()
-       for {
-               vesmgr.servRequest()
-       }
-}
diff --git a/cmd/vesmgr/vesmgr_queryxappconfig_test.go b/cmd/vesmgr/vesmgr_queryxappconfig_test.go
deleted file mode 100755 (executable)
index 053adcc..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- *  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.
- *
- *  This source code is part of the near-RT RIC (RAN Intelligent Controller)
- *  platform project (RICP).
- *
- */
-
-package main
-
-import (
-       "fmt"
-       "net"
-       "net/http"
-       "net/url"
-       "os"
-       "sync"
-       "testing"
-       "time"
-
-       "github.com/stretchr/testify/suite"
-)
-
-type do func(w http.ResponseWriter)
-
-type QueryXAppsConfigTestSuite struct {
-       suite.Suite
-       listener    net.Listener
-       xAppMgrFunc do
-       mu          sync.Mutex
-}
-
-// suite setup creates the HTTP server
-func (suite *QueryXAppsConfigTestSuite) SetupSuite() {
-       os.Unsetenv("http_proxy")
-       os.Unsetenv("HTTP_PROXY")
-       var err error
-       suite.listener, err = net.Listen("tcp", ":0")
-       suite.Nil(err)
-       go runXAppMgr(suite.listener, "/test_url/", suite)
-}
-
-func runXAppMgr(listener net.Listener, url string, suite *QueryXAppsConfigTestSuite) {
-
-       http.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
-               switch r.Method {
-               case "GET":
-                       suite.mu.Lock()
-                       defer suite.mu.Unlock()
-                       suite.xAppMgrFunc(w)
-               }
-       })
-
-       http.Serve(listener, nil)
-}
-
-func (suite *QueryXAppsConfigTestSuite) TestQueryXAppsConfigFailsWithTimeout() {
-       doSleep := func(w http.ResponseWriter) {
-               time.Sleep(time.Second * 2)
-       }
-
-       suite.mu.Lock()
-       suite.xAppMgrFunc = doSleep
-       suite.mu.Unlock()
-
-       data, err := queryXAppsConfig("http://"+suite.listener.Addr().String()+"/test_url/", 1)
-       suite.Equal([]byte("{}"), data)
-       suite.NotNil(err)
-       e, ok := err.(*url.Error)
-       suite.Equal(ok, true)
-       suite.Equal(e.Timeout(), true)
-}
-
-func (suite *QueryXAppsConfigTestSuite) TestQueryXAppsConfigFailsWithAnErrorReply() {
-       doReplyWithErr := func(w http.ResponseWriter) {
-               http.Error(w, "405 method not allowed", http.StatusMethodNotAllowed)
-       }
-
-       suite.mu.Lock()
-       suite.xAppMgrFunc = doReplyWithErr
-       suite.mu.Unlock()
-
-       data, err := queryXAppsConfig("http://"+suite.listener.Addr().String()+"/test_url/", 1)
-       suite.Equal([]byte("{}"), data)
-       suite.NotNil(err)
-       suite.Equal("405 Method Not Allowed", err.Error())
-}
-
-func (suite *QueryXAppsConfigTestSuite) TestQueryXAppsConfigOk() {
-       doReply := func(w http.ResponseWriter) {
-               fmt.Fprintf(w, "reply message")
-       }
-
-       suite.mu.Lock()
-       suite.xAppMgrFunc = doReply
-       suite.mu.Unlock()
-
-       data, err := queryXAppsConfig("http://"+suite.listener.Addr().String()+"/test_url/", 1)
-       suite.NotNil(data)
-       suite.Nil(err)
-       suite.Equal(data, []byte("reply message"))
-}
-
-func TestQueryXAppsConfigTestSuite(t *testing.T) {
-       suite.Run(t, new(QueryXAppsConfigTestSuite))
-}
diff --git a/cmd/vesmgr/vesmgr_test.go b/cmd/vesmgr/vesmgr_test.go
deleted file mode 100644 (file)
index 21c69c0..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- *  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.
- *
- *  This source code is part of the near-RT RIC (RAN Intelligent Controller)
- *  platform project (RICP).
- *
- */
-
-package main
-
-import (
-       "errors"
-       "os"
-       "os/exec"
-       "path/filepath"
-       "strconv"
-       "testing"
-       "time"
-
-       "github.com/stretchr/testify/assert"
-       "github.com/stretchr/testify/suite"
-)
-
-func TestGetMyIP(t *testing.T) {
-       myIPAddress, err := getMyIP()
-       assert.NotEqual(t, string(""), myIPAddress)
-       assert.Nil(t, err)
-}
-
-func TestConfCreate(t *testing.T) {
-       tmpfile := filepath.Join(os.TempDir(), "vestest."+strconv.Itoa(os.Getpid()))
-       defer os.Remove(tmpfile) // clean up
-       createConf(tmpfile, []byte("{}"))
-       _, err := os.Stat(tmpfile)
-       assert.Nil(t, err)
-}
-
-type VesmgrTestSuite struct {
-       suite.Suite
-       vesmgr VesMgr
-}
-
-func (suite *VesmgrTestSuite) SetupSuite() {
-       suite.vesmgr = VesMgr{}
-       suite.vesmgr.Init("0")
-       logger.MdcAdd("Testvesmgr", "0.0.1")
-       os.Setenv("VESMGR_HB_INTERVAL", "30s")
-       os.Setenv("VESMGR_MEAS_INTERVAL", "30s")
-       os.Setenv("VESMGR_PRICOLLECTOR_ADDR", "127.1.1.1")
-       os.Setenv("VESMGR_PRICOLLECTOR_PORT", "8443")
-       os.Setenv("VESMGR_PROMETHEUS_ADDR", "http://localhost:9090")
-}
-
-func (suite *VesmgrTestSuite) TestMainLoopSupervision() {
-       go suite.vesmgr.servRequest()
-       ch := make(chan string)
-       suite.vesmgr.chSupervision <- ch
-       reply := <-ch
-       suite.Equal("OK", reply)
-}
-
-func (suite *VesmgrTestSuite) TestMainLoopVesagentError() {
-       if os.Getenv("TEST_VESPA_EXIT") == "1" {
-               // we're run in a new process, now make vesmgr main loop exit
-               go suite.vesmgr.servRequest()
-               suite.vesmgr.chVesagent <- errors.New("vesagent killed")
-               // we should never actually end up to this sleep, since the runVesmgr should exit
-               time.Sleep(3 * time.Second)
-               return
-       }
-
-       // Run the vesmgr exit test as a separate process
-       cmd := exec.Command(os.Args[0], "-test.run", "TestVesMgrSuite", "-testify.m", "TestMainLoopVesagentError")
-       cmd.Env = append(os.Environ(), "TEST_VESPA_EXIT=1")
-       cmd.Stdout = os.Stdout
-       cmd.Stderr = os.Stderr
-       err := cmd.Run()
-       // check that vesmgr existed with status 1
-
-       e, ok := err.(*exec.ExitError)
-       suite.True(ok)
-       suite.Equal("exit status 1", e.Error())
-}
-
-func (suite *VesmgrTestSuite) TestWaitSubscriptionLoopRespondsSupervisionAndBreaksWhenReceivedSubsNotif() {
-       go func() {
-               time.Sleep(time.Second)
-               ch := make(chan string)
-               suite.vesmgr.chSupervision <- ch
-               suite.Equal("OK", <-ch)
-               suite.vesmgr.chSupervision <- ch
-               suite.Equal("OK", <-ch)
-               suite.vesmgr.chXAppSubscriptions <- subscriptionNotification{true, nil, ""}
-       }()
-
-       suite.vesmgr.waitSubscriptionLoop()
-}
-
-func (suite *VesmgrTestSuite) TestEmptyNotificationChannelReadsAllMsgsFromCh() {
-       go func() {
-               for i := 0; i < 11; i++ {
-                       suite.vesmgr.chXAppNotifications <- []byte("hello")
-               }
-       }()
-       time.Sleep(500 * time.Millisecond)
-       <-suite.vesmgr.chXAppNotifications
-       suite.vesmgr.emptyNotificationsChannel()
-       select {
-       case <-suite.vesmgr.chXAppNotifications:
-               suite.Fail("Got unexpected notification")
-       default:
-               // ok
-       }
-}
-
-func (suite *VesmgrTestSuite) TestVespaKilling() {
-       suite.vesmgr.vesagent = makeRunner("sleep", "20")
-       suite.vesmgr.startVesagent()
-       suite.NotNil(suite.vesmgr.killVespa())
-}
-
-func (suite *VesmgrTestSuite) TestVespaKillingAlreadyKilled() {
-       suite.vesmgr.vesagent = makeRunner("sleep", "20")
-       suite.vesmgr.startVesagent()
-       suite.NotNil(suite.vesmgr.killVespa())
-       // Just check that second kill does not block execution
-       suite.NotNil(suite.vesmgr.killVespa())
-}
-
-func TestVesMgrSuite(t *testing.T) {
-       suite.Run(t, new(VesmgrTestSuite))
-}
similarity index 64%
rename from cmd/vesmgr/config.go
rename to cmd/vespamgr/config.go
index 8ad122d..b7779f2 100755 (executable)
@@ -27,10 +27,10 @@ import (
        "io"
        "io/ioutil"
        "os"
-       "strconv"
        "strings"
        "time"
 
+       app "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
        "gopkg.in/yaml.v2"
 )
 
@@ -38,7 +38,7 @@ const defaultReportingEntityID = "00000000-0000-0000-0000-000000000000"
 const defaultVNFName = "Vespa"
 const defaultNFNamingCode = "ricp"
 
-func readSystemUUID() string {
+func (v *VespaMgr) readSystemUUID() string {
        data, err := ioutil.ReadFile("/sys/class/dmi/id/product_uuid")
        if err != nil {
                return defaultReportingEntityID
@@ -46,7 +46,7 @@ func readSystemUUID() string {
        return strings.TrimSpace(string(data))
 }
 
-func getVNFName() string {
+func (v *VespaMgr) getVNFName() string {
        VNFName := os.Getenv("VESMGR_VNFNAME")
        if VNFName == "" {
                return defaultVNFName
@@ -54,7 +54,7 @@ func getVNFName() string {
        return VNFName
 }
 
-func getNFNamingCode() string {
+func (v *VespaMgr) getNFNamingCode() string {
        NFNamingCode := os.Getenv("VESMGR_NFNAMINGCODE")
        if NFNamingCode == "" {
                return defaultNFNamingCode
@@ -62,16 +62,16 @@ func getNFNamingCode() string {
        return NFNamingCode
 }
 
-func basicVespaConf() VESAgentConfiguration {
+func (v *VespaMgr) BasicVespaConf() VESAgentConfiguration {
        var vespaconf = VESAgentConfiguration{
                DataDir: "/tmp/data",
                Debug:   false,
                Event: EventConfiguration{
-                       VNFName:             getVNFName(),
+                       VNFName:             v.getVNFName(),
                        ReportingEntityName: "Vespa",
-                       ReportingEntityID:   readSystemUUID(),
+                       ReportingEntityID:   v.readSystemUUID(),
                        MaxSize:             2000000,
-                       NfNamingCode:        getNFNamingCode(),
+                       NfNamingCode:        v.getNFNamingCode(),
                        NfcNamingCodes:      []NfcNamingCode{},
                        RetryInterval:       time.Second * 5,
                        MaxMissed:           2,
@@ -95,20 +95,6 @@ func basicVespaConf() VESAgentConfiguration {
        return vespaconf
 }
 
-// AppMetricsStruct contains xapplication metrics definition
-type AppMetricsStruct struct {
-       MoId           string
-       MeasType       string
-       MeasId         string
-       MeasInterval   string
-       ObjectName     string
-       ObjectInstance string
-       CounterId      string
-}
-
-// AppMetrics contains metrics definitions for all Xapps
-type AppMetrics map[string]AppMetricsStruct
-
 // Parses the metrics data from an array of bytes, which is expected to contain a JSON
 // array with structs of the following format:
 //
@@ -125,19 +111,19 @@ type AppMetrics map[string]AppMetricsStruct
 //     ]
 //    }
 // }
-func parseMetricsFromDescriptor(descriptor []byte, appMetrics AppMetrics) AppMetrics {
+func (v *VespaMgr) ParseMetricsFromDescriptor(descriptor []byte, appMetrics AppMetrics) AppMetrics {
        var desc []map[string]interface{}
        json.Unmarshal(descriptor, &desc)
 
-       for _, app := range desc {
-               config, configOk := app["config"]
+       for _, appl := range desc {
+               config, configOk := appl["config"]
                if !configOk {
-                       logger.Info("No xApp config found!")
+                       app.Logger.Info("No xApp config found!")
                        continue
                }
                measurements, measurementsOk := config.(map[string]interface{})["measurements"]
                if !measurementsOk {
-                       logger.Info("No xApp metrics found!")
+                       app.Logger.Info("No xApp metrics found!")
                        continue
                }
 
@@ -148,12 +134,12 @@ func parseMetricsFromDescriptor(descriptor []byte, appMetrics AppMetrics) AppMet
                        measInterval, measIntervalOk := m.(map[string]interface{})["measInterval"].(string)
                        metrics, metricsOk := m.(map[string]interface{})["metrics"]
                        if !metricsOk || !measTypeOk || !measIdOk || !moIdOk || !measIntervalOk {
-                               logger.Info("No metrics found for moId=%s measType=%s measId=%s measInterval=%s", moId, measId, measType, measInterval)
+                               app.Logger.Info("No metrics found for moId=%s measType=%s measId=%s measInterval=%s", moId, measId, measType, measInterval)
                                continue
                        }
-                       logger.Info("Parsed measurement: moId=%s type=%s id=%s interval=%s", moId, measType, measId, measInterval)
+                       app.Logger.Info("Parsed measurement: moId=%s type=%s id=%s interval=%s", moId, measType, measId, measInterval)
 
-                       parseMetricsRules(metrics.([]interface{}), appMetrics, moId, measType, measId, measInterval)
+                       v.ParseMetricsRules(metrics.([]interface{}), appMetrics, moId, measType, measId, measInterval)
                }
        }
        return appMetrics
@@ -163,7 +149,7 @@ func parseMetricsFromDescriptor(descriptor []byte, appMetrics AppMetrics) AppMet
 // of the following format:
 //    { "name": xxx, "objectName": yyy, "objectInstance": zzz }
 // Entries, which do not have all the necessary fields, are ignored.
-func parseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics, moId, measType, measId, measInterval string) AppMetrics {
+func (v *VespaMgr) ParseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics, moId, measType, measId, measInterval string) AppMetrics {
        for _, element := range metricsMap {
                name, nameOk := element.(map[string]interface{})["name"].(string)
                if nameOk {
@@ -173,17 +159,17 @@ func parseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics, moId, me
                        counterId, counterIdOk := element.(map[string]interface{})["counterId"].(string)
                        if !alreadyFound && objectNameOk && objectInstanceOk && counterIdOk {
                                appMetrics[name] = AppMetricsStruct{moId, measType, measId, measInterval, objectName, objectInstance, counterId}
-                               logger.Info("Parsed counter name=%s %s/%s  M%sC%s", name, objectName, objectInstance, measId, counterId)
+                               app.Logger.Info("Parsed counter name=%s %s/%s  M%sC%s", name, objectName, objectInstance, measId, counterId)
                        }
                        if alreadyFound {
-                               logger.Info("skipped duplicate counter %s", name)
+                               app.Logger.Info("skipped duplicate counter %s", name)
                        }
                }
        }
        return appMetrics
 }
 
-func getRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) bool {
+func (v *VespaMgr) GetRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) bool {
        makeRule := func(expr string, value AppMetricsStruct) MetricRule {
                return MetricRule{
                        Target:         "AdditionalObjects",
@@ -200,14 +186,14 @@ func getRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) bool {
                }
        }
        appMetrics := make(AppMetrics)
-       metrics := parseMetricsFromDescriptor(xAppConfig, appMetrics)
+       metrics := v.ParseMetricsFromDescriptor(xAppConfig, appMetrics)
 
-       if pltFile := os.Getenv("VESMGR_PLT_CFG_FILE"); pltFile != "" {
+       if pltFile := os.Getenv("VESMGR_PLT_CFG_FILE"); pltFile != "" {
                pltConfig, err := ioutil.ReadFile(pltFile)
                if err != nil {
-                       logger.Error("Unable to read platform config file: %v", err)
+                       app.Logger.Error("Unable to read platform config file: %v", err)
                } else {
-                       metrics = parseMetricsFromDescriptor(pltConfig, metrics)
+                       metrics = v.ParseMetricsFromDescriptor(pltConfig, metrics)
                }
        }
 
@@ -216,46 +202,34 @@ func getRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) bool {
                vespaconf.Measurement.Prometheus.Rules.Metrics = append(vespaconf.Measurement.Prometheus.Rules.Metrics, makeRule(key, value))
        }
        if len(vespaconf.Measurement.Prometheus.Rules.Metrics) == 0 {
-               logger.Info("vespa config with empty metrics")
+               app.Logger.Info("vespa config with empty metrics")
        }
 
        return len(vespaconf.Measurement.Prometheus.Rules.Metrics) > 0
 }
 
-func getCollectorConfiguration(vespaconf *VESAgentConfiguration) {
-       vespaconf.PrimaryCollector.User = os.Getenv("VESMGR_PRICOLLECTOR_USER")
-       vespaconf.PrimaryCollector.Password = os.Getenv("VESMGR_PRICOLLECTOR_PASSWORD")
-       vespaconf.PrimaryCollector.PassPhrase = os.Getenv("VESMGR_PRICOLLECTOR_PASSPHRASE")
-       vespaconf.PrimaryCollector.FQDN = os.Getenv("VESMGR_PRICOLLECTOR_ADDR")
-       vespaconf.PrimaryCollector.ServerRoot = os.Getenv("VESMGR_PRICOLLECTOR_SERVERROOT")
-       vespaconf.PrimaryCollector.Topic = os.Getenv("VESMGR_PRICOLLECTOR_TOPIC")
-       portStr := os.Getenv("VESMGR_PRICOLLECTOR_PORT")
-
-       if portStr == "" {
-               vespaconf.PrimaryCollector.Port = 8443
-       } else {
-               port, _ := strconv.Atoi(portStr)
-               vespaconf.PrimaryCollector.Port = port
-       }
-
-       secureStr := os.Getenv("VESMGR_PRICOLLECTOR_SECURE")
-       if secureStr == "true" {
-               vespaconf.PrimaryCollector.Secure = true
-       } else {
-               vespaconf.PrimaryCollector.Secure = false
-       }
+func (v *VespaMgr) GetCollectorConfiguration(vespaconf *VESAgentConfiguration) {
+       vespaconf.PrimaryCollector.User = app.Config.GetString("controls.collector.primaryUser")
+       vespaconf.PrimaryCollector.Password = app.Config.GetString("controls.collector.primaryPassword")
+       vespaconf.PrimaryCollector.PassPhrase = ""
+       vespaconf.PrimaryCollector.FQDN = app.Config.GetString("controls.collector.primaryAddr")
+       vespaconf.PrimaryCollector.ServerRoot = app.Config.GetString("controls.collector.serverRoot")
+       vespaconf.PrimaryCollector.Topic = ""
+       vespaconf.PrimaryCollector.Port = app.Config.GetInt("controls.collector.primaryPort")
+       vespaconf.PrimaryCollector.Secure = app.Config.GetBool("controls.collector.secure")
 }
 
-func createVespaConfig(writer io.Writer, xAppStatus []byte) {
-       vespaconf := basicVespaConf()
+func (v *VespaMgr) CreateConfig(writer io.Writer, xAppStatus []byte) {
+       vespaconf := v.BasicVespaConf()
 
-       getRules(&vespaconf, xAppStatus)
+       v.GetRules(&vespaconf, xAppStatus)
 
-       getCollectorConfiguration(&vespaconf)
+       v.GetCollectorConfiguration(&vespaconf)
 
        err := yaml.NewEncoder(writer).Encode(vespaconf)
        if err != nil {
-               logger.Error("Cannot write vespa conf file: %s", err.Error())
+               app.Logger.Error("Cannot write vespa conf file: %s", err.Error())
                return
        }
+       app.Logger.Info("Config file written to: %s", app.Config.GetString("controls.vesagent.configFile"))
 }
similarity index 70%
rename from cmd/vesmgr/config_test.go
rename to cmd/vespamgr/config_test.go
index 2217c92..eacbcbd 100755 (executable)
@@ -23,7 +23,6 @@ import (
        "bytes"
        "encoding/json"
        "io/ioutil"
-       "os"
        "testing"
        "time"
 
@@ -31,7 +30,11 @@ import (
        "gopkg.in/yaml.v2"
 )
 
+var vespaMgr *VespaMgr
+
 func testBaseConf(t *testing.T, vesconf VESAgentConfiguration) {
+       vespaMgr = NewVespaMgr()
+
        assert.Equal(t, "/tmp/data", vesconf.DataDir)
        assert.False(t, vesconf.Debug)
        assert.Equal(t, vesconf.Event.MaxMissed, 2)
@@ -48,87 +51,13 @@ func testBaseConf(t *testing.T, vesconf VESAgentConfiguration) {
 }
 
 func TestBasicConfigContainsCorrectValues(t *testing.T) {
-       vesconf := basicVespaConf()
+       vesconf := vespaMgr.BasicVespaConf()
        testBaseConf(t, vesconf)
 }
 
-func TestBasicConfigContainsCorrectVNFName(t *testing.T) {
-       os.Setenv("VESMGR_VNFNAME", "VNF-111")
-       os.Setenv("VESMGR_NFNAMINGCODE", "code55")
-       vesconf := basicVespaConf()
-       assert.Equal(t, vesconf.Event.VNFName, "VNF-111")
-       assert.Equal(t, vesconf.Event.NfNamingCode, "code55")
-       os.Unsetenv("VESMGR_VNFNAME")
-       os.Unsetenv("VESMGR_NFNAMINGCODE")
-}
-
-func TestCollectorConfiguration(t *testing.T) {
-       os.Unsetenv("VESMGR_VNFNAME")
-       os.Unsetenv("VESMGR_NFNAMINGCODE")
-       os.Setenv("VESMGR_PRICOLLECTOR_USER", "user123")
-       os.Setenv("VESMGR_PRICOLLECTOR_PASSWORD", "pass123")
-       os.Setenv("VESMGR_PRICOLLECTOR_PASSPHRASE", "phrase123")
-       os.Setenv("VESMGR_PRICOLLECTOR_ADDR", "1.2.3.4")
-       os.Setenv("VESMGR_PRICOLLECTOR_PORT", "1234")
-       os.Setenv("VESMGR_PRICOLLECTOR_SERVERROOT", "vescollector")
-       os.Setenv("VESMGR_PRICOLLECTOR_TOPIC", "sometopic")
-       os.Setenv("VESMGR_PRICOLLECTOR_SECURE", "true")
-
-       vesconf := basicVespaConf()
-       getCollectorConfiguration(&vesconf)
-
-       assert.Equal(t, "user123", vesconf.PrimaryCollector.User)
-       assert.Equal(t, "pass123", vesconf.PrimaryCollector.Password)
-       assert.Equal(t, "phrase123", vesconf.PrimaryCollector.PassPhrase)
-       assert.Equal(t, "1.2.3.4", vesconf.PrimaryCollector.FQDN)
-       assert.Equal(t, 1234, vesconf.PrimaryCollector.Port)
-       assert.Equal(t, "vescollector", vesconf.PrimaryCollector.ServerRoot)
-       assert.Equal(t, "sometopic", vesconf.PrimaryCollector.Topic)
-       assert.True(t, vesconf.PrimaryCollector.Secure)
-}
-
-func TestCollectorConfigurationWhenEnvironmentVariablesAreNotDefined(t *testing.T) {
-       os.Unsetenv("VESMGR_VNFNAME")
-       os.Unsetenv("VESMGR_NFNAMINGCODE")
-       os.Unsetenv("VESMGR_PRICOLLECTOR_USER")
-       os.Unsetenv("VESMGR_PRICOLLECTOR_PASSWORD")
-       os.Unsetenv("VESMGR_PRICOLLECTOR_PASSPHRASE")
-       os.Unsetenv("VESMGR_PRICOLLECTOR_ADDR")
-       os.Unsetenv("VESMGR_PRICOLLECTOR_PORT")
-       os.Unsetenv("VESMGR_PRICOLLECTOR_SERVERROOT")
-       os.Unsetenv("VESMGR_PRICOLLECTOR_TOPIC")
-       os.Unsetenv("VESMGR_PRICOLLECTOR_SECURE")
-
-       vesconf := basicVespaConf()
-       getCollectorConfiguration(&vesconf)
-
-       assert.Equal(t, "", vesconf.PrimaryCollector.User)
-       assert.Equal(t, "", vesconf.PrimaryCollector.Password)
-       assert.Equal(t, "", vesconf.PrimaryCollector.PassPhrase)
-       assert.Equal(t, "", vesconf.PrimaryCollector.FQDN)
-       assert.Equal(t, 8443, vesconf.PrimaryCollector.Port)
-       assert.Equal(t, "", vesconf.PrimaryCollector.ServerRoot)
-       assert.Equal(t, "", vesconf.PrimaryCollector.Topic)
-       assert.False(t, vesconf.PrimaryCollector.Secure)
-}
-
-func TestCollectorConfigurationWhenPrimaryCollectorPortIsNotInteger(t *testing.T) {
-       os.Setenv("VESMGR_PRICOLLECTOR_PORT", "abcd")
-       vesconf := basicVespaConf()
-       getCollectorConfiguration(&vesconf)
-       assert.Equal(t, 0, vesconf.PrimaryCollector.Port)
-}
-
-func TestCollectorConfigurationWhenPrimaryCollectorSecureIsNotTrueOrFalse(t *testing.T) {
-       os.Setenv("VESMGR_PRICOLLECTOR_SECURE", "foo")
-       vesconf := basicVespaConf()
-       getCollectorConfiguration(&vesconf)
-       assert.False(t, vesconf.PrimaryCollector.Secure)
-}
-
 func TestYamlGenerationWithoutXAppsConfig(t *testing.T) {
        buffer := new(bytes.Buffer)
-       createVespaConfig(buffer, []byte{})
+       vespaMgr.CreateConfig(buffer, []byte{})
        var vesconf VESAgentConfiguration
        err := yaml.Unmarshal(buffer.Bytes(), &vesconf)
        assert.Nil(t, err)
@@ -140,7 +69,7 @@ func TestYamlGenerationWithXAppsConfig(t *testing.T) {
        buffer := new(bytes.Buffer)
        bytes, err := ioutil.ReadFile("../../test/xApp_config_test_output.json")
        assert.Nil(t, err)
-       createVespaConfig(buffer, bytes)
+       vespaMgr.CreateConfig(buffer, bytes)
        var vesconf VESAgentConfiguration
        err = yaml.Unmarshal(buffer.Bytes(), &vesconf)
        assert.Nil(t, err)
@@ -165,7 +94,7 @@ func TestParseMetricsRules(t *testing.T) {
                        { "name": "ricxapp_SDL_StoreError", "objectName": "ricxappSDLStoreErrorCounter", "objectInstance": "ricxappSDLStoreError", "counterId": "0011" } ]}`
        appMetrics := make(AppMetrics)
        m := metricsStringToInterfaceArray(metricsJSON)
-       appMetrics = parseMetricsRules(m, appMetrics, "SEP/XAPP", "X2", "1234", "60")
+       appMetrics = vespaMgr.ParseMetricsRules(m, appMetrics, "SEP/XAPP", "X2", "1234", "60")
        assert.Len(t, appMetrics, 6)
        assert.Equal(t, "ricxappRMRreceivedCounter", appMetrics["ricxapp_RMR_Received"].ObjectName)
        assert.Equal(t, "ricxappRMRTransmitErrorCounter", appMetrics["ricxapp_RMR_TransmitError"].ObjectName)
@@ -176,7 +105,7 @@ func TestParseMetricsRulesNoMetrics(t *testing.T) {
        appMetrics := make(AppMetrics)
        metricsJSON := `{"metrics": []`
        m := metricsStringToInterfaceArray(metricsJSON)
-       appMetrics = parseMetricsRules(m, appMetrics, "SEP/XAPP", "X2", "1234", "60")
+       appMetrics = vespaMgr.ParseMetricsRules(m, appMetrics, "SEP/XAPP", "X2", "1234", "60")
        assert.Empty(t, appMetrics)
 }
 
@@ -185,7 +114,7 @@ func TestParseMetricsRulesAdditionalFields(t *testing.T) {
        metricsJSON := `{"metrics": [
                        { "additionalField": "valueIgnored", "name": "ricxapp_RMR_Received", "objectName": "ricxappRMRreceivedCounter", "objectInstance": "ricxappRMRReceived", "counterId": "0011" }]}`
        m := metricsStringToInterfaceArray(metricsJSON)
-       appMetrics = parseMetricsRules(m, appMetrics, "SEP/XAPP", "X2", "1234", "60")
+       appMetrics = vespaMgr.ParseMetricsRules(m, appMetrics, "SEP/XAPP", "X2", "1234", "60")
        assert.Len(t, appMetrics, 1)
        assert.Equal(t, "ricxappRMRreceivedCounter", appMetrics["ricxapp_RMR_Received"].ObjectName)
        assert.Equal(t, "ricxappRMRReceived", appMetrics["ricxapp_RMR_Received"].ObjectInstance)
@@ -198,7 +127,7 @@ func TestParseMetricsRulesMissingFields(t *testing.T) {
                        { "name": "ricxapp_RMR_ReceiveError", "objectInstance": "ricxappRMRReceiveError" },
                        { "name": "ricxapp_RMR_Transmitted", "objectName": "ricxappRMRTransmittedCounter", "objectInstance": "ricxappRMRTransmitted", "counterId": "0011" }]}`
        m := metricsStringToInterfaceArray(metricsJSON)
-       appMetrics = parseMetricsRules(m, appMetrics, "SEP/XAPP", "X2", "1234", "60")
+       appMetrics = vespaMgr.ParseMetricsRules(m, appMetrics, "SEP/XAPP", "X2", "1234", "60")
        assert.Len(t, appMetrics, 2)
        assert.Equal(t, "ricxappRMRreceivedCounter", appMetrics["ricxapp_RMR_Received"].ObjectName)
        assert.Equal(t, "ricxappRMRTransmittedCounter", appMetrics["ricxapp_RMR_Transmitted"].ObjectName)
@@ -213,7 +142,7 @@ func TestParseMetricsRulesDuplicateDefinitionIsIgnored(t *testing.T) {
                        { "name": "ricxapp_RMR_Received", "objectName": "ricxappRMRreceivedCounterXXX", "objectInstance": "ricxappRMRReceivedXXX", "counterId": "0011" },
                        { "name": "ricxapp_RMR_Transmitted", "objectName": "ricxappRMRTransmittedCounter", "objectInstance": "ricxappRMRTransmitted", "counterId": "0011" }]}`
        m := metricsStringToInterfaceArray(metricsJSON)
-       appMetrics = parseMetricsRules(m, appMetrics, "SEP/XAPP", "X2", "1234", "60")
+       appMetrics = vespaMgr.ParseMetricsRules(m, appMetrics, "SEP/XAPP", "X2", "1234", "60")
        assert.Len(t, appMetrics, 2)
        assert.Equal(t, "ricxappRMRreceivedCounter", appMetrics["ricxapp_RMR_Received"].ObjectName)
        assert.Equal(t, "ricxappRMRReceived", appMetrics["ricxapp_RMR_Received"].ObjectInstance)
@@ -227,8 +156,8 @@ func TestParseMetricsRulesIncrementalFillOfAppMetrics(t *testing.T) {
                        { "name": "ricxapp_RMR_Transmitted", "objectName": "ricxappRMRTransmittedCounter", "objectInstance": "ricxappRMRTransmitted", "counterId": "0011" }]}`
        m1 := metricsStringToInterfaceArray(metricsJSON1)
        m2 := metricsStringToInterfaceArray(metricsJSON2)
-       appMetrics = parseMetricsRules(m1, appMetrics, "SEP/XAPP", "X2", "1234", "60")
-       appMetrics = parseMetricsRules(m2, appMetrics, "SEP/XAPP", "X2", "1234", "60")
+       appMetrics = vespaMgr.ParseMetricsRules(m1, appMetrics, "SEP/XAPP", "X2", "1234", "60")
+       appMetrics = vespaMgr.ParseMetricsRules(m2, appMetrics, "SEP/XAPP", "X2", "1234", "60")
        assert.Len(t, appMetrics, 2)
        assert.Equal(t, "ricxappRMRreceivedCounter", appMetrics["ricxapp_RMR_Received"].ObjectName)
        assert.Equal(t, "ricxappRMRReceived", appMetrics["ricxapp_RMR_Received"].ObjectInstance)
@@ -239,7 +168,7 @@ func TestParseXAppDescriptor(t *testing.T) {
        bytes, err := ioutil.ReadFile("../../test/xApp_config_test_output.json")
        assert.Nil(t, err)
 
-       appMetrics = parseMetricsFromDescriptor(bytes, appMetrics)
+       appMetrics = vespaMgr.ParseMetricsFromDescriptor(bytes, appMetrics)
        assert.Len(t, appMetrics, 4)
        assert.Equal(t, "App1ExampleCounterOneObject", appMetrics["App1ExampleCounterOne"].ObjectName)
        assert.Equal(t, "App1ExampleCounterOneObjectInstance", appMetrics["App1ExampleCounterOne"].ObjectInstance)
@@ -256,7 +185,7 @@ func TestParseXAppDescriptorWithNoConfig(t *testing.T) {
                         {{"metadata": "something", "descriptor": "somethingelse"}}]`
        metricsBytes := []byte(metricsJSON)
        appMetrics := make(AppMetrics)
-       appMetrics = parseMetricsFromDescriptor(metricsBytes, appMetrics)
+       appMetrics = vespaMgr.ParseMetricsFromDescriptor(metricsBytes, appMetrics)
        assert.Empty(t, appMetrics)
 }
 
@@ -265,6 +194,6 @@ func TestParseXAppDescriptorWithNoMetrics(t *testing.T) {
                         {{"metadata": "something", "descriptor": "somethingelse", "config":{}}}]`
        metricsBytes := []byte(metricsJSON)
        appMetrics := make(AppMetrics)
-       appMetrics = parseMetricsFromDescriptor(metricsBytes, appMetrics)
+       appMetrics = vespaMgr.ParseMetricsFromDescriptor(metricsBytes, appMetrics)
        assert.Empty(t, appMetrics)
 }
old mode 100644 (file)
new mode 100755 (executable)
similarity index 79%
rename from cmd/vesmgr/subprocess.go
rename to cmd/vespamgr/subprocess.go
index 0c84312..0ea78cf
@@ -25,13 +25,13 @@ import (
        "os/exec"
 )
 
-type cmdRunner struct {
+type CommandRunner struct {
        exe  string
        args []string
        cmd  *exec.Cmd
 }
 
-func (r *cmdRunner) run(result chan error) {
+func (r *CommandRunner) Run(result chan error) {
        r.cmd = exec.Command(r.exe, r.args...)
        r.cmd.Stdout = os.Stdout
        r.cmd.Stderr = os.Stderr
@@ -45,11 +45,14 @@ func (r *cmdRunner) run(result chan error) {
        }()
 }
 
-func (r *cmdRunner) kill() error {
-       return r.cmd.Process.Kill()
+func (r *CommandRunner) Kill() error {
+       if r.cmd != nil {
+               return r.cmd.Process.Kill()
+       }
+       return nil
 }
 
-func makeRunner(exe string, arg ...string) cmdRunner {
-       r := cmdRunner{exe: exe, args: arg}
+func NewCommandRunner(exe string, arg ...string) *CommandRunner {
+       r := &CommandRunner{exe: exe, args: arg}
        return r
 }
old mode 100644 (file)
new mode 100755 (executable)
similarity index 87%
rename from cmd/vesmgr/subprocess_test.go
rename to cmd/vespamgr/subprocess_test.go
index d20a235..019e3c3
@@ -27,25 +27,25 @@ import (
 )
 
 func TestProcessRunning(t *testing.T) {
-       r := makeRunner("echo", "a")
+       r := NewCommandRunner("echo", "a")
        ch := make(chan error)
-       r.run(ch)
+       r.Run(ch)
        err := <-ch
        assert.Nil(t, err)
 }
 
 func TestProcessKill(t *testing.T) {
-       r := makeRunner("sleep", "20")
+       r := NewCommandRunner("sleep", "20")
        ch := make(chan error)
-       r.run(ch)
-       assert.Nil(t, r.kill())
+       r.Run(ch)
+       assert.Nil(t, r.Kill())
        <-ch // wait and seee that kills is actually done
 }
 
 func TestProcessRunningFails(t *testing.T) {
-       r := makeRunner("foobarbaz")
+       r := NewCommandRunner("foobarbaz")
        ch := make(chan error)
-       r.run(ch)
+       r.Run(ch)
        err := <-ch
        assert.NotNil(t, err)
 }
old mode 100644 (file)
new mode 100755 (executable)
similarity index 86%
rename from cmd/vesmgr/vespaconf.go
rename to cmd/vespamgr/types.go
index bbff8d2..c364196
@@ -25,6 +25,22 @@ import (
        "time"
 )
 
+type VespaMgr struct {
+       rmrReady             bool
+       vesAgent             *CommandRunner
+       chVesagent           chan error
+       appmgrHost           string
+       appmgrUrl            string
+       appmgrNotifUrl       string
+       appmgrSubsUrl        string
+       appmgrRetry          int
+       hbInterval           string
+       measInterval         string
+       prometheusAddr       string
+       alertManagerBindAddr string
+       subscriptionId       string
+}
+
 // Structs are copied from https://github.com/nokia/ONAP-VESPA/tree/master/ves-agent/config
 // and from https://github.com/nokia/ONAP-VESPA/blob/master/govel/config.go
 // Using tag v0.3.0
@@ -115,3 +131,20 @@ type VESAgentConfiguration struct {
        CaCert           string                   `yaml:"caCert,omitempty"` // Root certificate content
        DataDir          string                   `yaml:"datadir"`          // Path to directory containing data
 }
+
+// AppMetricsStruct contains xapplication metrics definition
+type AppMetricsStruct struct {
+       MoId           string
+       MeasType       string
+       MeasId         string
+       MeasInterval   string
+       ObjectName     string
+       ObjectInstance string
+       CounterId      string
+}
+
+// AppMetrics contains metrics definitions for all Xapps
+type AppMetrics map[string]AppMetricsStruct
+
+var Version string
+var Hash string
diff --git a/cmd/vespamgr/vespamgr.go b/cmd/vespamgr/vespamgr.go
new file mode 100755 (executable)
index 0000000..1a5246f
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ *  Copyright (c) 2020 AT&T Intellectual Property.
+ *  Copyright (c) 2020 Nokiv.
+ *
+ *  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.
+ *
+ * This source code is part of the near-RT RIC (RAN Intelligent Controller)
+ * platform project (RICP).
+ */
+
+package main
+
+import (
+       "bytes"
+       "encoding/json"
+       "fmt"
+       "io/ioutil"
+       "net/http"
+       "os"
+       "strings"
+       "time"
+
+       app "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
+)
+
+func NewVespaMgr() *VespaMgr {
+       return &VespaMgr{
+               rmrReady:             false,
+               chVesagent:           make(chan error),
+               appmgrHost:           app.Config.GetString("controls.appManager.host"),
+               appmgrUrl:            app.Config.GetString("controls.appManager.path"),
+               appmgrNotifUrl:       app.Config.GetString("controls.appManager.notificationUrl"),
+               appmgrSubsUrl:        app.Config.GetString("controls.appManager.subscriptionUrl"),
+               appmgrRetry:          app.Config.GetInt("controls.appManager.appmgrRetry"),
+               hbInterval:           app.Config.GetString("controls.vesagent.hbInterval"),
+               measInterval:         app.Config.GetString("controls.vesagent.measInterval"),
+               prometheusAddr:       app.Config.GetString("controls.vesagent.prometheusAddr"),
+               alertManagerBindAddr: app.Config.GetString("controls.vesagent.alertManagerBindAddr"),
+       }
+}
+
+func (v *VespaMgr) Run(sdlcheck, runXapp bool) {
+       app.Logger.SetMdc("vespamgr", fmt.Sprintf("%s:%s", Version, Hash))
+       app.SetReadyCB(func(d interface{}) { v.rmrReady = true }, true)
+       app.Resource.InjectStatusCb(v.StatusCB)
+       app.AddConfigChangeListener(v.ConfigChangeCB)
+
+       measUrl := app.Config.GetString("controls.measurementUrl")
+       app.Resource.InjectRoute(v.appmgrNotifUrl, v.HandlexAppNotification, "POST")
+       app.Resource.InjectRoute(measUrl, v.HandleMeasurements, "POST")
+       app.Resource.InjectRoute("/supervision", v.HandleSupervision, "GET") // @todo: remove this
+
+       go v.SubscribeXappNotif(fmt.Sprintf("%s%s", v.appmgrHost, v.appmgrSubsUrl))
+
+       if runXapp {
+               app.RunWithParams(v, sdlcheck)
+       }
+}
+
+func (v *VespaMgr) Consume(rp *app.RMRParams) (err error) {
+       app.Logger.Info("Message received!")
+
+       app.Rmr.Free(rp.Mbuf)
+       return nil
+}
+
+func (v *VespaMgr) StatusCB() bool {
+       if !v.rmrReady {
+               app.Logger.Info("RMR not ready yet!")
+       }
+
+       return v.rmrReady
+}
+
+func (v *VespaMgr) ConfigChangeCB(configparam string) {
+       return
+}
+
+func (v *VespaMgr) CreateConf(fname string, xappMetrics []byte) {
+       f, err := os.Create(fname)
+       if err != nil {
+               app.Logger.Error("os.Create failed: %s", err.Error())
+               return
+       }
+       defer f.Close()
+
+       v.CreateConfig(f, xappMetrics)
+}
+
+func (v *VespaMgr) QueryXappConf(appmgrUrl string) (appConfig []byte, err error) {
+       client := http.Client{Timeout: 10 * time.Second}
+
+       for i := 0; i < v.appmgrRetry; i++ {
+               app.Logger.Info("Getting xApp config from: %s [%d]", appmgrUrl, v.appmgrRetry)
+
+               resp, err := client.Get(appmgrUrl)
+               if err != nil || resp == nil {
+                       app.Logger.Error("client.Get failed: %v", err)
+                       time.Sleep(5 * time.Second)
+                       continue
+               }
+
+               defer resp.Body.Close()
+               appConfig, err := ioutil.ReadAll(resp.Body)
+               if err != nil {
+                       app.Logger.Error("ioutil.ReadAll failed: %v", err)
+                       time.Sleep(5 * time.Second)
+                       continue
+               }
+
+               app.Logger.Info("Received xApp config: %d", len(appConfig))
+               if len(appConfig) > 0 {
+                       return appConfig, err
+               }
+       }
+
+       return appConfig, err
+}
+
+func (v *VespaMgr) ReadPayload(w http.ResponseWriter, r *http.Request) ([]byte, error) {
+       payload, err := ioutil.ReadAll(r.Body)
+       defer r.Body.Close()
+       if err != nil {
+               app.Logger.Error("ioutil.ReadAll failed: %v", err)
+               return payload, err
+       }
+       v.respondWithJSON(w, http.StatusOK, err)
+
+       return payload, err
+}
+
+func (v *VespaMgr) HandleSupervision(w http.ResponseWriter, r *http.Request) {
+       v.respondWithJSON(w, http.StatusOK, nil)
+}
+
+func (v *VespaMgr) HandleMeasurements(w http.ResponseWriter, r *http.Request) {
+       app.Logger.Info("HandleMeasurements called!")
+       if appConfig, err := v.ReadPayload(w, r); err == nil {
+               v.CreateConf(app.Config.GetString("controls.vesagent.configFile"), appConfig)
+       }
+}
+
+func (v *VespaMgr) HandlexAppNotification(w http.ResponseWriter, r *http.Request) {
+       if _, err := v.ReadPayload(w, r); err != nil {
+               return
+       }
+
+       app.Logger.Info("xApp event notification received!")
+       if appConfig, err := v.QueryXappConf(fmt.Sprintf("%s%s", v.appmgrHost, v.appmgrUrl)); err == nil {
+               v.CreateConf(app.Config.GetString("controls.vesagent.configFile"), appConfig)
+               v.RestartVesagent()
+       }
+}
+
+func (v *VespaMgr) DoSubscribe(appmgrUrl string, subscriptionData []byte) string {
+       resp, err := http.Post(appmgrUrl, "application/json", bytes.NewBuffer(subscriptionData))
+       if err != nil || resp == nil || resp.StatusCode != http.StatusCreated {
+               app.Logger.Error("http.Post failed: %s", err)
+               return ""
+       }
+
+       body, err := ioutil.ReadAll(resp.Body)
+       if err != nil {
+               app.Logger.Error("ioutil.ReadAll for body failed: %s", err)
+               return ""
+       }
+
+       var result map[string]interface{}
+       if err := json.Unmarshal([]byte(body), &result); err != nil {
+               app.Logger.Error("json.Unmarshal failed: %s", err)
+               return ""
+       }
+       v.subscriptionId = result["id"].(string)
+       app.Logger.Info("Subscription id from the response: %s", v.subscriptionId)
+
+       return v.subscriptionId
+}
+
+func (v *VespaMgr) SubscribeXappNotif(appmgrUrl string) {
+       targetUrl := fmt.Sprintf("%s%s", app.Config.GetString("controls.host"), v.appmgrNotifUrl)
+       subscriptionData := []byte(fmt.Sprintf(`{"Data": {"maxRetries": 5, "retryTimer": 5, "eventType":"all", "targetUrl": "%v"}}`, targetUrl))
+
+       for {
+               app.Logger.Info("Subscribing xApp notification from: %v", appmgrUrl)
+
+               if id := v.DoSubscribe(appmgrUrl, subscriptionData); id != "" {
+                       app.Logger.Info("Subscription done, id=%s", id)
+                       break
+               }
+
+               app.Logger.Info("Subscription failed, retyring after short delay ...")
+               time.Sleep(5 * time.Second)
+       }
+
+       if xappConfig, err := v.QueryXappConf(fmt.Sprintf("%s%s", v.appmgrHost, v.appmgrUrl)); err == nil {
+               v.CreateConf(app.Config.GetString("controls.vesagent.configFile"), xappConfig)
+               v.RestartVesagent()
+       }
+}
+
+func (v *VespaMgr) 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 (v *VespaMgr) StartVesagent() {
+       v.vesAgent = NewCommandRunner("ves-agent", "-i", v.hbInterval, "-m", v.measInterval, "--Debug",
+               "--Measurement.Prometheus.Address", v.prometheusAddr, "--AlertManager.Bind", v.alertManagerBindAddr)
+
+       v.vesAgent.Run(v.chVesagent)
+}
+
+func (v *VespaMgr) RestartVesagent() {
+       if strings.Contains(app.Config.GetString("controls.host"), "localhost") {
+               return
+       }
+
+       if v.vesAgent != nil {
+               err := v.vesAgent.Kill()
+               if err != nil {
+                       app.Logger.Error("Couldn't kill vespa-agent: %s", err.Error())
+                       return
+               }
+               <-v.chVesagent
+       }
+
+       v.StartVesagent()
+}
+
+func main() {
+       NewVespaMgr().Run(false, true)
+}
old mode 100644 (file)
new mode 100755 (executable)
similarity index 51%
rename from cmd/vesmgr/subscribexAPPNotifications_test.go
rename to cmd/vespamgr/vespamgr_test.go
index 1521da3..2b84ef5
@@ -26,33 +26,32 @@ import (
        "encoding/json"
        "fmt"
        "io/ioutil"
+       "net"
        "net/http"
        "net/http/httptest"
+       "os"
        "testing"
+       "time"
 
+       app "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
        "github.com/stretchr/testify/suite"
 )
 
-type AppmgrHTTPServerTestSuite struct {
+type VespaMgrTestSuite struct {
        suite.Suite
-       subscriptions chan subscriptionNotification
-       xappNotifURL  string
+       vespaMgr *VespaMgr
 }
 
 // suite setup
-func (suite *AppmgrHTTPServerTestSuite) SetupSuite() {
-       // the url here is not actually used anywhere
-       suite.xappNotifURL = "http://127.0.0.1:8080" + vesmgrXappNotifPath
-       suite.subscriptions = make(chan subscriptionNotification)
+func (suite *VespaMgrTestSuite) SetupSuite() {
+       os.Unsetenv("http_proxy")
+       os.Unsetenv("HTTP_PROXY")
+       suite.vespaMgr = NewVespaMgr()
 }
 
-// test setup
-func (suite *AppmgrHTTPServerTestSuite) SetupTest() {
-       suite.subscriptions = make(chan subscriptionNotification)
-}
-
-func (suite *AppmgrHTTPServerTestSuite) TestSubscribexAppNotifications() {
+func (suite *VespaMgrTestSuite) TestSubscribexAppNotifications() {
        testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
+
                body, _ := ioutil.ReadAll(req.Body)
                var result map[string]interface{}
                err := json.Unmarshal([]byte(body), &result)
@@ -68,13 +67,11 @@ func (suite *AppmgrHTTPServerTestSuite) TestSubscribexAppNotifications() {
        }))
        defer testServer.Close()
 
-       go subscribexAppNotifications(suite.xappNotifURL, suite.subscriptions, 1, testServer.URL)
-       isSubscribed := <-suite.subscriptions
-       suite.Nil(isSubscribed.err)
-       suite.Equal("deadbeef1234567890", isSubscribed.subsID)
+       suite.vespaMgr.SubscribeXappNotif(testServer.URL)
+       suite.Equal("deadbeef1234567890", suite.vespaMgr.subscriptionId)
 }
 
-func (suite *AppmgrHTTPServerTestSuite) TestSubscribexAppNotificationsWrongStatus() {
+func (suite *VespaMgrTestSuite) TestSubscribexAppNotificationsWrongStatus() {
        testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
                res.Header().Add("Content-Type", "application/json")
                res.WriteHeader(http.StatusUnauthorized)
@@ -82,32 +79,16 @@ func (suite *AppmgrHTTPServerTestSuite) TestSubscribexAppNotificationsWrongStatu
        }))
        defer testServer.Close()
 
-       requestBody := []byte(fmt.Sprintf(`{"maxRetries": 5, "retryTimer": 5, "eventType":"all", "targetUrl": "%v"}`, suite.xappNotifURL))
+       requestBody := []byte(fmt.Sprintf(`{"maxRetries": 5, "retryTimer": 5, "eventType":"all", "targetUrl": "%v"}`, "localhost:8080"))
        req, _ := http.NewRequest("POST", testServer.URL, bytes.NewBuffer(requestBody))
        req.Header.Set("Content-Type", "application/json")
-       client := &http.Client{}
-
-       subsID, err := subscribexAppNotificationsClientDo(req, client)
-       suite.Equal(errWrongStatusCode, err)
-       // after failed POST vesmgr.appmgrSubsId holds an initial values
-       suite.Equal("", subsID)
-}
-
-func (suite *AppmgrHTTPServerTestSuite) TestSubscribexAppNotificationsWrongUrl() {
-       // use fake appmgrUrl that is not served in unit test
-       appmgrURL := "/I_do_not_exist/"
-       requestBody := []byte(fmt.Sprintf(`{"maxRetries": 5, "retryTimer": 5, "eventType":"all", "targetUrl": "%v"}`, suite.xappNotifURL))
-       req, _ := http.NewRequest("POST", appmgrURL, bytes.NewBuffer(requestBody))
-       req.Header.Set("Content-Type", "application/json")
-       client := &http.Client{}
 
-       subsID, err := subscribexAppNotificationsClientDo(req, client)
-       suite.Equal(errPostingFailed, err)
-       // after failed POST vesmgr.appmgrSubsId holds an initial values
-       suite.Equal("", subsID)
+       suite.vespaMgr.subscriptionId = ""
+       suite.vespaMgr.DoSubscribe(testServer.URL, requestBody)
+       suite.Equal("", suite.vespaMgr.subscriptionId)
 }
 
-func (suite *AppmgrHTTPServerTestSuite) TestSubscribexAppNotificationsReadBodyFails() {
+func (suite *VespaMgrTestSuite) TestSubscribexAppNotificationsReadBodyFails() {
        testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
                res.Header().Set("Content-Length", "1")
                res.Header().Add("Content-Type", "application/json")
@@ -115,13 +96,12 @@ func (suite *AppmgrHTTPServerTestSuite) TestSubscribexAppNotificationsReadBodyFa
        }))
        defer testServer.Close()
 
-       go subscribexAppNotifications(suite.xappNotifURL, suite.subscriptions, 1, testServer.URL)
-       isSubscribed := <-suite.subscriptions
-       suite.Equal("unexpected EOF", isSubscribed.err.Error())
-       suite.Equal("", isSubscribed.subsID)
+       suite.vespaMgr.subscriptionId = ""
+       suite.vespaMgr.DoSubscribe(testServer.URL, []byte{})
+       suite.Equal("", suite.vespaMgr.subscriptionId)
 }
 
-func (suite *AppmgrHTTPServerTestSuite) TestSubscribexAppNotificationsUnMarshalFails() {
+func (suite *VespaMgrTestSuite) TestSubscribexAppNotificationsUnMarshalFails() {
        testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
                res.Header().Add("Content-Type", "application/json")
                res.WriteHeader(http.StatusCreated)
@@ -129,12 +109,57 @@ func (suite *AppmgrHTTPServerTestSuite) TestSubscribexAppNotificationsUnMarshalF
        }))
        defer testServer.Close()
 
-       go subscribexAppNotifications(suite.xappNotifURL, suite.subscriptions, 1, testServer.URL)
-       isSubscribed := <-suite.subscriptions
-       suite.Equal("invalid character 'd' after object key", isSubscribed.err.Error())
-       suite.Equal("", isSubscribed.subsID)
+       suite.vespaMgr.subscriptionId = ""
+       suite.vespaMgr.DoSubscribe(testServer.URL, []byte{})
+       suite.Equal("", suite.vespaMgr.subscriptionId)
+}
+
+func (suite *VespaMgrTestSuite) TestQueryXAppsConfigOk() {
+       listener, err := net.Listen("tcp", ":0")
+       suite.Nil(err)
+
+       http.HandleFunc("/test_url/", func(w http.ResponseWriter, r *http.Request) {
+               switch r.Method {
+               case "GET":
+                       fmt.Fprintf(w, "reply message")
+               }
+       })
+
+       go http.Serve(listener, nil)
+
+       xappConfig, err := suite.vespaMgr.QueryXappConf("http://" + listener.Addr().String() + "/test_url/")
+       suite.NotNil(xappConfig)
+       suite.Nil(err)
+       suite.Equal(xappConfig, []byte("reply message"))
+}
+
+func (suite *VespaMgrTestSuite) TestHandlexAppNotification() {
+       data, err := ioutil.ReadFile("../../test/xApp_config_test_output.json")
+       suite.Nil(err)
+
+       pbodyEn, _ := json.Marshal(data)
+       req, _ := http.NewRequest("POST", "/ric/v1/xappnotif", bytes.NewBuffer(pbodyEn))
+       handleFunc := http.HandlerFunc(suite.vespaMgr.HandlexAppNotification)
+       response := executeRequest(req, handleFunc)
+       suite.Equal(http.StatusOK, response.Code)
+}
+
+func (suite *VespaMgrTestSuite) TestSubscribexAppNotificationsOnStartup() {
+       suite.vespaMgr.Run(false, false)
+       time.Sleep(2 * time.Second)
+
+       suite.vespaMgr.Consume(&app.RMRParams{})
+       suite.vespaMgr.StatusCB()
+       suite.vespaMgr.rmrReady = true
+       suite.vespaMgr.StatusCB()
+}
+
+func executeRequest(req *http.Request, handleR http.HandlerFunc) *httptest.ResponseRecorder {
+       rr := httptest.NewRecorder()
+       handleR.ServeHTTP(rr, req)
+       return rr
 }
 
-func TestAppmgrHttpServerTestSuite(t *testing.T) {
-       suite.Run(t, new(AppmgrHTTPServerTestSuite))
+func TestVespaMgrTestSuite(t *testing.T) {
+       suite.Run(t, new(VespaMgrTestSuite))
 }
diff --git a/config/config-file-ut.json b/config/config-file-ut.json
new file mode 100755 (executable)
index 0000000..497242f
--- /dev/null
@@ -0,0 +1,83 @@
+{
+    "name": "vespamgr",
+    "version": "0.7.0",
+    "vendor": "Nokia",
+    "moId": "SEP",
+    "containers": [],
+    "livenessProbe": {
+        "httpGet": {
+            "path": "ric/v1/health/alive",
+            "port": 8080
+        },
+        "initialDelaySeconds": 5,
+        "periodSeconds": 15
+    },
+    "readinessProbe": {
+        "httpGet": {
+            "path": "ric/v1/health/ready",
+            "port": 8080
+        },
+        "initialDelaySeconds": 5,
+        "periodSeconds": 15
+    },
+    "messaging": {
+        "ports": [
+            {
+                "name": "http",
+                "container": "qos",
+                "port": 8088,
+                "description": "http service"
+            },
+            {
+                "name": "rmr-data",
+                "container": "qos",
+                "port": 4560,
+                "maxSize": 2072,
+                "threadType": 0,
+                "lowLatency": false,
+                "rxMessages": [],
+                "txMessages": [],
+                "policies": [],
+                "description": "rmr data port for qos"
+            },
+            {
+                "name": "rmr-route",
+                "container": "qos",
+                "port": 4561,
+                "description": "rmr route port for qos"
+            }
+        ]
+    },
+    "controls": {
+        "logger": {
+            "level": 4
+        },
+        "host": "localhost:8080",
+        "measurementUrl": "/ric/v1/measurements",
+        "appManager": {
+            "host": "http://localhost:8080",
+            "path": "/ric/v1/config",
+            "notificationUrl": "/ric/v1/xappnotif",
+            "subscriptionUrl": "/ric/v1/subscriptions",
+            "appmgrRetry": 2
+        },
+        "vesagent": {
+            "configFile": "/tmp/ves-agent.yaml",
+            "hbInterval": "60s",
+            "measInterval": "30s",
+            "prometheusAddr": "http://infra-cpro-server:80",
+            "alertManagerBindAddr": ":9095"
+        },
+        "collector": {
+            "primaryAddr": "localhost",
+            "secondaryAddr": "localhost",
+            "serverRoot": "0",
+            "primaryPort": 8443,
+            "primaryUser": "sample1",
+            "primaryPassword": "sample1",
+            "secure": false
+        }
+    },
+    "faults": { },
+    "metrics": []
+}
diff --git a/config/config-file.json b/config/config-file.json
new file mode 100755 (executable)
index 0000000..eead269
--- /dev/null
@@ -0,0 +1,83 @@
+{
+    "name": "vespamgr",
+    "version": "0.7.0",
+    "vendor": "Nokia",
+    "moId": "SEP",
+    "containers": [],
+    "livenessProbe": {
+        "httpGet": {
+            "path": "ric/v1/health/alive",
+            "port": 8080
+        },
+        "initialDelaySeconds": 5,
+        "periodSeconds": 15
+    },
+    "readinessProbe": {
+        "httpGet": {
+            "path": "ric/v1/health/ready",
+            "port": 8080
+        },
+        "initialDelaySeconds": 5,
+        "periodSeconds": 15
+    },
+    "messaging": {
+        "ports": [
+            {
+                "name": "http",
+                "container": "qos",
+                "port": 8080,
+                "description": "http service"
+            },
+            {
+                "name": "rmr-data",
+                "container": "qos",
+                "port": 4560,
+                "maxSize": 2072,
+                "threadType": 0,
+                "lowLatency": false,
+                "rxMessages": [],
+                "txMessages": [],
+                "policies": [],
+                "description": "rmr data port for qos"
+            },
+            {
+                "name": "rmr-route",
+                "container": "qos",
+                "port": 4561,
+                "description": "rmr route port for qos"
+            }
+        ]
+    },
+    "controls": {
+        "logger": {
+            "level": 4
+        },
+        "host": "http://service-ricplt-vespamgr-http.ricplt.svc.cluster.local:8080",
+        "measurementUrl": "/ric/v1/measurements",
+        "appManager": {
+            "host": "http://service-ricplt-appmgr-http.ricplt.svc.cluster.local:8080",
+            "path": "/ric/v1/config",
+            "notificationUrl": "/ric/v1/xappnotif",
+            "subscriptionUrl": "/ric/v1/subscriptions",
+            "appmgrRetry": 2
+        },
+        "vesagent": {
+            "configFile": "/etc/ves-agent/ves-agent.yaml",
+            "hbInterval": "60s",
+            "measInterval": "30s",
+            "prometheusAddr": "http://infra-cpro-server:80",
+            "alertManagerBindAddr": ":9095"
+        },
+        "collector": {
+            "primaryAddr": "pod-ves-simulator",
+            "secondaryAddr": "pod-ves-simulator",
+            "serverRoot": "0",
+            "primaryPort": 8443,
+            "primaryUser": "sample1",
+            "primaryPassword": "sample1",
+            "secure": false
+        }
+    },
+    "faults": { },
+    "metrics": []
+}
\ No newline at end of file
diff --git a/config/uta_rtg.rt b/config/uta_rtg.rt
new file mode 100644 (file)
index 0000000..900f071
--- /dev/null
@@ -0,0 +1,4 @@
+newrt|start
+rte|13111|127.0.0.1:4588
+rte|13111|127.0.0.1:4560
+newrt|end
index 795ef43..31badd2 100644 (file)
@@ -1,4 +1,4 @@
 # The Jenkins job uses this string for the tag in the image name
 # for example nexus3.o-ran-sc.org:10004/my-image-name:0.0.1
 ---
-tag: 0.5.4
+tag: 0.7.0
diff --git a/go.mod b/go.mod
index 10cf8df..c4111ca 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -1,10 +1,16 @@
-module RIC_VESAgent
+module gerrit.o-ran-sc.org/r/ric-plt/vespamgr
 
-go 1.12
+go 1.13
+
+replace gerrit.o-ran-sc.org/r/ric-plt/xapp-frame => gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.6.7
+
+replace gerrit.o-ran-sc.org/r/ric-plt/sdlgo => gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.5.2
+
+replace gerrit.o-ran-sc.org/r/com/golog => gerrit.o-ran-sc.org/r/com/golog.git v0.0.2
 
 require (
        gerrit.o-ran-sc.org/r/com/golog.git v0.0.1
-       github.com/davecgh/go-spew v1.1.1 // indirect
-       github.com/stretchr/testify v1.3.0
-       gopkg.in/yaml.v2 v2.2.2
+       gerrit.o-ran-sc.org/r/ric-plt/xapp-frame v0.0.0-00010101000000-000000000000
+       github.com/stretchr/testify v1.5.1
+       gopkg.in/yaml.v2 v2.2.4
 )
diff --git a/go.sum b/go.sum
index 94304a2..6cf4488 100644 (file)
--- a/go.sum
+++ b/go.sum
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 gerrit.o-ran-sc.org/r/com/golog.git v0.0.1 h1:9RfO/Whehaaq5KiJTT7s+YOzmi9mT1C3HktfhwwMEmw=
 gerrit.o-ran-sc.org/r/com/golog.git v0.0.1/go.mod h1:b8YB31U8/4iRpABioeSzGi/YMzOQ/Zq7hrJmmXKqlJk=
+gerrit.o-ran-sc.org/r/com/golog.git v0.0.2 h1:Ix6SgFuzd6yW6Ur6+qDlGhDO65UYs8PiIkeAL1VaQ2o=
+gerrit.o-ran-sc.org/r/com/golog.git v0.0.2/go.mod h1:A7hUL52YQSO4dFIZNcj76XQ09C9PftAe3LyL7kqBnok=
+gerrit.o-ran-sc.org/r/ric-plt/alarm-go.git/alarm v0.4.0 h1:Uvyfuq2jyb1aosy2BEzH1g3pe2gy4sMgpsXlvjVeBzI=
+gerrit.o-ran-sc.org/r/ric-plt/alarm-go.git/alarm v0.4.0/go.mod h1:AdEWKtERGvOQy9ybLhyhrb9w9LLVn8i9xzTwoR5n4BY=
+gerrit.o-ran-sc.org/r/ric-plt/alarm-go.git/alarm v0.4.2 h1:XNfkp3PwZ7pfkPszX7NaX6DzToCGjcWTLbIHYqCFNu0=
+gerrit.o-ran-sc.org/r/ric-plt/alarm-go.git/alarm v0.4.2/go.mod h1:AdEWKtERGvOQy9ybLhyhrb9w9LLVn8i9xzTwoR5n4BY=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/common v1.0.21 h1:eK9nUZOTMJ/EnMpH9bkWtMgOvCn3u4+PNCb9gu10s6w=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/common v1.0.21/go.mod h1:QJ1uPPZosGbhxUWpUpeM5fLqFHdnWTrVnvW2DgyOCes=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/common v1.0.35 h1:TGXHb4DNY8on+ej4S9VUnk2HibIC/5chDy64OE+bQBQ=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/common v1.0.35/go.mod h1:QJ1uPPZosGbhxUWpUpeM5fLqFHdnWTrVnvW2DgyOCes=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/entities v1.0.21 h1:PQ/Mu2ol+8Oh/0BqCWWhPlVVoRCg5dQDEGm4+Opp5w4=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/entities v1.0.21/go.mod h1:GXiXLz4ORBeIr0FLIbzENRykgh3Po5uPkX2jICxnRF0=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/entities v1.0.35 h1:tkM3yE8UzmuH4nf9TqAmiNBSuIZ2CtcMRH2eBIYIzpQ=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/entities v1.0.35/go.mod h1:G+4sUBMbLfQ+RrGS65U15tKmbnP+/1b5oLTPmMfyfT4=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/reader v1.0.21 h1:N3UbqJ9WqC8JEz/TwHHwZwCFAW6VTlZLpD5lnbdD+Y8=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/reader v1.0.21/go.mod h1:SQBZLy1HP94i1vQ3y730wGFsrHqZtgPaEkzPgtqBNw0=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/reader v1.0.35 h1:LcxnUUDwsCzYEISKmkjkyYfg/lnLt8ofkPiGK69vNIA=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/reader v1.0.35/go.mod h1:2bSaXTpECbZieB8bMnubTqMwF3n+mMBxlTaAXvcduNg=
+gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.5.2 h1:UK7awyRKIkVdokWvvkYvazlg3EWIfMnIqCcJxTnLlDA=
+gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.5.2/go.mod h1:y2WhrCvdLkAKdH+ySdHSOSehACJkTMyZghCGVcqoZzc=
+gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.4.0 h1:FGQY+g9r+xxqNrU2V0VvwcpibAEgRnIAN43+4ULC8qE=
+gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.4.0/go.mod h1:lW3UYpVXwPiOR39t6JgNCE4kdmMSdPVelMPC/Pp9fQM=
+gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.4.4 h1:aUsIGYCHYsNX0pQxLL+sLTOzBLCaQ1p2/tMY3UEYcMs=
+gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.4.4/go.mod h1:29CdUcCrNEqVBjQWAlgeGrYxSWqSZ4e67xzhuSf6BkI=
+gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.6.7 h1:lTURRbfaV0kPcplGF7jyowRXoUkNknRzd7Y51ZPs5PI=
+gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.6.7/go.mod h1:fpHeoGISAkz6bNfgZtq8Ycg9i9KdGgUBP2jLs6TGk2g=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
+github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
+github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
+github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
+github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
+github.com/go-openapi/analysis v0.19.5 h1:8b2ZgKfKIUTVQpTb77MoRDIMEIwvDVw40o3aOXdfYzI=
+github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
+github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
+github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
+github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
+github.com/go-openapi/errors v0.19.3 h1:7MGZI1ibQDLasvAz8HuhvYk9eNJbJkCOXWsSjjMS+Zc=
+github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
+github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
+github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
+github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
+github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
+github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
+github.com/go-openapi/loads v0.19.4 h1:5I4CCSqoWzT+82bBkNIvmLc0UOsoKKQ4Fz+3VxOB7SY=
+github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
+github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
+github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
+github.com/go-openapi/runtime v0.19.4 h1:csnOgcgAiuGoM/Po7PEpKDoNulCcF3FGbSnbHfxgjMI=
+github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
+github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
+github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
+github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
+github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
+github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
+github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
+github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
+github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
+github.com/go-openapi/strfmt v0.19.4 h1:eRvaqAhpL0IL6Trh5fDsGnGhiXndzHFuA05w6sXH6/g=
+github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
+github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
+github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.7 h1:VRuXN2EnMSsZdauzdss6JBC29YotDqG59BZ+tdlIL1s=
+github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
+github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
+github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
+github.com/go-openapi/validate v0.19.6 h1:WsKw9J1WzYBVxWRYwLqEk3325RL6G0SSWksuamkk6q0=
+github.com/go-openapi/validate v0.19.6/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
+github.com/go-redis/redis v6.15.3+incompatible h1:NZ0O90AhLSvSrvLZ/S9h7D4kl1mW2PrKyxL7MyBKO2g=
+github.com/go-redis/redis v6.15.3+incompatible/go.mod h1:W2YCLaZryXHirdd9QqwkiVUxCQsrx8SbLq9Uqk7JS7A=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+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/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+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/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
+github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+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/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+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/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+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/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+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.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
+github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.1.2 h1:jxcFYjlkl8xaERsgLo+RNquI0epW6zuy/ZRQs6jnrFA=
+go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 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/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+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=
+gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
+k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=