/* * Copyright (c) 2020 InterDigital Communications, Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package systemTest import ( "context" "encoding/json" "errors" "io/ioutil" "net/http" "net/url" "os" "os/signal" "strconv" "strings" "syscall" "time" "github.com/ghodss/yaml" gisClient "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-gis-engine-client" log "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-logger" platformCtrlClient "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-platform-ctrl-client" sandboxCtrlClient "github.com/InterDigitalInc/AdvantEDGE/go-packages/meep-sandbox-ctrl-client" ) var httpReqBody []string var platformCtrlAppClient *platformCtrlClient.APIClient var sandboxCtrlAppClient *sandboxCtrlClient.APIClient var gisAppClient *gisClient.APIClient var sandboxName = "sandbox-system-test" var hostUrlStr = "" var httpListenerPort = "3333" var run = false func resetHttpReqBody() { httpReqBody = []string{} } func printHttpReqBody() { for index, body := range httpReqBody { log.Info("Notification received: (" + strconv.Itoa(index) + "):" + body) } } func printHttpReqBodyByIndex(index int) { log.Info("Notification received: (" + strconv.Itoa(index) + "):" + httpReqBody[index]) } func initialiseVars() { hostUrl, _ := url.Parse(strings.TrimSpace(os.Getenv("MEEP_HOST_TEST_URL"))) hostUrlStr = hostUrl.String() if hostUrlStr == "" { hostUrlStr = "http://localhost" } } func createClients() error { // Create & store client for App REST API platformCtrlAppClientCfg := platformCtrlClient.NewConfiguration() if hostUrlStr == "" { hostUrlStr = "http://localhost" } platformCtrlAppClientCfg.BasePath = hostUrlStr + "/platform-ctrl/v1" platformCtrlAppClient = platformCtrlClient.NewAPIClient(platformCtrlAppClientCfg) if platformCtrlAppClient == nil { log.Error("Failed to create Platform App REST API client: ", platformCtrlAppClientCfg.BasePath) err := errors.New("Failed to create Platform App REST API client") return err } return nil } func createSandboxClients(sandboxName string) error { // Create & store client for App REST API sandboxCtrlAppClientCfg := sandboxCtrlClient.NewConfiguration() if hostUrlStr == "" { hostUrlStr = "http://localhost" } sandboxCtrlAppClientCfg.BasePath = hostUrlStr + "/" + sandboxName + "/sandbox-ctrl/v1" sandboxCtrlAppClient = sandboxCtrlClient.NewAPIClient(sandboxCtrlAppClientCfg) if sandboxCtrlAppClient == nil { log.Error("Failed to create Sandbox App REST API client: ", sandboxCtrlAppClientCfg.BasePath) err := errors.New("Failed to create Sandbox App REST API client") return err } gisAppClientCfg := gisClient.NewConfiguration() gisAppClientCfg.BasePath = hostUrlStr + "/" + sandboxName + "/gis/v1" gisAppClient = gisClient.NewAPIClient(gisAppClientCfg) if gisAppClient == nil { log.Error("Failed to create GIS App REST API client: ", gisAppClientCfg.BasePath) err := errors.New("Failed to create GIS App REST API client") return err } return nil } func createSandbox(name string) error { config := platformCtrlClient.SandboxConfig{""} _, err := platformCtrlAppClient.SandboxControlApi.CreateSandboxWithName(context.TODO(), name, config) if err != nil { log.Error("Failed to create sandbox: ", err) return err } return nil } func deleteSandbox(name string) error { _, err := platformCtrlAppClient.SandboxControlApi.DeleteSandbox(context.TODO(), name) if err != nil { log.Error("Failed to delete sandbox: ", err) return err } return nil } func createScenario(name string, filepath string) error { //get the content of the file, assuming yaml content yamlContent, err := ioutil.ReadFile(filepath) if err != nil { log.Error("Couldn't read file: ", err) return err } //converting to json since unmarshal with yaml directly not working well, while json does jsonContent, err := yaml.YAMLToJSON(yamlContent) if err != nil { log.Error("Failed converting yaml to json: ", err) return err } var scenario platformCtrlClient.Scenario err = json.Unmarshal([]byte(jsonContent), &scenario) if err != nil { log.Error("Failed to unmarshal: ", err) return err } _, err = platformCtrlAppClient.ScenarioConfigurationApi.CreateScenario(context.TODO(), name, scenario) if err != nil { log.Error("Failed to create scenario: ", err) return err } return nil } func deleteScenario(name string) error { _, err := platformCtrlAppClient.ScenarioConfigurationApi.DeleteScenario(context.TODO(), name) if err != nil { log.Error("Failed to delete scenario: ", err) return err } return nil } func activateScenario(name string) error { _, err := sandboxCtrlAppClient.ActiveScenarioApi.ActivateScenario(context.TODO(), name, nil) if err != nil { log.Error("Failed to activate scenario: ", err) return err } _, _, err = sandboxCtrlAppClient.ActiveScenarioApi.GetActiveScenario(context.TODO(), nil) if err != nil { log.Error("Scenario not active : ", err) return err } //reinitialisation of http msg queue resetHttpReqBody() return nil } func terminateScenario() error { _, err := sandboxCtrlAppClient.ActiveScenarioApi.TerminateScenario(context.TODO()) if err != nil { log.Error("Failed to terminate scenario: ", err) return err } return nil } func createSystemTestReadyState() error { initialiseVars() log.Info("creating Clients") err := createClients() if err != nil { return err } log.Info("creating Sandbox") err = createSandbox(sandboxName) if err != nil { return err } else { time.Sleep(30000 * time.Millisecond) } log.Info("creating Sandbox Clients") err = createSandboxClients(sandboxName) if err != nil { clearSystemTestReadyState() return err } return nil } func clearSystemTestReadyState() { log.Info("deleting Sandbox") deleteSandbox(sandboxName) } func startSystemTest() error { if !run { go main() err := createSystemTestReadyState() if err != nil { run = false return err } } return nil } func stopSystemTest() { if run { clearSystemTestReadyState() run = false } } func main() { //create default route handler http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { log.Info("http message received!") defer req.Body.Close() body, _ := ioutil.ReadAll(req.Body) httpReqBody = append(httpReqBody, string(body)) }) log.Info(os.Args) log.Info("Starting System Test") run = true //creating a server for graceful shutdown listenerPort := ":" + httpListenerPort server := &http.Server{Addr: listenerPort} go func() { sigchan := make(chan os.Signal, 10) signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) <-sigchan log.Info("Program killed !") // do last actions and wait for all write operations to end run = false //graceful server shutdown err := server.Shutdown(context.Background()) if err != nil { log.Info("Error shuting down the server: ", err) } }() go func() { //create default route handler if err := server.ListenAndServe(); err != http.ErrServerClosed { // Error starting or closing listener log.Fatal("HTTP server ListenAndServe: ", err) } //log.Fatal(http.ListenAndServe(":" + httpListenerPort, nil)) run = false }() count := 0 for { if !run { log.Info("Ran for ", count, " seconds") clearSystemTestReadyState() break } time.Sleep(time.Second) count++ } } func isAutomationReady(waitUntilReady bool, maxRetry int, initialWait int) bool { if initialWait > 0 { time.Sleep(time.Duration(initialWait) * time.Second) } var err error if waitUntilReady { retry := 0 for retry <= maxRetry { _, _, err = gisAppClient.AutomationApi.GetAutomationState(context.TODO()) if err != nil { retry++ log.Error("Failed to communicate with gis engine but retrying: ", err) time.Sleep(time.Second) } else { return true } } log.Error("Failed to communicate with gis engine: ", err) return false } else { _, _, err = gisAppClient.AutomationApi.GetAutomationState(context.TODO()) if err != nil { log.Error("Failed to communicate with gis engine: ", err) return false } } return true } func geAutomationUpdate(mobility bool, movement bool, poasInRange bool, netCharUpd bool) error { _, err := gisAppClient.AutomationApi.SetAutomationStateByName(context.TODO(), "MOBILITY", mobility) if err != nil { log.Error("Failed to communicate with gis engine: ", err) return err } _, err = gisAppClient.AutomationApi.SetAutomationStateByName(context.TODO(), "MOVEMENT", movement) if err != nil { log.Error("Failed to communicate with gis engine: ", err) return err } _, err = gisAppClient.AutomationApi.SetAutomationStateByName(context.TODO(), "POAS-IN-RANGE", poasInRange) if err != nil { log.Error("Failed to communicate with gis engine: ", err) return err } _, err = gisAppClient.AutomationApi.SetAutomationStateByName(context.TODO(), "NETWORK-CHARACTERISTICS-UPDATE", netCharUpd) if err != nil { log.Error("Failed to communicate with gis engine: ", err) return err } return nil } func geMoveAssetCoordinates(assetName string, long float32, lat float32) error { var geoData gisClient.GeoDataAsset point := gisClient.Point{"Point", []float32{long, lat}} geoData.Location = &point geoData.AssetName = assetName geoData.AssetType = "UE" geoData.SubType = "UE" err := geMoveAsset(assetName, geoData) if err != nil { log.Error("Failed to move asset: ", err) return err } return nil } func geMoveAsset(assetName string, geoData gisClient.GeoDataAsset) error { _, err := gisAppClient.GeospatialDataApi.UpdateGeoDataByName(context.TODO(), assetName, geoData) if err != nil { log.Error("Failed to communicate with gis engine: ", err) return err } return nil }