mirror of
https://gitee.com/rainbond/Rainbond.git
synced 2024-11-29 18:27:58 +08:00
[ADD] add windows service manager code
This commit is contained in:
parent
5641c809b1
commit
77cd220133
5
Makefile
5
Makefile
@ -35,8 +35,9 @@ image:
|
||||
binary:
|
||||
@echo "🐳build binary ${WHAT} os ${GOOS}"
|
||||
@ GOOS=${GOOS} bash ./release.sh binary ${WHAT}
|
||||
|
||||
run:build image
|
||||
run-c:image
|
||||
test/run/run_${WHAT}.sh
|
||||
run:build
|
||||
ifeq ($(WHAT),api)
|
||||
${BIN_PATH}/${BASE_NAME}-api --log-level=debug \
|
||||
--mysql="root:@tcp(127.0.0.1:3306)/region" \
|
||||
|
@ -22,12 +22,16 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/goodrain/rainbond/cmd/node/option"
|
||||
"github.com/goodrain/rainbond/cmd/node/server"
|
||||
)
|
||||
|
||||
func main() {
|
||||
server.ParseClientCommnad(os.Args)
|
||||
option.Config.AddFlags(pflag.CommandLine)
|
||||
server.InstallServiceFlags(pflag.CommandLine)
|
||||
option.Init()
|
||||
if err := server.Run(option.Config); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
|
@ -19,6 +19,7 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"path"
|
||||
@ -49,7 +50,6 @@ func Init() error {
|
||||
if initialized {
|
||||
return nil
|
||||
}
|
||||
Config.AddFlags(pflag.CommandLine)
|
||||
pflag.Parse()
|
||||
Config.SetLog()
|
||||
if err := Config.parse(); err != nil {
|
||||
@ -154,7 +154,7 @@ func (a *Conf) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.IntVar(&a.FailTime, "failTime", 3, "the fail time of healthy check")
|
||||
fs.IntVar(&a.CheckIntervalSec, "checkInterval-second", 5, "the interval time of healthy check")
|
||||
fs.StringSliceVar(&a.Etcd.Endpoints, "etcd", []string{"http://127.0.0.1:2379"}, "the path of node in etcd")
|
||||
fs.DurationVar(&a.Etcd.DialTimeout, "etcd-dialTimeOut", 2*time.Second, "etcd cluster dialTimeOut.")
|
||||
fs.DurationVar(&a.Etcd.DialTimeout, "etcd-dialTimeOut", 3, "etcd cluster dialTimeOut In seconds")
|
||||
fs.IntVar(&a.ReqTimeout, "reqTimeOut", 2, "req TimeOut.")
|
||||
fs.Int64Var(&a.TTL, "ttl", 10, "node timeout second")
|
||||
fs.Int64Var(&a.ProcTTL, "procttl", 600, "proc ttl")
|
||||
@ -190,16 +190,24 @@ func (a *Conf) SetLog() {
|
||||
logrus.SetLevel(level)
|
||||
}
|
||||
|
||||
//Parse handle config and create some api
|
||||
func (a *Conf) Parse() (err error) {
|
||||
//ParseClient handle config and create some api
|
||||
func (a *Conf) ParseClient() (err error) {
|
||||
a.DockerCli, err = dockercli.NewEnvClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.EtcdCli, err = client.New(a.Etcd)
|
||||
if err != nil {
|
||||
return err
|
||||
logrus.Infof("begin create etcd client: %s", a.Etcd.Endpoints)
|
||||
for {
|
||||
a.EtcdCli, err = client.New(a.Etcd)
|
||||
if err != nil {
|
||||
logrus.Errorf("create etcd client failure %s, will retry after 3 second", err.Error())
|
||||
}
|
||||
if err == nil && a.EtcdCli != nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
logrus.Infof("create etcd client success")
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -231,15 +239,19 @@ func cleanKeyPrefix(p string) string {
|
||||
}
|
||||
|
||||
//parse parse
|
||||
func (c *Conf) parse() error {
|
||||
if c.Etcd.DialTimeout > 0 {
|
||||
c.Etcd.DialTimeout *= time.Second
|
||||
func (a *Conf) parse() error {
|
||||
if a.Etcd.DialTimeout < 3 {
|
||||
a.Etcd.DialTimeout = time.Second * 3
|
||||
} else {
|
||||
a.Etcd.DialTimeout = a.Etcd.DialTimeout * time.Second
|
||||
}
|
||||
if c.TTL <= 0 {
|
||||
c.TTL = 10
|
||||
|
||||
a.Etcd.Context = context.Background()
|
||||
if a.TTL <= 0 {
|
||||
a.TTL = 10
|
||||
}
|
||||
if c.LockTTL < 2 {
|
||||
c.LockTTL = 300
|
||||
if a.LockTTL < 2 {
|
||||
a.LockTTL = 300
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ import (
|
||||
// node reg : Register the daemon configuration for node
|
||||
// node run: daemon start node server
|
||||
func ParseClientCommnad(args []string) {
|
||||
fmt.Println(args)
|
||||
if len(args) > 1 {
|
||||
switch args[1] {
|
||||
case "version":
|
||||
|
@ -40,11 +40,25 @@ import (
|
||||
|
||||
//Run start run
|
||||
func Run(c *option.Conf) error {
|
||||
if err := c.Parse(); err != nil {
|
||||
stop, err := initService(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if stop {
|
||||
return nil
|
||||
}
|
||||
nodemanager, err := nodem.NewNodeManager(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create node manager failed: %s", err)
|
||||
}
|
||||
if err := nodemanager.InitStart(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.ParseClient(); err != nil {
|
||||
return fmt.Errorf("config parse error:%s", err.Error())
|
||||
}
|
||||
errChan := make(chan error, 3)
|
||||
err := eventLog.NewManager(eventLog.EventConfig{
|
||||
err = eventLog.NewManager(eventLog.EventConfig{
|
||||
EventLogServers: c.EventLogServer,
|
||||
DiscoverAddress: c.Etcd.Endpoints,
|
||||
})
|
||||
@ -53,24 +67,24 @@ func Run(c *option.Conf) error {
|
||||
return nil
|
||||
}
|
||||
defer eventLog.CloseManager()
|
||||
|
||||
logrus.Debug("create and start event log client success")
|
||||
kubecli, err := kubecache.NewKubeClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer kubecli.Stop()
|
||||
|
||||
logrus.Debug("create and start kube cache moudle success")
|
||||
// init etcd client
|
||||
if err = store.NewClient(c); err != nil {
|
||||
return fmt.Errorf("Connect to ETCD %s failed: %s", c.Etcd.Endpoints, err)
|
||||
}
|
||||
nodemanager, err := nodem.NewNodeManager(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create node manager failed: %s", err)
|
||||
}
|
||||
if err := nodemanager.Start(errChan); err != nil {
|
||||
return fmt.Errorf("start node manager failed: %s", err)
|
||||
}
|
||||
defer nodemanager.Stop()
|
||||
logrus.Debug("create and start node manager moudle success")
|
||||
|
||||
//master服务在node服务之后启动
|
||||
var ms *masterserver.MasterServer
|
||||
if c.RunMode == "master" {
|
||||
@ -85,6 +99,7 @@ func Run(c *option.Conf) error {
|
||||
return err
|
||||
}
|
||||
defer ms.Stop(nil)
|
||||
logrus.Debug("create and start master server moudle success")
|
||||
}
|
||||
//create api manager
|
||||
apiManager := api.NewManager(*c, nodemanager.GetCurrentNode(), ms, kubecli)
|
||||
@ -95,6 +110,7 @@ func Run(c *option.Conf) error {
|
||||
return err
|
||||
}
|
||||
defer apiManager.Stop()
|
||||
logrus.Debug("create and start api server moudle success")
|
||||
|
||||
defer controller.Exist(nil)
|
||||
//step finally: listen Signal
|
||||
|
33
cmd/node/server/service_nosupport.go
Normal file
33
cmd/node/server/service_nosupport.go
Normal file
@ -0,0 +1,33 @@
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/goodrain/rainbond/cmd/node/option"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
//InstallServiceFlags install service flag set
|
||||
func InstallServiceFlags(flags *pflag.FlagSet) {
|
||||
}
|
||||
func initService(*option.Conf) (bool, error) {
|
||||
return false, nil
|
||||
}
|
91
cmd/node/server/service_windows.go
Normal file
91
cmd/node/server/service_windows.go
Normal file
@ -0,0 +1,91 @@
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/goodrain/rainbond/cmd/node/option"
|
||||
utilwindows "github.com/goodrain/rainbond/util/windows"
|
||||
"github.com/spf13/pflag"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
flRegisterService *bool
|
||||
flUnregisterService *bool
|
||||
flServiceName *string
|
||||
|
||||
setStdHandle = windows.NewLazySystemDLL("kernel32.dll").NewProc("SetStdHandle")
|
||||
oldStderr windows.Handle
|
||||
panicFile *os.File
|
||||
)
|
||||
|
||||
//InstallServiceFlags install service flag set
|
||||
func InstallServiceFlags(flags *pflag.FlagSet) {
|
||||
flServiceName = flags.String("service-name", "rainbond-node", "Set the Windows service name")
|
||||
flRegisterService = flags.Bool("register-service", false, "Register the service and exit")
|
||||
flUnregisterService = flags.Bool("unregister-service", false, "Unregister the service and exit")
|
||||
}
|
||||
func getServicePath() (string, error) {
|
||||
p, err := exec.LookPath(os.Args[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Abs(p)
|
||||
}
|
||||
|
||||
// initService is the entry point for running the daemon as a Windows
|
||||
// service. It returns an indication to stop (if registering/un-registering);
|
||||
// an indication of whether it is running as a service; and an error.
|
||||
func initService(conf *option.Conf) (bool, error) {
|
||||
if *flUnregisterService {
|
||||
if *flRegisterService {
|
||||
return true, errors.New("--register-service and --unregister-service cannot be used together")
|
||||
}
|
||||
return true, unregisterService()
|
||||
}
|
||||
|
||||
if *flRegisterService {
|
||||
return true, registerService()
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func unregisterService() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerService() error {
|
||||
p, err := getServicePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Configure the service to launch with the arguments that were just passed.
|
||||
args := []string{""}
|
||||
for _, a := range os.Args[1:] {
|
||||
if a != "--register-service" && a != "--unregister-service" {
|
||||
args = append(args, a)
|
||||
}
|
||||
}
|
||||
return utilwindows.RegisterService(*flServiceName, p, "Rainbond NodeManager", []string{}, args)
|
||||
}
|
@ -19,8 +19,12 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/goodrain/rainbond/cmd/node/option"
|
||||
"github.com/goodrain/rainbond/node/nodem/service"
|
||||
"github.com/goodrain/rainbond/util/windows"
|
||||
)
|
||||
|
||||
//NewController At the stage you want to load the configurations of all rainbond components
|
||||
@ -37,28 +41,46 @@ type windowsServiceController struct {
|
||||
}
|
||||
|
||||
func (w *windowsServiceController) InitStart(services []*service.Service) error {
|
||||
for _, s := range services {
|
||||
if s.IsInitStart {
|
||||
if err := w.WriteConfig(s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.StartService(s.Name); err != nil {
|
||||
return fmt.Errorf("start windows service %s failure %s", s.Name, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *windowsServiceController) StartService(name string) error {
|
||||
return nil
|
||||
return windows.StartService(name)
|
||||
}
|
||||
func (w *windowsServiceController) StopService(name string) error {
|
||||
return nil
|
||||
return windows.StopService(name)
|
||||
}
|
||||
func (w *windowsServiceController) StartList(list []*service.Service) error {
|
||||
for _, s := range list {
|
||||
w.StartService(s.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (w *windowsServiceController) StopList(list []*service.Service) error {
|
||||
for _, s := range list {
|
||||
w.StopService(s.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (w *windowsServiceController) RestartService(serviceName string) error {
|
||||
return nil
|
||||
return windows.RestartService(serviceName)
|
||||
}
|
||||
func (w *windowsServiceController) WriteConfig(s *service.Service) error {
|
||||
return nil
|
||||
cmds := strings.Split(s.Start, " ")
|
||||
return windows.RegisterService(s.Name, cmds[0], "Rainbond "+s.Name, s.Requires, cmds)
|
||||
}
|
||||
func (w *windowsServiceController) RemoveConfig(name string) error {
|
||||
return nil
|
||||
return windows.UnRegisterService(name)
|
||||
}
|
||||
func (w *windowsServiceController) EnableService(name string) error {
|
||||
return nil
|
||||
|
@ -191,11 +191,8 @@ func (m *ControllerSystemd) run(args ...string) error {
|
||||
|
||||
//InitStart init start. will start some required service
|
||||
func (m *ControllerSystemd) InitStart(services []*service.Service) error {
|
||||
if err := m.run("start", "docker"); err != nil {
|
||||
return fmt.Errorf("systemctl start docker error:%s", err.Error())
|
||||
}
|
||||
for _, s := range services {
|
||||
if s.Name == "etcd" {
|
||||
if s.IsInitStart {
|
||||
fileName := fmt.Sprintf("/etc/systemd/system/%s.service", s.Name)
|
||||
content := ToConfig(s)
|
||||
if content == "" {
|
||||
|
@ -101,14 +101,20 @@ func (n *NodeManager) AddAPIManager(apim *api.Manager) error {
|
||||
return n.monitor.SetAPIRoute(apim)
|
||||
}
|
||||
|
||||
//Start start
|
||||
func (n *NodeManager) Start(errchan chan error) error {
|
||||
//InitStart init start is first start module.
|
||||
//it would not depend etcd
|
||||
func (n *NodeManager) InitStart() error {
|
||||
if err := n.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := n.controller.Start(n.HostNode); err != nil {
|
||||
return fmt.Errorf("start node controller error,%s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Start start
|
||||
func (n *NodeManager) Start(errchan chan error) error {
|
||||
services, err := n.controller.GetAllService()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get all services error,%s", err.Error())
|
||||
|
@ -38,6 +38,7 @@ type Service struct {
|
||||
Endpoints []*Endpoint `yaml:"endpoints,omitempty"`
|
||||
ServiceHealth *Health `yaml:"health"`
|
||||
OnlyHealthCheck bool `yaml:"only_health_check"`
|
||||
IsInitStart bool `yaml:"is_init_start"`
|
||||
Disable bool `yaml:"disable"`
|
||||
After []string `yaml:"after"`
|
||||
Requires []string `yaml:"requires"`
|
||||
|
167
util/windows/service_windows.go
Normal file
167
util/windows/service_windows.go
Normal file
@ -0,0 +1,167 @@
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
)
|
||||
|
||||
//RegisterService register a service
|
||||
func RegisterService(serviceName, execPath, displayName string, depends, args []string) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
c := mgr.Config{
|
||||
ServiceType: windows.SERVICE_WIN32_OWN_PROCESS,
|
||||
StartType: mgr.StartAutomatic,
|
||||
ErrorControl: mgr.ErrorNormal,
|
||||
Dependencies: depends,
|
||||
DisplayName: displayName,
|
||||
}
|
||||
|
||||
s, err := m.CreateService(serviceName, execPath, c, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
// See http://stackoverflow.com/questions/35151052/how-do-i-configure-failure-actions-of-a-windows-service-written-in-go
|
||||
const (
|
||||
scActionNone = 0
|
||||
scActionRestart = 1
|
||||
scActionReboot = 2
|
||||
scActionRunCommand = 3
|
||||
|
||||
serviceConfigFailureActions = 2
|
||||
)
|
||||
|
||||
type serviceFailureActions struct {
|
||||
ResetPeriod uint32
|
||||
RebootMsg *uint16
|
||||
Command *uint16
|
||||
ActionsCount uint32
|
||||
Actions uintptr
|
||||
}
|
||||
|
||||
type scAction struct {
|
||||
Type uint32
|
||||
Delay uint32
|
||||
}
|
||||
t := []scAction{
|
||||
{Type: scActionRestart, Delay: uint32(60 * time.Second / time.Millisecond)},
|
||||
{Type: scActionRestart, Delay: uint32(60 * time.Second / time.Millisecond)},
|
||||
{Type: scActionNone},
|
||||
}
|
||||
lpInfo := serviceFailureActions{ResetPeriod: uint32(24 * time.Hour / time.Second), ActionsCount: uint32(3), Actions: uintptr(unsafe.Pointer(&t[0]))}
|
||||
err = windows.ChangeServiceConfig2(s.Handle, serviceConfigFailureActions, (*byte)(unsafe.Pointer(&lpInfo)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return eventlog.Install(serviceName, execPath, false, eventlog.Info|eventlog.Warning|eventlog.Error)
|
||||
}
|
||||
|
||||
//UnRegisterService unres
|
||||
func UnRegisterService(serviceName string) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(serviceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
eventlog.Remove(serviceName)
|
||||
err = s.Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//StartService start a windows service
|
||||
func StartService(serviceName string) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(serviceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open service: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
err = s.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start service: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//StopService stop a windows service
|
||||
func StopService(serviceName string) error {
|
||||
return controlService(serviceName, svc.Stop, svc.Stopped)
|
||||
}
|
||||
|
||||
//RestartService restart a windows service
|
||||
func RestartService(serviceName string) error {
|
||||
if err := controlService(serviceName, svc.Stop, svc.Stopped); err != nil {
|
||||
return err
|
||||
}
|
||||
return StartService(serviceName)
|
||||
}
|
||||
|
||||
func controlService(name string, c svc.Cmd, to svc.State) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not access service: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
status, err := s.Control(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not send control=%d: %v", c, err)
|
||||
}
|
||||
timeout := time.Now().Add(10 * time.Second)
|
||||
for status.State != to {
|
||||
if timeout.Before(time.Now()) {
|
||||
return fmt.Errorf("timeout waiting for service to go to state=%d", to)
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
status, err = s.Query()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not retrieve service status: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user