Refactor the code
Multiple code refactorings to make the code
simpler and increase the unit testing coverage.
* Golint findings are fixed.
* Xapp query function name changed from status to
config.
The functionality is slightly changed so that
- Vespa is started only after the xapp notification
subscription is successful and the vesmgr has received
the current xapp configuration from the xapp manager.
- The vesmgr goes to the main loop after that.
Change-Id: Ie2675c0543d4e4ce0a60b92a6c06a79b9e2cb2cd
Signed-off-by: Roni Riska <roni.riska@nokia.com>
diff --git a/cmd/vesmgr/config.go b/cmd/vesmgr/config.go
index e2c9f25..8301b0c 100644
--- a/cmd/vesmgr/config.go
+++ b/cmd/vesmgr/config.go
@@ -19,11 +19,12 @@
import (
"encoding/json"
- "gopkg.in/yaml.v2"
"io"
"os"
"strconv"
"time"
+
+ "gopkg.in/yaml.v2"
)
func basicVespaConf() VESAgentConfiguration {
@@ -65,12 +66,13 @@
return vespaconf
}
+// AppMetricsStruct contains xapplication metrics definition
type AppMetricsStruct struct {
ObjectName string
ObjectInstance string
- // xxx add labels here
}
+// 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
@@ -89,10 +91,10 @@
json.Unmarshal(descriptor, &desc)
for _, app := range desc {
- config, config_ok := app["config"]
- if config_ok {
- metrics, metrics_ok := config.(map[string]interface{})["metrics"]
- if metrics_ok {
+ config, configOk := app["config"]
+ if configOk {
+ metrics, metricsOk := config.(map[string]interface{})["metrics"]
+ if metricsOk {
parseMetricsRules(metrics.([]interface{}), appMetrics)
}
}
@@ -106,16 +108,16 @@
// Entries, which do not have all the necessary fields, are ignored.
func parseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics) AppMetrics {
for _, element := range metricsMap {
- name, name_ok := element.(map[string]interface{})["name"].(string)
- if name_ok {
- _, already_found := appMetrics[name]
- objectName, objectName_ok := element.(map[string]interface{})["objectName"].(string)
- objectInstance, objectInstance_ok := element.(map[string]interface{})["objectInstance"].(string)
- if !already_found && objectName_ok && objectInstance_ok {
+ name, nameOk := element.(map[string]interface{})["name"].(string)
+ if nameOk {
+ _, alreadyFound := appMetrics[name]
+ objectName, objectNameOk := element.(map[string]interface{})["objectName"].(string)
+ objectInstance, objectInstanceOk := element.(map[string]interface{})["objectInstance"].(string)
+ if !alreadyFound && objectNameOk && objectInstanceOk {
appMetrics[name] = AppMetricsStruct{objectName, objectInstance}
logger.Info("parsed counter %s %s %s", name, objectName, objectInstance)
}
- if already_found {
+ if alreadyFound {
logger.Info("skipped duplicate counter %s", name)
}
}
@@ -127,12 +129,12 @@
appMetrics := make(AppMetrics)
parseMetricsFromXAppDescriptor(xAppConfig, appMetrics)
- makeRule := func(expr string, obj_name string, obj_instance string) MetricRule {
+ makeRule := func(expr string, objName string, objInstance string) MetricRule {
return MetricRule{
Target: "AdditionalObjects",
Expr: expr,
- ObjectInstance: obj_instance,
- ObjectName: obj_name,
+ ObjectInstance: objInstance,
+ ObjectName: objName,
ObjectKeys: []Label{
Label{
Name: "ricComponentName",
@@ -161,15 +163,15 @@
vespaconf.PrimaryCollector.FQDN = os.Getenv("VESMGR_PRICOLLECTOR_ADDR")
vespaconf.PrimaryCollector.ServerRoot = os.Getenv("VESMGR_PRICOLLECTOR_SERVERROOT")
vespaconf.PrimaryCollector.Topic = os.Getenv("VESMGR_PRICOLLECTOR_TOPIC")
- port_str := os.Getenv("VESMGR_PRICOLLECTOR_PORT")
- if port_str == "" {
+ portStr := os.Getenv("VESMGR_PRICOLLECTOR_PORT")
+ if portStr == "" {
vespaconf.PrimaryCollector.Port = 8443
} else {
- port, _ := strconv.Atoi(port_str)
+ port, _ := strconv.Atoi(portStr)
vespaconf.PrimaryCollector.Port = port
}
- secure_str := os.Getenv("VESMGR_PRICOLLECTOR_SECURE")
- if secure_str == "true" {
+ secureStr := os.Getenv("VESMGR_PRICOLLECTOR_SECURE")
+ if secureStr == "true" {
vespaconf.PrimaryCollector.Secure = true
} else {
vespaconf.PrimaryCollector.Secure = false
diff --git a/cmd/vesmgr/config_test.go b/cmd/vesmgr/config_test.go
index 08bde19..76793a1 100644
--- a/cmd/vesmgr/config_test.go
+++ b/cmd/vesmgr/config_test.go
@@ -19,12 +19,13 @@
import (
"bytes"
"encoding/json"
- "github.com/stretchr/testify/assert"
- "gopkg.in/yaml.v2"
"io/ioutil"
"os"
"testing"
"time"
+
+ "github.com/stretchr/testify/assert"
+ "gopkg.in/yaml.v2"
)
func testBaseConf(t *testing.T, vesconf VESAgentConfiguration) {
@@ -130,7 +131,7 @@
}
func TestParseMetricsRules(t *testing.T) {
- metricsJson := `{"metrics": [
+ metricsJSON := `{"metrics": [
{ "name": "ricxapp_RMR_Received", "objectName": "ricxappRMRreceivedCounter", "objectInstance": "ricxappRMRReceived" },
{ "name": "ricxapp_RMR_ReceiveError", "objectName": "ricxappRMRReceiveErrorCounter", "objectInstance": "ricxappRMRReceiveError" },
{ "name": "ricxapp_RMR_Transmitted", "objectName": "ricxappRMRTransmittedCounter", "objectInstance": "ricxappRMRTransmitted" },
@@ -138,7 +139,7 @@
{ "name": "ricxapp_SDL_Stored", "objectName": "ricxappSDLStoredCounter", "objectInstance": "ricxappSDLStored" },
{ "name": "ricxapp_SDL_StoreError", "objectName": "ricxappSDLStoreErrorCounter", "objectInstance": "ricxappSDLStoreError" } ]}`
appMetrics := make(AppMetrics)
- var m []interface{} = metricsStringToInterfaceArray(metricsJson)
+ m := metricsStringToInterfaceArray(metricsJSON)
appMetrics = parseMetricsRules(m, appMetrics)
assert.Len(t, appMetrics, 6)
assert.Equal(t, "ricxappRMRreceivedCounter", appMetrics["ricxapp_RMR_Received"].ObjectName)
@@ -148,17 +149,17 @@
func TestParseMetricsRulesNoMetrics(t *testing.T) {
appMetrics := make(AppMetrics)
- metricsJson := `{"metrics": []`
- var m []interface{} = metricsStringToInterfaceArray(metricsJson)
+ metricsJSON := `{"metrics": []`
+ m := metricsStringToInterfaceArray(metricsJSON)
appMetrics = parseMetricsRules(m, appMetrics)
assert.Empty(t, appMetrics)
}
func TestParseMetricsRulesAdditionalFields(t *testing.T) {
appMetrics := make(AppMetrics)
- metricsJson := `{"metrics": [
+ metricsJSON := `{"metrics": [
{ "additionalField": "valueIgnored", "name": "ricxapp_RMR_Received", "objectName": "ricxappRMRreceivedCounter", "objectInstance": "ricxappRMRReceived" }]}`
- var m []interface{} = metricsStringToInterfaceArray(metricsJson)
+ m := metricsStringToInterfaceArray(metricsJSON)
appMetrics = parseMetricsRules(m, appMetrics)
assert.Len(t, appMetrics, 1)
assert.Equal(t, "ricxappRMRreceivedCounter", appMetrics["ricxapp_RMR_Received"].ObjectName)
@@ -167,11 +168,11 @@
func TestParseMetricsRulesMissingFields(t *testing.T) {
appMetrics := make(AppMetrics)
- metricsJson := `{"metrics": [
+ metricsJSON := `{"metrics": [
{ "name": "ricxapp_RMR_Received", "objectName": "ricxappRMRreceivedCounter", "objectInstance": "ricxappRMRReceived" },
{ "name": "ricxapp_RMR_ReceiveError", "objectInstance": "ricxappRMRReceiveError" },
{ "name": "ricxapp_RMR_Transmitted", "objectName": "ricxappRMRTransmittedCounter", "objectInstance": "ricxappRMRTransmitted" }]}`
- var m []interface{} = metricsStringToInterfaceArray(metricsJson)
+ m := metricsStringToInterfaceArray(metricsJSON)
appMetrics = parseMetricsRules(m, appMetrics)
assert.Len(t, appMetrics, 2)
assert.Equal(t, "ricxappRMRreceivedCounter", appMetrics["ricxapp_RMR_Received"].ObjectName)
@@ -182,11 +183,11 @@
func TestParseMetricsRulesDuplicateDefinitionIsIgnored(t *testing.T) {
appMetrics := make(AppMetrics)
- metricsJson := `{"metrics": [
+ metricsJSON := `{"metrics": [
{ "name": "ricxapp_RMR_Received", "objectName": "ricxappRMRreceivedCounter", "objectInstance": "ricxappRMRReceived" },
{ "name": "ricxapp_RMR_Received", "objectName": "ricxappRMRreceivedCounterXXX", "objectInstance": "ricxappRMRReceivedXXX" },
{ "name": "ricxapp_RMR_Transmitted", "objectName": "ricxappRMRTransmittedCounter", "objectInstance": "ricxappRMRTransmitted" }]}`
- var m []interface{} = metricsStringToInterfaceArray(metricsJson)
+ m := metricsStringToInterfaceArray(metricsJSON)
appMetrics = parseMetricsRules(m, appMetrics)
assert.Len(t, appMetrics, 2)
assert.Equal(t, "ricxappRMRreceivedCounter", appMetrics["ricxapp_RMR_Received"].ObjectName)
@@ -195,12 +196,12 @@
func TestParseMetricsRulesIncrementalFillOfAppMetrics(t *testing.T) {
appMetrics := make(AppMetrics)
- metricsJson1 := `{"metrics": [
+ metricsJSON1 := `{"metrics": [
{ "name": "ricxapp_RMR_Received", "objectName": "ricxappRMRreceivedCounter", "objectInstance": "ricxappRMRReceived" }]}`
- metricsJson2 := `{"metrics": [
+ metricsJSON2 := `{"metrics": [
{ "name": "ricxapp_RMR_Transmitted", "objectName": "ricxappRMRTransmittedCounter", "objectInstance": "ricxappRMRTransmitted" }]}`
- var m1 []interface{} = metricsStringToInterfaceArray(metricsJson1)
- var m2 []interface{} = metricsStringToInterfaceArray(metricsJson2)
+ m1 := metricsStringToInterfaceArray(metricsJSON1)
+ m2 := metricsStringToInterfaceArray(metricsJSON2)
appMetrics = parseMetricsRules(m1, appMetrics)
appMetrics = parseMetricsRules(m2, appMetrics)
assert.Len(t, appMetrics, 2)
@@ -226,18 +227,18 @@
}
func TestParseXAppDescriptorWithNoConfig(t *testing.T) {
- metricsJson := `[{{"metadata": "something", "descriptor": "somethingelse"}},
+ metricsJSON := `[{{"metadata": "something", "descriptor": "somethingelse"}},
{{"metadata": "something", "descriptor": "somethingelse"}}]`
- metricsBytes := []byte(metricsJson)
+ metricsBytes := []byte(metricsJSON)
appMetrics := make(AppMetrics)
appMetrics = parseMetricsFromXAppDescriptor(metricsBytes, appMetrics)
assert.Empty(t, appMetrics)
}
func TestParseXAppDescriptorWithNoMetrics(t *testing.T) {
- metricsJson := `[{{"metadata": "something", "descriptor": "somethingelse", "config":{}},
+ metricsJSON := `[{{"metadata": "something", "descriptor": "somethingelse", "config":{}},
{{"metadata": "something", "descriptor": "somethingelse", "config":{}}}]`
- metricsBytes := []byte(metricsJson)
+ metricsBytes := []byte(metricsJSON)
appMetrics := make(AppMetrics)
appMetrics = parseMetricsFromXAppDescriptor(metricsBytes, appMetrics)
assert.Empty(t, appMetrics)
diff --git a/cmd/vesmgr/httpserver.go b/cmd/vesmgr/httpserver.go
index 585319e..2564ef3 100644
--- a/cmd/vesmgr/httpserver.go
+++ b/cmd/vesmgr/httpserver.go
@@ -24,28 +24,47 @@
"net/http"
)
-const SupervisionUrl = "/supervision/"
+// SupervisionURL is the url where kubernetes posts alive queries
+const SupervisionURL = "/supervision/"
-func startHttpServer(listener net.Listener, xappnotifUrl string, notif_ch chan []byte, supervision_ch chan chan string) {
- go runHttpServer(listener, xappnotifUrl, notif_ch, supervision_ch)
+// HTTPServer is the VesMgr HTTP server struct
+type HTTPServer struct {
+ listener net.Listener
}
-func runHttpServer(listener net.Listener, xappNotifUrl string, notif_ch chan []byte, supervision_ch chan chan string) {
+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) {
+ http.HandleFunc(xappNotifURL, func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
- logger.Info("httpServer: POST in %s", xappNotifUrl)
+ 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
}
- notif_ch <- body
+ notifCh <- body
return
default:
logger.Error("httpServer: Invalid method %s to %s", r.Method, r.URL.Path)
@@ -54,15 +73,15 @@
}
})
- http.HandleFunc(SupervisionUrl, func(w http.ResponseWriter, r *http.Request) {
+ http.HandleFunc(SupervisionURL, func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
logger.Info("httpServer: GET supervision")
- supervision_ack_ch := make(chan string)
+ supervisionAckCh := make(chan string)
// send supervision to the main loop
- supervision_ch <- supervision_ack_ch
- reply := <-supervision_ack_ch
+ supervisionCh <- supervisionAckCh
+ reply := <-supervisionAckCh
logger.Info("httpServer: supervision ack from the main loop: %s", reply)
fmt.Fprintf(w, reply)
return
diff --git a/cmd/vesmgr/httpserver_test.go b/cmd/vesmgr/httpserver_test.go
index d52f4b2..c2946aa 100644
--- a/cmd/vesmgr/httpserver_test.go
+++ b/cmd/vesmgr/httpserver_test.go
@@ -17,42 +17,41 @@
package main
import (
- "github.com/stretchr/testify/suite"
"io/ioutil"
- "net"
"net/http"
"os"
"strings"
"testing"
+
+ "github.com/stretchr/testify/suite"
)
-type HttpServerTestSuite struct {
+type HTTPServerTestSuite struct {
suite.Suite
- listener net.Listener
- ch_notif chan []byte
- ch_supervision chan chan string
+ chNotif chan []byte
+ chSupervision chan chan string
+ server HTTPServer
}
// suite setup creates the HTTP server
-func (suite *HttpServerTestSuite) SetupSuite() {
+func (suite *HTTPServerTestSuite) SetupSuite() {
os.Unsetenv("http_proxy")
os.Unsetenv("HTTP_PROXY")
- var err error
- suite.listener, err = net.Listen("tcp", ":0")
- suite.Nil(err)
- suite.ch_notif = make(chan []byte)
- suite.ch_supervision = make(chan chan string)
- startHttpServer(suite.listener, "/vesmgr_notif/", suite.ch_notif, suite.ch_supervision)
+ 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.listener.Addr().String()+SupervisionUrl, "supervision")
+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) {
+func (suite *HTTPServerTestSuite) doGet(url string) (*http.Response, string) {
resp, err := http.Get(url)
suite.Nil(err)
@@ -62,8 +61,8 @@
return resp, string(contents)
}
-func (suite *HttpServerTestSuite) doPost(serverUrl string, msg string) (*http.Response, string) {
- resp, err := http.Post(serverUrl, "data", strings.NewReader(msg))
+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()
@@ -72,41 +71,41 @@
return resp, string(contents)
}
-func replySupervision(ch_supervision chan chan string, reply string) {
- ch_supervision_ack := <-ch_supervision
- ch_supervision_ack <- reply
+func replySupervision(chSupervision chan chan string, reply string) {
+ chSupervisionAck := <-chSupervision
+ chSupervisionAck <- reply
}
-func (suite *HttpServerTestSuite) TestHttpServerSupervision() {
+func (suite *HTTPServerTestSuite) TestHttpServerSupervision() {
// start the "main loop" to reply to the supervision to the HTTPServer
- go replySupervision(suite.ch_supervision, "I'm just fine")
+ go replySupervision(suite.chSupervision, "I'm just fine")
- resp, reply := suite.doGet("http://" + suite.listener.Addr().String() + SupervisionUrl)
+ 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.listener.Addr().String()+"/invalid_url", "foo")
+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(ch_notif chan []byte, ch chan []byte) {
- notification := <-ch_notif
+func readXAppNotification(chNotif chan []byte, ch chan []byte) {
+ notification := <-chNotif
ch <- notification
}
-func (suite *HttpServerTestSuite) TestHttpServerXappNotif() {
+func (suite *HTTPServerTestSuite) TestHttpServerXappNotif() {
// start the "main loop" to receive the xAppNotification message from the HTTPServer
ch := make(chan []byte)
- go readXAppNotification(suite.ch_notif, ch)
+ go readXAppNotification(suite.chNotif, ch)
- resp, reply := suite.doPost("http://"+suite.listener.Addr().String()+"/vesmgr_notif/", "test data")
+ 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)
@@ -114,13 +113,13 @@
suite.Equal([]byte("test data"), notification)
}
-func (suite *HttpServerTestSuite) TestHttpServerXappNotifInvalidOperation() {
- resp, reply := suite.doGet("http://" + suite.listener.Addr().String() + "/vesmgr_notif/")
+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))
+ suite.Run(t, new(HTTPServerTestSuite))
}
diff --git a/cmd/vesmgr/main.go b/cmd/vesmgr/main.go
index 68e5c0c..cd3d14e 100644
--- a/cmd/vesmgr/main.go
+++ b/cmd/vesmgr/main.go
@@ -18,5 +18,6 @@
package main
func main() {
- vesmgrInit()
+ vesmgr := VesMgr{}
+ vesmgr.Init(vesmgrXappNotifPort).Run()
}
diff --git a/cmd/vesmgr/subprocess.go b/cmd/vesmgr/subprocess.go
new file mode 100644
index 0000000..0071c20
--- /dev/null
+++ b/cmd/vesmgr/subprocess.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "os"
+ "os/exec"
+)
+
+type cmdRunner struct {
+ exe string
+ args []string
+ cmd *exec.Cmd
+}
+
+func (r *cmdRunner) run(result chan error) {
+ r.cmd = exec.Command(r.exe, r.args...)
+ r.cmd.Stdout = os.Stdout
+ r.cmd.Stderr = os.Stderr
+ err := r.cmd.Start()
+ go func() {
+ if err != nil {
+ result <- err
+ } else {
+ result <- r.cmd.Wait()
+ }
+ }()
+}
+
+func (r *cmdRunner) kill() error {
+ return r.cmd.Process.Kill()
+}
+
+func makeRunner(exe string, arg ...string) cmdRunner {
+ r := cmdRunner{exe: exe, args: arg}
+ return r
+}
diff --git a/cmd/vesmgr/subprocess_test.go b/cmd/vesmgr/subprocess_test.go
new file mode 100644
index 0000000..bf460d9
--- /dev/null
+++ b/cmd/vesmgr/subprocess_test.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestProcessRunning(t *testing.T) {
+ r := makeRunner("echo", "a")
+ ch := make(chan error)
+ r.run(ch)
+ err := <-ch
+ assert.Nil(t, err)
+}
+
+func TestProcessKill(t *testing.T) {
+ r := makeRunner("sleep", "20")
+ ch := make(chan error)
+ 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")
+ ch := make(chan error)
+ r.run(ch)
+ err := <-ch
+ assert.NotNil(t, err)
+}
diff --git a/cmd/vesmgr/subscribexAPPNotifications.go b/cmd/vesmgr/subscribexAPPNotifications.go
index 6f8ed77..1d3cc22 100644
--- a/cmd/vesmgr/subscribexAPPNotifications.go
+++ b/cmd/vesmgr/subscribexAPPNotifications.go
@@ -30,60 +30,58 @@
// appmgr API
const appmgrSubsPath = "/ric/v1/subscriptions"
-var errPostingFailed error = errors.New("Posting subscriptions failed")
-var errWrongStatusCode error = errors.New("Wrong subscriptions response StatusCode")
+var errPostingFailed = errors.New("Posting subscriptions failed")
+var errWrongStatusCode = errors.New("Wrong subscriptions response StatusCode")
-func subscribexAppNotifications(targetUrl string, subscriptions chan subsChannel, timeout time.Duration, subsUrl string) {
- requestBody := []byte(fmt.Sprintf(`{"maxRetries": 5, "retryTimer": 5, "eventType":"all", "targetUrl": "%v"}`, targetUrl))
- req, err := http.NewRequest("POST", subsUrl, bytes.NewBuffer(requestBody))
+func subscribexAppNotifications(targetURL string, subscriptions chan subscriptionNotification, timeout time.Duration, subsURL string) {
+ requestBody := []byte(fmt.Sprintf(`{"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 <- subsChannel{false, 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 {
- err := subscribexAppNotificationsClientDo(req, client)
+ subsID, err = subscribexAppNotificationsClientDo(req, client)
if err == nil {
break
} else if err != errPostingFailed && err != errWrongStatusCode {
- subscriptions <- subsChannel{false, err}
+ subscriptions <- subscriptionNotification{false, err, ""}
return
}
time.Sleep(5 * time.Second)
}
- subscriptions <- subsChannel{true, nil}
+ subscriptions <- subscriptionNotification{true, nil, subsID}
}
-func subscribexAppNotificationsClientDo(req *http.Request, client *http.Client) error {
+func subscribexAppNotificationsClientDo(req *http.Request, client *http.Client) (string, error) {
resp, err := client.Do(req)
if err != nil {
logger.Error("Posting subscriptions failed: %s", err)
- return errPostingFailed
- } else {
- 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))
- vesmgr.appmgrSubsId = result["id"].(string)
- return nil
- } else {
- logger.Error("Wrong subscriptions response StatusCode: %d", resp.StatusCode)
- return errWrongStatusCode
- }
+ 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/subscribexAPPNotifications_test.go b/cmd/vesmgr/subscribexAPPNotifications_test.go
index 31621b3..6305f40 100644
--- a/cmd/vesmgr/subscribexAPPNotifications_test.go
+++ b/cmd/vesmgr/subscribexAPPNotifications_test.go
@@ -21,38 +21,33 @@
"bytes"
"encoding/json"
"fmt"
- "github.com/stretchr/testify/suite"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
+
+ "github.com/stretchr/testify/suite"
)
-type AppmgrHttpServerTestSuite struct {
+type AppmgrHTTPServerTestSuite struct {
suite.Suite
- subscriptions chan subsChannel
- xappNotifUrl string
+ subscriptions chan subscriptionNotification
+ xappNotifURL string
}
// suite setup
-func (suite *AppmgrHttpServerTestSuite) SetupSuite() {
- vesmgr.appmgrSubsId = string("")
- vesmgr.myIPAddress, _ = getMyIP()
- suite.xappNotifUrl = "http://" + vesmgr.myIPAddress + ":" + vesmgrXappNotifPort + vesmgrXappNotifPath
- suite.subscriptions = make(chan subsChannel)
+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)
}
// test setup
-func (suite *AppmgrHttpServerTestSuite) SetupTest() {
- suite.subscriptions = make(chan subsChannel)
+func (suite *AppmgrHTTPServerTestSuite) SetupTest() {
+ suite.subscriptions = make(chan subscriptionNotification)
}
-// test teardown
-func (suite *AppmgrHttpServerTestSuite) TearDownTest() {
- vesmgr.appmgrSubsId = string("")
-}
-
-func (suite *AppmgrHttpServerTestSuite) TestSubscribexAppNotifications() {
+func (suite *AppmgrHTTPServerTestSuite) TestSubscribexAppNotifications() {
testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
body, _ := ioutil.ReadAll(req.Body)
var result map[string]interface{}
@@ -68,13 +63,13 @@
}))
defer testServer.Close()
- go subscribexAppNotifications(suite.xappNotifUrl, suite.subscriptions, 1, testServer.URL)
+ go subscribexAppNotifications(suite.xappNotifURL, suite.subscriptions, 1, testServer.URL)
isSubscribed := <-suite.subscriptions
suite.Nil(isSubscribed.err)
- suite.Equal("deadbeef1234567890", vesmgr.appmgrSubsId)
+ suite.Equal("deadbeef1234567890", isSubscribed.subsID)
}
-func (suite *AppmgrHttpServerTestSuite) TestSubscribexAppNotificationsWrongStatus() {
+func (suite *AppmgrHTTPServerTestSuite) 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 +77,32 @@
}))
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"}`, suite.xappNotifURL))
req, _ := http.NewRequest("POST", testServer.URL, bytes.NewBuffer(requestBody))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
- err := subscribexAppNotificationsClientDo(req, client)
+ subsID, err := subscribexAppNotificationsClientDo(req, client)
suite.Equal(errWrongStatusCode, err)
// after failed POST vesmgr.appmgrSubsId holds an initial values
- suite.Equal("", vesmgr.appmgrSubsId)
+ suite.Equal("", subsID)
}
-func (suite *AppmgrHttpServerTestSuite) TestSubscribexAppNotificationsWrongUrl() {
+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))
+ 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{}
- err := subscribexAppNotificationsClientDo(req, client)
+ subsID, err := subscribexAppNotificationsClientDo(req, client)
suite.Equal(errPostingFailed, err)
// after failed POST vesmgr.appmgrSubsId holds an initial values
- suite.Equal("", vesmgr.appmgrSubsId)
+ suite.Equal("", subsID)
}
-func (suite *AppmgrHttpServerTestSuite) TestSubscribexAppNotificationsReadBodyFails() {
+func (suite *AppmgrHTTPServerTestSuite) 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 +110,13 @@
}))
defer testServer.Close()
- go subscribexAppNotifications(suite.xappNotifUrl, suite.subscriptions, 1, testServer.URL)
+ go subscribexAppNotifications(suite.xappNotifURL, suite.subscriptions, 1, testServer.URL)
isSubscribed := <-suite.subscriptions
suite.Equal("unexpected EOF", isSubscribed.err.Error())
- suite.Equal("", vesmgr.appmgrSubsId)
+ suite.Equal("", isSubscribed.subsID)
}
-func (suite *AppmgrHttpServerTestSuite) TestSubscribexAppNotificationsUnMarshalFails() {
+func (suite *AppmgrHTTPServerTestSuite) 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 +124,12 @@
}))
defer testServer.Close()
- go subscribexAppNotifications(suite.xappNotifUrl, suite.subscriptions, 1, testServer.URL)
+ 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("", vesmgr.appmgrSubsId)
+ suite.Equal("", isSubscribed.subsID)
}
func TestAppmgrHttpServerTestSuite(t *testing.T) {
- suite.Run(t, new(AppmgrHttpServerTestSuite))
+ suite.Run(t, new(AppmgrHTTPServerTestSuite))
}
diff --git a/cmd/vesmgr/vesmgr.go b/cmd/vesmgr/vesmgr.go
index 0e27838..aeebb15 100755
--- a/cmd/vesmgr/vesmgr.go
+++ b/cmd/vesmgr/vesmgr.go
@@ -23,7 +23,6 @@
"net"
"net/http"
"os"
- "os/exec"
"time"
mdcloggo "gerrit.o-ran-sc.org/r/com/golog.git"
@@ -34,29 +33,29 @@
const appmgrXAppConfigPath = "/ric/v1/config"
const appmgrPort = "8080"
-type VesAgent struct {
- Pid int
- name string
- process *os.Process
-}
-
+// VesMgr contains runtime information of the vesmgr process
type VesMgr struct {
- myIPAddress string
- appmgrSubsId string
+ myIPAddress string
+ chXAppSubscriptions chan subscriptionNotification
+ chXAppNotifications chan []byte
+ chSupervision chan chan string
+ chVesagent chan error
+ vesagent cmdRunner
+ httpServer HTTPServer
}
-type subsChannel struct {
+type subscriptionNotification struct {
subscribed bool
err error
+ subsID string
}
-var vesagent VesAgent
-var vesmgr VesMgr
var logger *mdcloggo.MdcLogger
const vesmgrXappNotifPort = "8080"
const vesmgrXappNotifPath = "/vesmgr_xappnotif/"
const timeoutPostXAppSubscriptions = 5
+const vespaConfigFile = "/etc/ves-agent/ves-agent.yaml"
func init() {
logger, _ = mdcloggo.InitLogger("vesmgr")
@@ -80,8 +79,8 @@
return "", nil
}
-func createConf(xappMetrics []byte) {
- f, err := os.Create("/etc/ves-agent/ves-agent.yaml")
+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)
@@ -92,21 +91,20 @@
logger.Info("Vespa config created")
}
-func subscribeXAppNotifications(chSubscriptions chan subsChannel) {
- xappNotifUrl := "http://" + vesmgr.myIPAddress + ":" + vesmgrXappNotifPort + vesmgrXappNotifPath
- subsUrl := "http://" + appmgrDomain + ":" + appmgrPort + appmgrSubsPath
- go subscribexAppNotifications(xappNotifUrl, chSubscriptions, timeoutPostXAppSubscriptions, subsUrl)
- logger.Info("xApp notifications subscribed from %s", subsUrl)
+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)
}
-func vesmgrInit() {
- vesagent.name = "ves-agent"
+// Init initializes the vesmgr
+func (vesmgr *VesMgr) Init(listenPort string) *VesMgr {
logger.Info("vesmgrInit")
-
var err error
if vesmgr.myIPAddress, err = getMyIP(); err != nil || vesmgr.myIPAddress == "" {
logger.Error("Cannot get myIPAddress: IP %s, err %s", vesmgr.myIPAddress, err.Error())
- return
+ panic("Cannot get my IP address")
}
var ok bool
@@ -117,138 +115,134 @@
appmgrDomain = "service-ricplt-appmgr-http.ricplt.svc.cluster.local"
logger.Info("Using default appmgrdomain %s", appmgrDomain)
}
- chXAppSubscriptions := make(chan subsChannel)
- chXAppNotifications := make(chan []byte)
- chSupervision := make(chan chan string)
- chVesagent := make(chan error)
-
- listener, err := net.Listen("tcp", vesmgr.myIPAddress+":"+vesmgrXappNotifPort)
- startHttpServer(listener, vesmgrXappNotifPath, chXAppNotifications, chSupervision)
-
- subscribeXAppNotifications(chXAppSubscriptions)
-
- runVesmgr(chVesagent, chSupervision, chXAppNotifications, chXAppSubscriptions)
+ 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"))
+ return vesmgr
}
-func startVesagent(ch chan error) {
- cmd := exec.Command(vesagent.name, "-i", os.Getenv("VESMGR_HB_INTERVAL"), "-m", os.Getenv("VESMGR_MEAS_INTERVAL"), "--Measurement.Prometheus.Address", os.Getenv("VESMGR_PROMETHEUS_ADDR"))
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- if err := cmd.Start(); err != nil {
- logger.Error("vesmgr exiting, ves-agent start failed: %s", err)
- go func() {
- ch <- err
- }()
- } else {
- logger.Info("ves-agent started with pid %d", cmd.Process.Pid)
- vesagent.Pid = cmd.Process.Pid
- vesagent.process = cmd.Process
- go func() {
- // wait ves-agent exit and then post the error to the channel
- err := cmd.Wait()
- ch <- err
- }()
- }
+func (vesmgr *VesMgr) startVesagent() {
+ vesmgr.vesagent.run(vesmgr.chVesagent)
}
-func killVespa(process *os.Process) {
+func (vesmgr *VesMgr) killVespa() error {
logger.Info("Killing vespa")
- err := process.Kill()
+ err := vesmgr.vesagent.kill()
if err != nil {
logger.Error("Cannot kill vespa: %s", err.Error())
+ return err
}
+ return <-vesmgr.chVesagent // wait vespa exit
}
-func queryXAppsStatus(appmgrUrl string, timeout time.Duration) ([]byte, error) {
-
- logger.Info("query xAppStatus started, url %s", appmgrUrl)
- req, err := http.NewRequest("GET", appmgrUrl, nil)
+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 nil, 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 status failed: %s", err)
- return nil, err
+ 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 status body: %s", err)
- return nil, err
+ logger.Error("Failed to read xApp config body: %s", err)
+ return emptyConfig, err
}
- logger.Info("query xAppStatus completed")
+ logger.Info("query xAppConfig completed")
return body, nil
- } else {
- logger.Error("Error from xApp status query: %s", resp.Status)
- return nil, errors.New(resp.Status)
}
+ logger.Error("Error from xApp config query: %s", resp.Status)
+ return emptyConfig, errors.New(resp.Status)
}
-type state int
-
-const (
- normalState state = iota
- vespaTerminatingState state = iota
-)
-
func queryConf() ([]byte, error) {
- return queryXAppsStatus("http://"+appmgrDomain+":"+appmgrPort+appmgrXAppConfigPath,
+ return queryXAppsConfig("http://"+appmgrDomain+":"+appmgrPort+appmgrXAppConfigPath,
10*time.Second)
}
-func runVesmgr(chVesagent chan error, chSupervision chan chan string, chXAppNotifications chan []byte, chXAppSubscriptions chan subsChannel) {
-
- logger.Info("vesmgr main loop ready")
- mystate := normalState
- var xappStatus []byte
- var err error
+func (vesmgr *VesMgr) emptyNotificationsChannel() {
for {
select {
- case supervision := <-chSupervision:
+ 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 xAppNotif := <-chXAppNotifications:
- logger.Info("vesmgr: xApp notification")
- logger.Info(string(xAppNotif))
- /*
- * If xapp status 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
- */
- xappStatus, err = queryConf()
- if err == nil {
- killVespa(vesagent.process)
- mystate = vespaTerminatingState
- }
- case err := <-chVesagent:
- switch mystate {
- case vespaTerminatingState:
- logger.Info("Vesagent termination completed")
- createConf(xappStatus)
- startVesagent(chVesagent)
- mystate = normalState
- default:
- logger.Error("Vesagent exited: " + err.Error())
- os.Exit(1)
- }
- case isSubscribed := <-chXAppSubscriptions:
+ case isSubscribed := <-vesmgr.chXAppSubscriptions:
if isSubscribed.err != nil {
logger.Error("Failed to make xApp subscriptions, vesmgr exiting: %s", isSubscribed.err)
os.Exit(1)
}
- xappStatus, err = queryConf()
- if err == nil {
- createConf(xappStatus)
- startVesagent(chVesagent)
- }
+ 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_queryxappssttus_test.go b/cmd/vesmgr/vesmgr_queryxappconfig_test.go
similarity index 67%
rename from cmd/vesmgr/vesmgr_queryxappssttus_test.go
rename to cmd/vesmgr/vesmgr_queryxappconfig_test.go
index 5f0f36c..e9bcbfe 100644
--- a/cmd/vesmgr/vesmgr_queryxappssttus_test.go
+++ b/cmd/vesmgr/vesmgr_queryxappconfig_test.go
@@ -19,25 +19,26 @@
import (
"fmt"
- "github.com/stretchr/testify/suite"
"net"
"net/http"
"net/url"
"os"
"testing"
"time"
+
+ "github.com/stretchr/testify/suite"
)
type do func(w http.ResponseWriter)
-type QueryXAppsStatusTestSuite struct {
+type QueryXAppsConfigTestSuite struct {
suite.Suite
listener net.Listener
xAppMgrFunc do
}
// suite setup creates the HTTP server
-func (suite *QueryXAppsStatusTestSuite) SetupSuite() {
+func (suite *QueryXAppsConfigTestSuite) SetupSuite() {
os.Unsetenv("http_proxy")
os.Unsetenv("HTTP_PROXY")
var err error
@@ -46,7 +47,7 @@
go runXAppMgr(suite.listener, "/test_url/", suite)
}
-func runXAppMgr(listener net.Listener, url string, suite *QueryXAppsStatusTestSuite) {
+func runXAppMgr(listener net.Listener, url string, suite *QueryXAppsConfigTestSuite) {
http.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
@@ -57,44 +58,44 @@
http.Serve(listener, nil)
}
-func (suite *QueryXAppsStatusTestSuite) TestQueryXAppsStatusFailsWithTimeout() {
- do_sleep := func(w http.ResponseWriter) {
+func (suite *QueryXAppsConfigTestSuite) TestQueryXAppsConfigFailsWithTimeout() {
+ doSleep := func(w http.ResponseWriter) {
time.Sleep(time.Second * 2)
}
- suite.xAppMgrFunc = do_sleep
+ suite.xAppMgrFunc = doSleep
- data, err := queryXAppsStatus("http://"+suite.listener.Addr().String()+"/test_url/", 1)
- suite.Nil(data)
+ 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 *QueryXAppsStatusTestSuite) TestQueryXAppsStatusFailsWithAnErrorReply() {
- do_reply_with_err := func(w http.ResponseWriter) {
+func (suite *QueryXAppsConfigTestSuite) TestQueryXAppsConfigFailsWithAnErrorReply() {
+ doReplyWithErr := func(w http.ResponseWriter) {
http.Error(w, "405 method not allowed", http.StatusMethodNotAllowed)
}
- suite.xAppMgrFunc = do_reply_with_err
+ suite.xAppMgrFunc = doReplyWithErr
- data, err := queryXAppsStatus("http://"+suite.listener.Addr().String()+"/test_url/", 1)
- suite.Nil(data)
+ 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 *QueryXAppsStatusTestSuite) TestQueryXAppsStatusOk() {
- do_reply := func(w http.ResponseWriter) {
+func (suite *QueryXAppsConfigTestSuite) TestQueryXAppsConfigOk() {
+ doReply := func(w http.ResponseWriter) {
fmt.Fprintf(w, "reply message")
}
- suite.xAppMgrFunc = do_reply
+ suite.xAppMgrFunc = doReply
- data, err := queryXAppsStatus("http://"+suite.listener.Addr().String()+"/test_url/", 1)
+ data, err := queryXAppsConfig("http://"+suite.listener.Addr().String()+"/test_url/", 1)
suite.NotNil(data)
suite.Nil(err)
suite.Equal(data, []byte("reply message"))
}
-func TestQueryXAppsStatusTestSuite(t *testing.T) {
- suite.Run(t, new(QueryXAppsStatusTestSuite))
+func TestQueryXAppsConfigTestSuite(t *testing.T) {
+ suite.Run(t, new(QueryXAppsConfigTestSuite))
}
diff --git a/cmd/vesmgr/vesmgr_test.go b/cmd/vesmgr/vesmgr_test.go
index 19dd60d..a9b9275 100644
--- a/cmd/vesmgr/vesmgr_test.go
+++ b/cmd/vesmgr/vesmgr_test.go
@@ -19,15 +19,39 @@
import (
"errors"
- "github.com/stretchr/testify/assert"
"os"
"os/exec"
+ "path/filepath"
+ "strconv"
"testing"
"time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/suite"
)
-func init() {
- vesagent.name = "echo" // no need to run real ves-agent
+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")
@@ -36,73 +60,82 @@
os.Setenv("VESMGR_PROMETHEUS_ADDR", "http://localhost:9090")
}
-func TestStartVesagent(t *testing.T) {
- assert.Equal(t, 0, vesagent.Pid)
- ch := make(chan error)
- startVesagent(ch)
- assert.NotEqual(t, 0, vesagent.Pid)
- t.Logf("VES agent pid = %d", vesagent.Pid)
- vesagent.Pid = 0
- err := <-ch
- assert.Nil(t, err)
-}
-
-func TestStartVesagentFails(t *testing.T) {
- vesagent.name = "Not-ves-agent"
- assert.Equal(t, 0, vesagent.Pid)
- ch := make(chan error)
- startVesagent(ch)
- err := <-ch
- assert.NotNil(t, err)
- assert.Equal(t, 0, vesagent.Pid)
- vesagent.name = "ves-agent"
-}
-
-func TestGetMyIP(t *testing.T) {
- vesmgr.myIPAddress = string("")
- var err error
- vesmgr.myIPAddress, err = getMyIP()
- assert.NotEqual(t, string(""), vesmgr.myIPAddress)
- assert.Equal(t, nil, err)
-}
-
-func TestMainLoopSupervision(t *testing.T) {
- chXAppNotifications := make(chan []byte)
- chSupervision := make(chan chan string)
- chVesagent := make(chan error)
- chSubscriptions := make(chan subsChannel)
- go runVesmgr(chVesagent, chSupervision, chXAppNotifications, chSubscriptions)
-
+func (suite *VesmgrTestSuite) TestMainLoopSupervision() {
+ go suite.vesmgr.servRequest()
ch := make(chan string)
- chSupervision <- ch
+ suite.vesmgr.chSupervision <- ch
reply := <-ch
- assert.Equal(t, "OK", reply)
+ suite.Equal("OK", reply)
}
-func TestMainLoopVesagentError(t *testing.T) {
+func (suite *VesmgrTestSuite) TestMainLoopVesagentError() {
if os.Getenv("TEST_VESPA_EXIT") == "1" {
// we're run in a new process, now make vesmgr main loop exit
- chXAppNotifications := make(chan []byte)
- chSupervision := make(chan chan string)
- chVesagent := make(chan error)
- chSubscriptions := make(chan subsChannel)
- go runVesmgr(chVesagent, chSupervision, chXAppNotifications, chSubscriptions)
-
- chVesagent <- errors.New("vesagent killed")
+ 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=TestMainLoopVesagentError")
+ 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)
- assert.Equal(t, true, ok)
- assert.Equal(t, "exit status 1", e.Error())
+ 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))
}
diff --git a/container-tag.yaml b/container-tag.yaml
index 553a6e5..fa6e83d 100644
--- a/container-tag.yaml
+++ b/container-tag.yaml
@@ -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.0.4
+tag: 0.0.5