Пример #1
0
func (s *SkynetDaemon) Deploy(requestInfo *skynet.RequestInfo, in daemon.DeployRequest, out *daemon.DeployResponse) (err error) {
	out.UUID = skynet.UUID()

	s.Log.Trace(fmt.Sprintf("%+v", SubserviceDeployment{
		ServicePath: in.ServicePath,
		Args:        in.Args,
	}))

	ss, err := NewSubService(s, in.ServicePath, in.Args, out.UUID)
	if err != nil {
		return
	}

	s.serviceLock.Lock()
	s.Services[out.UUID] = ss
	s.serviceLock.Unlock()

	start, startErr := ss.Start()

	if startErr != nil {
		return errors.New("Service failed to start: " + startErr.Error())
	} else if !start {
		return errors.New("Service failed to start")
	}

	return
}
Пример #2
0
// ServiceClient.sendToInstance() tries to make an RPC request on a particular connection to an instance
func (c *ServiceClient) sendToInstance(sr ServiceResource, requestInfo *skynet.RequestInfo, funcName string, in interface{}) (result []byte, err error) {
	if requestInfo == nil {
		requestInfo = &skynet.RequestInfo{
			RequestID: skynet.UUID(),
		}
	}

	sin := service.ServiceRPCIn{
		RequestInfo: requestInfo,
		Method:      funcName,
	}

	sin.In, err = bson.Marshal(in)
	if err != nil {
		return
	}

	sout := service.ServiceRPCOut{}

	err = sr.rpcClient.Call(sr.service.Config.Name+".Forward", sin, &sout)
	if err != nil {
		sr.Close()
		c.Log.Item(err)
	}

	if sout.ErrString != "" {
		err = serviceError{sout.ErrString}
	}

	result = sout.Out

	return
}
Пример #3
0
func main() {
	var err error

	flag.Parse()

	log = skynet.NewConsoleSemanticLogger("dashboard", os.Stderr)
	if *mgoserver == "" {
		log.Trace(fmt.Sprintf("%+v", skynet.MongoError{
			"",
			"No mongodb server url (both -mgoserver and SKYNET_MGOSERVER missing)",
		}))
	}

	mlogger, err := skynet.NewMongoSemanticLogger(*mgoserver, "skynet",
		"log", skynet.UUID())
	if err != nil {
		log.Error(fmt.Sprintf("%+v", skynet.MongoError{
			Addr: "Could not connect to mongo db for logging",
			Err:  err.Error(),
		}))
	}
	log = skynet.NewMultiSemanticLogger(mlogger, log)

	DC = Doozer()

	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/logs/search", searchHandler)
	http.Handle("/media/", http.StripPrefix("/media/", http.FileServer(http.Dir(*webroot+"/tmpl"))))
	http.Handle("/favicon.ico", http.FileServer(http.Dir(*webroot+"/tmpl/images")))
	http.Handle("/logs/ws", websocket.Handler(wsHandler))

	im := client.NewInstanceMonitor(DC, true)
	http.Handle("/instances/ws", websocket.Handler(func(ws *websocket.Conn) {
		NewInstanceSocket(ws, im)
	}))

	// Cache templates
	layoutTmpl = template.Must(template.ParseFiles(*webroot +
		"/tmpl/layout.html.template"))
	indexTmpl = template.Must(template.ParseFiles(*webroot +
		"/tmpl/index.html.template"))
	searchTmpl = template.Must(template.ParseFiles(*webroot +
		"/tmpl/search.html.template"))

	err = http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: " + err.Error())
	}
}
Пример #4
0
func (s *SkynetDaemon) Deploy(requestInfo *skynet.RequestInfo, in DeployIn, out *DeployOut) (err error) {
	out.UUID = skynet.UUID()

	s.Log.Item(SubserviceDeployment{
		ServicePath: in.ServicePath,
		Args:        in.Args,
	})

	ss, err := NewSubService(s.Log, in.ServicePath, in.Args, out.UUID)
	if err != nil {
		return
	}
	s.serviceLock.Lock()
	s.Services[out.UUID] = ss
	s.serviceLock.Unlock()
	return
}
Пример #5
0
func newServiceClient(query *Query, c *Client) (sc *ServiceClient) {
	sc = &ServiceClient{
		client:      c,
		Log:         c.Config.Log,
		cconfig:     c.Config,
		query:       query,
		instances:   make(map[string]*servicePool),
		chooser:     NewInstanceChooser(),
		muxChan:     make(chan interface{}),
		timeoutChan: make(chan timeoutLengths),
	}
	sc.listenID = skynet.UUID()
	sc.instanceListener = c.instanceMonitor.Listen(sc.listenID, query)

	go sc.mux()
	return
}
Пример #6
0
func newServiceClient(query *skynet.Query, c *Client) (sc *ServiceClient) {
	sc = &ServiceClient{
		client:        c,
		Log:           c.Config.Log,
		cconfig:       c.Config,
		query:         query,
		instances:     make(map[string]*servicePool),
		chooser:       NewInstanceChooser(c),
		muxChan:       make(chan interface{}),
		timeoutChan:   make(chan timeoutLengths),
		retryTimeout:  skynet.DefaultRetryDuration,
		giveupTimeout: skynet.DefaultTimeoutDuration,
	}
	sc.listenID = skynet.UUID()
	sc.instanceListener = c.instanceMonitor.Listen(sc.listenID, query, false)

	go sc.mux()
	return
}
Пример #7
0
func (c *ServiceClient) Send(requestInfo *skynet.RequestInfo, funcName string, in interface{}, outPointer interface{}) (err error) {
	// TODO: timeout logic
	s, err := c.getConnection(0)
	if err != nil {
		c.Log.Item(err)
		return
	}

	if requestInfo == nil {
		requestInfo = &skynet.RequestInfo{
			RequestID: skynet.UUID(),
		}
	}

	sin := service.ServiceRPCIn{
		RequestInfo: requestInfo,
		Method:      funcName,
	}

	sin.In, err = bson.Marshal(in)
	if err != nil {
		return
	}

	sout := service.ServiceRPCOut{}

	// TODO: Check for connectivity issue so that we can try to get another resource out of the pool
	err = s.rpcClient.Call(s.service.Config.Name+".Forward", sin, &sout)
	if err != nil {
		c.Log.Item(err)
	}

	err = bson.Unmarshal(sout.Out, outPointer)
	if err != nil {
		return
	}

	c.connectionPool.Put(s)

	return
}
Пример #8
0
func (s *SkynetDaemon) Deploy(requestInfo *skynet.RequestInfo, in M, out *M) (err error) {
	*out = map[string]interface{}{}
	uuid := skynet.UUID()
	(*out)["uuid"] = uuid

	servicePath := in["service"].(string)
	args := in["args"].(string)

	s.Log.Item(SubserviceDeployment{
		ServicePath: servicePath,
		Args:        args,
	})

	ss, err := NewSubService(s.Log, servicePath, args, uuid)
	if err != nil {
		return
	}
	s.serviceLock.Lock()
	s.Services[uuid] = ss
	s.serviceLock.Unlock()
	return
}
Пример #9
0
func (c *ServiceClient) send(retry, giveup time.Duration, ri *skynet.RequestInfo, fn string, in interface{}, out interface{}) (err error) {
	if ri == nil {
		ri = &skynet.RequestInfo{
			RequestID: skynet.UUID(),
		}
	}

	attempts := make(chan sendAttempt)

	var ticker <-chan time.Time
	if retry > 0 {
		ticker = time.NewTicker(retry).C
	}

	var timeout <-chan time.Time
	if giveup > 0 {
		timeout = time.NewTimer(giveup).C
	}

	doneSignal := make(chan bool)
	attemptCount := 1

	defer func() {
		go func() {
			for i := 0; i < attemptCount; i++ {
				doneSignal <- true
			}
		}()
	}()

	go c.attemptSend(doneSignal, attempts, ri, fn, in)

	for {
		select {
		case <-ticker:
			attemptCount++
			ri.RetryCount++

			go c.attemptSend(doneSignal, attempts, ri, fn, in)
		case <-timeout:
			if err == nil {
				err = ErrRequestTimeout
			}
			// otherwise use the last error reported from an attempt
			return
		case attempt := <-attempts:
			err = attempt.err
			if err != nil {
				if _, ok := err.(serviceError); !ok {
					// error during transmition, abort this attempt
					if giveup == 0 {
						return
					}
					continue
				}
			}

			unmarshallerr := bson.Unmarshal(attempt.result, out)
			if unmarshallerr != nil {
				err = unmarshallerr
			}
			return
		}
	}

	return
}
Пример #10
0
func NewInstanceSocket(ws *websocket.Conn, im *client.InstanceMonitor) {
	closeChan := make(chan bool, 1)
	readChan := make(chan SocketRequest)
	ticker := time.NewTicker(5 * time.Second)
	lastHeartbeat := time.Now()

	go instanceSocketRead(ws, readChan, closeChan)

	l := im.Listen(skynet.UUID(), &client.Query{})

	instances := <-l.NotificationChan
	err := websocket.JSON.Send(ws, SocketResponse{Action: "List", Data: instances})

	if err != nil {
		closeChan <- true
	}

	for {
		select {
		case <-closeChan:
			ticker.Stop()
			ws.Close()
			l.Close()
		case t := <-ticker.C:
			// Check for timeout
			if t.Sub(lastHeartbeat) > (15 * time.Second) {
				closeChan <- true
			}
		case request := <-readChan:
			lastHeartbeat = time.Now()

			switch request.Action {
			case "List":
				err := websocket.JSON.Send(ws, SocketResponse{Action: "List", Data: instances})
				if err != nil {
					closeChan <- true
				}
			case "Heartbeat":
				// this is here more for documentation purposes, setting the lastHeartbeat on read handles the logic here
			case "Filter":
				if request.Data != nil {
					data := request.Data.(map[string]interface{})

					if r, ok := data["Reset"]; ok {
						reset := r.(bool)
						if reset {
							l.Query.Reset()
						}
					}

					if r, ok := data["Registered"]; ok {
						filter := r.(bool)
						l.Query.Registered = &filter
					}
				}

				instances := l.GetInstances()
				iln := make(client.InstanceListenerNotification)
				for _, i := range instances {
					path := i.GetConfigPath()
					iln[path] = client.InstanceMonitorNotification{
						Path:    path,
						Service: i,
						Type:    client.InstanceAddNotification,
					}
				}

				err := websocket.JSON.Send(ws, SocketResponse{Action: "List", Data: iln})

				if err != nil {
					closeChan <- true
				}
			}

		case notification := <-l.NotificationChan:
			var err error

			// Forward message as it stands across the websocket
			err = websocket.JSON.Send(ws, SocketResponse{Action: "Update", Data: notification})

			instances = instances.Join(notification)

			if err != nil {
				closeChan <- true
			}
		}
	}
}
Пример #11
0
// this function is the goroutine that owns this service - all thread-sensitive data needs to
// be manipulated only through here.
func (s *Service) mux() {
loop:
	for {
		select {
		case conn := <-s.connectionChan:
			atomic.AddInt32(&s.Stats.Clients, 1)

			clientID := skynet.UUID()

			s.clientMutex.Lock()
			s.clientInfo[clientID] = ClientInfo{
				Address: conn.RemoteAddr(),
			}
			s.clientMutex.Unlock()

			// send the server handshake
			sh := skynet.ServiceHandshake{
				Registered: s.Registered,
				ClientID:   clientID,
			}
			encoder := bsonrpc.NewEncoder(conn)
			err := encoder.Encode(sh)
			if err != nil {
				s.Log.Item(err)

				atomic.AddInt32(&s.Stats.Clients, -1)
				break
			}
			if !s.Registered {
				conn.Close()
				atomic.AddInt32(&s.Stats.Clients, -1)
				break
			}

			// read the client handshake
			var ch skynet.ClientHandshake
			decoder := bsonrpc.NewDecoder(conn)
			err = decoder.Decode(&ch)
			if err != nil {
				s.Log.Item(err)
				atomic.AddInt32(&s.Stats.Clients, -1)
				break
			}

			// here do stuff with the client handshake

			go func() {
				s.RPCServ.ServeCodec(bsonrpc.NewServerCodec(conn))

				atomic.AddInt32(&s.Stats.Clients, -1)
			}()
		case register := <-s.registeredChan:
			if register {
				s.register()
			} else {
				s.unregister()
			}
		case _ = <-s.doneChan:
			go func() {
				for _ = range s.doneChan {
				}
			}()
			s.RemoveFromCluster()
			s.doozerChan <- doozerFinish{}
			break loop

		case _ = <-s.updateTicker.C:
			s.UpdateCluster()
		}
	}
}