Add service status checking API.
refer: http://suprem.sec.samsung.net/jira/browse/EDGE-224
writeJSONResponse(w, smbytes, http.StatusOK)
}
}
+
// APIV1ServicemgrEventServiceIDPost function
func APIV1ServicemgrEventServiceIDPost(w http.ResponseWriter, r *http.Request) {
log.Printf("[%s] APIV1ServicemgrEventServiceIDPost", logPrefix)
}
}
+// 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")
"/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,
+ },
}
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
}
}
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())
}
}
--- /dev/null
+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)
+ }
+}
ServiceList []ServiceExecutionItem `json:"ServiceList"`
}
-// ServiceExecutionItem structrue
+// ServiceExecutionItem structure
type ServiceExecutionItem struct {
ID uint64 `json:"ID"`
Time string `json:"Time"`
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"`
+}