[EDGE-224] Implement APIs for services status check.
authorsewon.oh <sewon.oh@samsung.com>
Mon, 18 Mar 2019 11:12:51 +0000 (20:12 +0900)
committersewon.oh <sewon.oh@samsung.com>
Wed, 20 Mar 2019 07:11:46 +0000 (16:11 +0900)
Add service status checking API.
refer: http://suprem.sec.samsung.net/jira/browse/EDGE-224

src/restapi/v1/restapi.go
src/restapi/v1/routers.go
src/servicemgr/service_discovery.go
src/servicemgr/service_execution.go
src/servicemgr/test/service_discovery_test.go [new file with mode: 0644]
src/servicemgr/types.go

index 1d59741db091f457e73ca49a700b1a0df848fc38..0df85e026fa98cef777c58ac26c92516ee9adf50 100644 (file)
@@ -107,6 +107,7 @@ func APIV1ServicemgrServicesServiceIDDelete(w http.ResponseWriter, r *http.Reque
                writeJSONResponse(w, smbytes, http.StatusOK)
        }
 }
+
 // APIV1ServicemgrEventServiceIDPost function
 func APIV1ServicemgrEventServiceIDPost(w http.ResponseWriter, r *http.Request) {
        log.Printf("[%s] APIV1ServicemgrEventServiceIDPost", logPrefix)
@@ -153,6 +154,82 @@ func APIV1ServicemgrServicesServiceIDPost(w http.ResponseWriter, r *http.Request
        }
 }
 
+// APIV1ServicemgrServicesGet function
+func APIV1ServicemgrServicesGet(w http.ResponseWriter, r *http.Request) {
+       log.Printf("[%s] APIV1ServicemgrServicesGet", logPrefix)
+
+       ret, err := servicemgr.ServiceList()
+       if err != nil {
+               w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+               writeJSONResponse(w, nil, http.StatusBadRequest)
+               return
+       }
+
+       json, err := json.Marshal(ret)
+       if err == nil {
+               w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+               writeJSONResponse(w, json, http.StatusOK)
+       } else {
+               w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+               writeJSONResponse(w, nil, http.StatusBadRequest)
+       }
+}
+
+// APIV1ServicemgrServicesGetByAppName function
+func APIV1ServicemgrServicesGetByAppName(w http.ResponseWriter, r *http.Request) {
+       log.Printf("[%s] APIV1ServicemgrServicesGetByAppName", logPrefix)
+
+       vars := mux.Vars(r)
+       appName := vars["appname"]
+
+       ret, err := servicemgr.FindServiceByName(appName)
+       if err != nil {
+               w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+               writeJSONResponse(w, nil, http.StatusBadRequest)
+               return
+       }
+
+       json, err := json.Marshal(ret)
+       if err == nil {
+               w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+               writeJSONResponse(w, json, http.StatusOK)
+       } else {
+               w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+               writeJSONResponse(w, nil, http.StatusBadRequest)
+       }
+}
+
+// APIV1ServicemgrServicesGetByServiceID function
+func APIV1ServicemgrServicesGetByServiceID(w http.ResponseWriter, r *http.Request) {
+       log.Printf("[%s] APIV1ServicemgrServicesGetByServiceID", logPrefix)
+
+       vars := mux.Vars(r)
+       serviceID := vars["serviceid"]
+
+       id, err := strconv.ParseUint(serviceID, 10, 64)
+       if err != nil {
+               w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+               writeJSONResponse(w, nil, http.StatusBadRequest)
+               return
+       }
+
+       ret, err := servicemgr.FindServiceByID(id)
+       if err != nil {
+               w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+               writeJSONResponse(w, nil, http.StatusBadRequest)
+               return
+       }
+
+       json, err := json.Marshal(ret)
+       if err == nil {
+               w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+               writeJSONResponse(w, json, http.StatusOK)
+       } else {
+               w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+               writeJSONResponse(w, nil, http.StatusBadRequest)
+       }
+}
+
 func writeJSONResponse(w http.ResponseWriter, data []byte, status int) {
        log.Printf("[%s] writeJSONResponse: %s", logPrefix, data)
        w.Header().Set("Content-Type", "application/json; charset=UTF-8")
index 558afc92daf44b12d4c247ac7ea7231c5ed4b30d..8ab65f6d05a7d4641e911ee452277df0e5b37aeb 100644 (file)
@@ -142,4 +142,25 @@ var routes = Routes{
                "/api/v1/servicemgr/services/{serviceid}",
                APIV1ServicemgrServicesServiceIDPost,
        },
+
+       Route{
+               "APIV1ServicemgrServicesGet",
+               strings.ToUpper("Get"),
+               "/api/v1/servicemgr/services",
+               APIV1ServicemgrServicesGet,
+       },
+
+       Route{
+               "APIV1ServicemgrServicesGetByAppName",
+               strings.ToUpper("Get"),
+               "/api/v1/servicemgr/services/{appname}",
+               APIV1ServicemgrServicesGetByAppName,
+       },
+
+       Route{
+               "APIV1ServicemgrServicesGetByServiceID",
+               strings.ToUpper("Get"),
+               "/api/v1/servicemgr/services/{serviceid}",
+               APIV1ServicemgrServicesGetByServiceID,
+       },
 }
index 1d25051fb48e577269d0a7933b7ba07a9e9483d9..1489c6fc207c68fc6d66f0f907d2138166e3b7ea 100644 (file)
 package servicemgr
 
 import (
+       "context"
        "fmt"
+       "strconv"
+       "strings"
+       "time"
 
        "github.com/grandcat/zeroconf"
 )
 
 const (
-       //pass & fail public enum
+       // pass & fail public enum
        Fail = 0
        Pass = 1
+
+       // service type for ochestration.
+       serviceType = "_ochestration._tcp"
+       servicePort = 5737
 )
 
-var exits map[string]chan int = make(map[string]chan int)
+var exits = make(map[uint64]chan int)
 
-func RegisterService(name string, domain string, port int, ret chan int) (int, error) {
-       if name == "" {
-               ret <- Fail
-               return Fail, fmt.Errorf("Missing service name")
+func RegisterService(appName string, serviceID uint64, serviceName string, status string, ret chan error) {
+       if appName == "" {
+               ret <- fmt.Errorf("Missing app name")
+               return
        }
 
-       if domain == "" {
-               domain = "local"
+       if serviceName == "" {
+               ret <- fmt.Errorf("Missing service name")
+               return
        }
 
-       if port == 0 {
-               ret <- Fail
-               return Fail, fmt.Errorf("Missing service port")
+       if exits[serviceID] != nil {
+               ret <- fmt.Errorf("Service id is duplicated")
+               return
        }
 
-       var service string = "_" + name + "._tcp"
+       if status == "" {
+               ret <- fmt.Errorf("Missing service status")
+               return
+       }
 
-       server, err := zeroconf.Register(name, service, domain, port, []string{""}, nil)
+       appName = appName + "+" + strconv.FormatUint(serviceID, 10)
+       domain := "local"
+       text := []string{strconv.FormatUint(serviceID, 10), serviceName, status}
+       server, err := zeroconf.Register(appName, serviceType, domain, servicePort, text, nil)
        if err != nil {
-               return Fail, err
+               ret <- err
+               return
        }
        defer server.Shutdown()
 
-       exits[name] = make(chan int)
-       ret <- Pass
+       exits[serviceID] = make(chan int)
+       ret <- nil
 
        select {
-       case <-exits[name]:
-               server.Shutdown()
+       case <-exits[serviceID]:
+               fmt.Println("Service has been terminated")
        }
-
-       return Pass, nil
 }
 
-func RemoveService(name string) (int, error) {
+func RemoveService(id uint64) error {
        done := make(chan int)
 
-       if exits[name] == nil {
-               return Fail, fmt.Errorf("Service name is invalid")
+       if exits[id] == nil {
+               return fmt.Errorf("Service id is invalid")
        }
 
        go func() {
-               exits[name] <- Pass
+               exits[id] <- Pass
                done <- Pass
        }()
 
        select {
        case <-done:
-               delete(exits, name)
+               delete(exits, id)
+       }
+       return nil
+}
+
+func ServiceList() ([]AppReturnInfo, error) {
+       domain := "local"
+
+       resolver, err := zeroconf.NewResolver(nil)
+       if err != nil {
+               return nil, err
+       }
+
+       var data = make(map[string][]ServiceReturnInfo) //data[appname]
+       entries := make(chan *zeroconf.ServiceEntry)
+       go func(results <-chan *zeroconf.ServiceEntry) {
+               for entry := range results {
+                       appName := strings.Split(entry.Instance, "+")[0]
+                       var services []ServiceReturnInfo
+                       if data[appName] != nil {
+                               services = data[appName]
+                       }
+
+                       strID := entry.Text[0]
+                       if id, err := strconv.ParseUint(strID, 10, 64); err == nil {
+                               services = append(services, ServiceReturnInfo{
+                                       ServiceID:   id,
+                                       ServiceName: entry.Text[1],
+                                       Status:      entry.Text[2],
+                                       DeviceIP:    entry.AddrIPv4[0].String()})
+                       }
+                       data[appName] = services
+               }
+       }(entries)
+
+       ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(1))
+       defer cancel()
+       err = resolver.Browse(ctx, serviceType, domain, entries)
+       if err != nil {
+               return nil, err
+       }
+       <-ctx.Done()
+
+       var ret []AppReturnInfo
+       for key, value := range data {
+               ret = append(ret, AppReturnInfo{
+                       AppName:     key,
+                       ServiceList: value})
+       }
+
+       return ret, nil
+}
+
+func FindServiceByName(name string) (AppReturnInfo, error) {
+       if name == "" {
+               return AppReturnInfo{}, fmt.Errorf("App name is invalid")
+       }
+
+       domain := "local"
+       resolver, err := zeroconf.NewResolver(nil)
+       if err != nil {
+               return AppReturnInfo{}, err
+       }
+
+       app := AppReturnInfo{
+               AppName: name}
+
+       entries := make(chan *zeroconf.ServiceEntry)
+       go func(results <-chan *zeroconf.ServiceEntry) {
+               for entry := range results {
+                       strID := entry.Text[0]
+                       if id, err := strconv.ParseUint(strID, 10, 64); err == nil &&
+                               name == strings.Split(entry.Instance, "+")[0] {
+                               app.ServiceList = append(app.ServiceList, ServiceReturnInfo{
+                                       ServiceID:   id,
+                                       ServiceName: entry.Text[1],
+                                       Status:      entry.Text[2],
+                                       DeviceIP:    entry.AddrIPv4[0].String()})
+                       }
+               }
+       }(entries)
+
+       ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(1))
+       defer cancel()
+       err = resolver.Browse(ctx, serviceType, domain, entries)
+       if err != nil {
+               return AppReturnInfo{}, err
+       }
+
+       <-ctx.Done()
+       return app, nil
+}
+
+func FindServiceByID(serviceID uint64) (ServiceReturnInfo, error) {
+       domain := "local"
+
+       resolver, err := zeroconf.NewResolver(nil)
+       if err != nil {
+               return ServiceReturnInfo{}, err
+       }
+
+       ctx, cancel := context.WithCancel(context.Background())
+       defer cancel()
+
+       var ret ServiceReturnInfo
+       entries := make(chan *zeroconf.ServiceEntry)
+       go func(results <-chan *zeroconf.ServiceEntry) {
+               for entry := range results {
+                       if strconv.FormatUint(serviceID, 10) == entry.Text[0] {
+                               ret.ServiceID = serviceID
+                               ret.ServiceName = entry.Text[1]
+                               ret.Status = entry.Text[2]
+                               ret.DeviceIP = entry.AddrIPv4[0].String()
+                               cancel()
+                               return
+                       }
+               }
+       }(entries)
+
+       err = resolver.Browse(ctx, serviceType, domain, entries)
+       if err != nil {
+               return ServiceReturnInfo{}, err
+       }
+       <-ctx.Done()
+
+       return ret, nil
+}
+
+func ChangeServiceStatus(serviceID uint64, status string) error {
+       domain := "local"
+
+       resolver, err := zeroconf.NewResolver(nil)
+       if err != nil {
+               return err
+       }
+
+       ctx, cancel := context.WithCancel(context.Background())
+       defer cancel()
+
+       var appName string
+       var service ServiceReturnInfo
+       entries := make(chan *zeroconf.ServiceEntry)
+       go func(results <-chan *zeroconf.ServiceEntry) {
+               for entry := range results {
+                       if strconv.FormatUint(serviceID, 10) == entry.Text[0] {
+                               appName = strings.Split(entry.Instance, "+")[0]
+                               service.ServiceID = serviceID
+                               service.ServiceName = entry.Text[1]
+                               service.Status = entry.Text[2]
+                               service.DeviceIP = entry.AddrIPv4[0].String()
+                               cancel()
+                               return
+                       }
+               }
+       }(entries)
+
+       err = resolver.Browse(ctx, serviceType, domain, entries)
+       if err != nil {
+               return err
+       }
+       <-ctx.Done()
+
+       RemoveService(serviceID)
+
+       ret := make(chan error)
+       go RegisterService(appName, serviceID, service.ServiceName, status, ret)
+
+       if err := <-ret; err != nil {
+               return err
        }
-       return Pass, nil
+
+       return nil
 }
index 843b3d193e1c31f8bc15343c5dd87d117fcc38ef..8f563807122e46588cfa897983f7dd2b21634961 100644 (file)
@@ -56,24 +56,28 @@ func (p Service) execute() error {
 }
 
 func (p Service) registerService() {
-       registerCh := make(chan int)
+       registerCh := make(chan error)
 
        // portNumber is not meaningful number now, so use magic number temporarily
-       portNumber := 2838
-       go RegisterService(p.serviceName, "", portNumber, registerCh)
-       if <-registerCh == Pass {
-               log.Println("Service Register is Success")
+       appName, err := GetAppName(p.serviceID)
+       if err != nil {
+               log.Println("[Fail] GetAppName is Failed")
+       }
+
+       go RegisterService(appName, p.serviceID, p.serviceName, "todo", registerCh)
+       if err := <-registerCh; err == nil {
+               log.Println("[Success] Service Register is Success")
        } else {
-               log.Println("Service Register is Failed")
+               log.Println("[Fail] " + err.Error())
        }
 }
 
 func (p Service) removeService() {
-       res, _ := RemoveService(p.serviceName)
-       if res == Pass {
-               log.Println("Service Unregister is Success")
+       err := RemoveService(p.serviceID)
+       if err == nil {
+               log.Println("[Success] Service Unregister is Success")
        } else {
-               log.Println("Service Unregister is Failed")
+               log.Println("[Fail] " + err.Error())
        }
 }
 
diff --git a/src/servicemgr/test/service_discovery_test.go b/src/servicemgr/test/service_discovery_test.go
new file mode 100644 (file)
index 0000000..02100f8
--- /dev/null
@@ -0,0 +1,160 @@
+package test
+
+import (
+       "reflect"
+       sm "servicemgr"
+       "testing"
+)
+
+var data = sm.AppReturnInfo{
+       AppName: "GreetWorldApp",
+       ServiceList: []sm.ServiceReturnInfo{
+               {
+                       ServiceID:   1,
+                       ServiceName: "hello world#1",
+                       Status:      "started",
+                       DeviceIP:    "10.113.175.144",
+               },
+               {
+                       ServiceID:   2,
+                       ServiceName: "hello world#2",
+                       Status:      "progressing",
+                       DeviceIP:    "10.113.175.144",
+               },
+       },
+}
+
+func TestServiceRegisterRemove(t *testing.T) {
+       ret := make(chan error)
+
+       go sm.RegisterService(data.AppName, data.ServiceList[0].ServiceID,
+               data.ServiceList[0].ServiceName, data.ServiceList[0].Status, ret)
+
+       if err := <-ret; err != nil {
+               t.Error(err)
+       }
+
+       err := sm.RemoveService(data.ServiceList[0].ServiceID)
+
+       if err != nil {
+               t.Error(err)
+       }
+}
+
+func TestServiceList(t *testing.T) {
+       ret := make(chan error)
+
+       for _, service := range data.ServiceList {
+               go sm.RegisterService(data.AppName, service.ServiceID,
+                       service.ServiceName, service.Status, ret)
+
+               if err := <-ret; err != nil {
+                       t.Error(err)
+               }
+       }
+
+       cmp, err := sm.ServiceList()
+
+       t.Log(cmp[0])
+       if len(cmp[0].ServiceList) != 2 {
+               t.Error("fail")
+       }
+
+       for _, service := range data.ServiceList {
+               err = sm.RemoveService(service.ServiceID)
+
+               if err != nil {
+                       t.Error(err)
+               }
+       }
+}
+
+func TestFindServiceByName(t *testing.T) {
+       ret := make(chan error)
+
+       for _, service := range data.ServiceList {
+               go sm.RegisterService(data.AppName, service.ServiceID,
+                       service.ServiceName, service.Status, ret)
+
+               if err := <-ret; err != nil {
+                       t.Error(err)
+               }
+       }
+
+       cmp, err := sm.FindServiceByName(data.AppName)
+
+       t.Log(cmp)
+       if len(cmp.ServiceList) != 2 {
+               t.Error("fail")
+       }
+
+       for _, service := range data.ServiceList {
+               err = sm.RemoveService(service.ServiceID)
+
+               if err != nil {
+                       t.Error(err)
+               }
+       }
+}
+
+func TestFindServiceByID(t *testing.T) {
+       ret := make(chan error)
+
+       for _, service := range data.ServiceList {
+               go sm.RegisterService(data.AppName, service.ServiceID,
+                       service.ServiceName, service.Status, ret)
+
+               if err := <-ret; err != nil {
+                       t.Error(err)
+               }
+       }
+
+       cmp, err := sm.FindServiceByID(data.ServiceList[0].ServiceID)
+
+       t.Log(cmp)
+       if !reflect.DeepEqual(cmp, data.ServiceList[0]) {
+               t.Error("[Fail] FindServiceByID is failed")
+       }
+
+       for _, service := range data.ServiceList {
+               err = sm.RemoveService(service.ServiceID)
+
+               if err != nil {
+                       t.Error(err)
+               }
+       }
+}
+
+func TestChangeServiceStatus(t *testing.T) {
+       ret := make(chan error)
+
+       go sm.RegisterService(data.AppName, data.ServiceList[0].ServiceID,
+               data.ServiceList[0].ServiceName, data.ServiceList[0].Status, ret)
+
+       if err := <-ret; err != nil {
+               t.Error(err)
+       }
+
+       cmp, err := sm.FindServiceByID(data.ServiceList[0].ServiceID)
+       t.Log(cmp)
+       if !reflect.DeepEqual(cmp, data.ServiceList[0]) {
+               t.Error("[Fail] FindServiceByID is failed")
+       }
+
+       err = sm.ChangeServiceStatus(cmp.ServiceID, "progressing")
+       if err != nil {
+               t.Error(err)
+       }
+
+       cmp, err = sm.FindServiceByID(data.ServiceList[0].ServiceID)
+       t.Log(cmp)
+       if !reflect.DeepEqual(cmp.Status, "progressing") {
+               t.Error("[Fail] Status is not equal")
+       }
+
+       err = sm.RemoveService(data.ServiceList[0].ServiceID)
+
+       if err != nil {
+               t.Error(err)
+       }
+}
index 2a9955f763a3587de0723253ddbf66592728b76b..ce38f53fc9f77b3aca23fa6e2f727da132085d02 100644 (file)
@@ -47,7 +47,7 @@ type ServiceExecutionResponse struct {
        ServiceList []ServiceExecutionItem `json:"ServiceList"`
 }
 
-// ServiceExecutionItem structrue
+// ServiceExecutionItem structure
 type ServiceExecutionItem struct {
        ID   uint64 `json:"ID"`
        Time string `json:"Time"`
@@ -63,3 +63,17 @@ type MsgFormat struct {
 type MsgHeader struct {
        Type string `json:"Type"`
 }
+
+// ServiceReturnInfo structure
+type ServiceReturnInfo struct {
+       ServiceID   uint64 `json:"ServiceID"`
+       ServiceName string `json:"ServiceName"`
+       Status      string `json:"Status"`
+       DeviceIP    string `json:"DeviceIP"`
+}
+
+//AppReturnInfo structure
+type AppReturnInfo struct {
+       AppName     string              `json:"AppName"`
+       ServiceList []ServiceReturnInfo `json:"ServiceList"`
+}