Enhance up command (#133)

* move setup-key to root command

* up will check login and start service

* update tests to reflect new UP capabilities

* display client IP

* removed unused argument

* install service if not installed

* update post-install and add pre remove script

* improve log messages

* handle service status failures and install service when needed

* removing unused files

* update documentation and description

* add version command

* update service lib version

* using lib constant for not installed services

* match version from goreleaser

* fix: graceful shutdown

* stop only if service is running

* add logs initialization to service controller commands

Co-authored-by: braginini <bangvalo@gmail.com>
This commit is contained in:
Maycon Santos
2021-10-17 21:34:07 +02:00
committed by GitHub
parent 96799a25b5
commit fcea3c99d4
16 changed files with 225 additions and 225 deletions

View File

@@ -152,6 +152,5 @@ func promptPeerSetupKey() (string, error) {
return "", s.Err()
}
func init() {
loginCmd.PersistentFlags().StringVar(&setupKey, "setup-key", "", "Setup key obtained from the Management Service Dashboard (used to register peer)")
}
//func init() {
//}

View File

@@ -2,11 +2,13 @@ package cmd
import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/wiretrustee/wiretrustee/client/internal"
"os"
"os/signal"
"runtime"
"syscall"
)
const (
@@ -22,8 +24,7 @@ var (
defaultLogFile string
logFile string
managementURL string
rootCmd = &cobra.Command{
rootCmd = &cobra.Command{
Use: "wiretrustee",
Short: "",
Long: "",
@@ -52,9 +53,11 @@ func init() {
rootCmd.PersistentFlags().StringVar(&configPath, "config", defaultConfigPath, "Wiretrustee config file location")
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "sets Wiretrustee log level")
rootCmd.PersistentFlags().StringVar(&logFile, "log-file", defaultLogFile, "sets Wiretrustee log path. If console is specified the the log will be output to stdout")
rootCmd.PersistentFlags().StringVar(&setupKey, "setup-key", "", "Setup key obtained from the Management Service Dashboard (used to register peer)")
rootCmd.AddCommand(serviceCmd)
rootCmd.AddCommand(upCmd)
rootCmd.AddCommand(loginCmd)
rootCmd.AddCommand(versionCmd)
serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service
serviceCmd.AddCommand(installCmd, uninstallCmd) // service installer commands are subcommands of service
}
@@ -62,10 +65,10 @@ func init() {
// SetupCloseHandler handles SIGTERM signal and exits with success
func SetupCloseHandler() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
go func() {
for range c {
fmt.Println("\r- Ctrl+C pressed in Terminal")
log.Info("shutdown signal received")
stopCh <- 0
}
}()

View File

@@ -4,13 +4,14 @@ import (
"github.com/kardianos/service"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/wiretrustee/wiretrustee/util"
)
func (p *program) Start(s service.Service) error {
// Start should not block. Do the actual work async.
log.Info("starting service") //nolint
go func() {
err := upCmd.RunE(p.cmd, p.args)
err := runClient()
if err != nil {
return
}
@@ -20,7 +21,6 @@ func (p *program) Start(s service.Service) error {
}
func (p *program) Stop(s service.Service) error {
stopCh <- 1
return nil
}
@@ -29,7 +29,11 @@ var (
Use: "run",
Short: "runs wiretrustee as service",
Run: func(cmd *cobra.Command, args []string) {
err := util.InitLog(logLevel, logFile)
if err != nil {
log.Errorf("failed initializing log %v", err)
return
}
prg := &program{
cmd: cmd,
args: args,
@@ -54,19 +58,24 @@ var (
startCmd = &cobra.Command{
Use: "start",
Short: "starts wiretrustee service",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
err := util.InitLog(logLevel, logFile)
if err != nil {
log.Errorf("failed initializing log %v", err)
return err
}
s, err := newSVC(&program{}, newSVCConfig())
if err != nil {
cmd.PrintErrln(err)
return
return err
}
err = s.Start()
if err != nil {
cmd.PrintErrln(err)
return
return err
}
cmd.Printf("Wiretrustee service has been started")
cmd.Println("Wiretrustee service has been started")
return nil
},
}
)
@@ -76,7 +85,10 @@ var (
Use: "stop",
Short: "stops wiretrustee service",
Run: func(cmd *cobra.Command, args []string) {
err := util.InitLog(logLevel, logFile)
if err != nil {
log.Errorf("failed initializing log %v", err)
}
s, err := newSVC(&program{}, newSVCConfig())
if err != nil {
cmd.PrintErrln(err)
@@ -87,7 +99,7 @@ var (
cmd.PrintErrln(err)
return
}
cmd.Printf("Wiretrustee service has been stopped")
cmd.Println("Wiretrustee service has been stopped")
},
}
)
@@ -97,7 +109,10 @@ var (
Use: "restart",
Short: "restarts wiretrustee service",
Run: func(cmd *cobra.Command, args []string) {
err := util.InitLog(logLevel, logFile)
if err != nil {
log.Errorf("failed initializing log %v", err)
}
s, err := newSVC(&program{}, newSVCConfig())
if err != nil {
cmd.PrintErrln(err)
@@ -108,7 +123,7 @@ var (
cmd.PrintErrln(err)
return
}
cmd.Printf("Wiretrustee service has been restarted")
cmd.Println("Wiretrustee service has been restarted")
},
}
)

View File

@@ -9,7 +9,7 @@ var (
installCmd = &cobra.Command{
Use: "install",
Short: "installs wiretrustee service",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
svcConfig := newSVCConfig()
@@ -30,15 +30,16 @@ var (
s, err := newSVC(&program{}, svcConfig)
if err != nil {
cmd.PrintErrln(err)
return
return err
}
err = s.Install()
if err != nil {
cmd.PrintErrln(err)
return
return err
}
cmd.Printf("Wiretrustee service has been installed")
cmd.Println("Wiretrustee service has been installed")
return nil
},
}
)
@@ -60,7 +61,7 @@ var (
cmd.PrintErrln(err)
return
}
cmd.Printf("Wiretrustee has been uninstalled")
cmd.Println("Wiretrustee has been uninstalled")
},
}
)

View File

@@ -2,6 +2,7 @@ package cmd
import (
"context"
"github.com/kardianos/service"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/wiretrustee/wiretrustee/client/internal"
@@ -17,7 +18,7 @@ import (
var (
upCmd = &cobra.Command{
Use: "up",
Short: "start wiretrustee",
Short: "install, login and start wiretrustee client",
RunE: func(cmd *cobra.Command, args []string) error {
err := util.InitLog(logLevel, logFile)
if err != nil {
@@ -25,89 +26,42 @@ var (
return err
}
config, err := internal.ReadConfig(managementURL, configPath)
err = loginCmd.RunE(cmd, args)
if err != nil {
log.Errorf("failed reading config %s %v", configPath, err)
return err
}
if logFile == "console" {
return runClient()
}
s, err := newSVC(&program{}, newSVCConfig())
if err != nil {
cmd.PrintErrln(err)
return err
}
//validate our peer's Wireguard PRIVATE key
myPrivateKey, err := wgtypes.ParseKey(config.PrivateKey)
srvStatus, err := s.Status()
if err != nil {
log.Errorf("failed parsing Wireguard key %s: [%s]", config.PrivateKey, err.Error())
return err
if err == service.ErrNotInstalled {
log.Infof("%s. Installing it now", err.Error())
e := installCmd.RunE(cmd, args)
if e != nil {
return e
}
} else {
log.Warnf("failed retrieving service status: %v", err)
}
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
mgmTlsEnabled := false
if config.ManagementURL.Scheme == "https" {
mgmTlsEnabled = true
if srvStatus == service.StatusRunning {
stopCmd.Run(cmd, args)
}
// connect (just a connection, no stream yet) and login to Management Service to get an initial global Wiretrustee config
mgmClient, loginResp, err := connectToManagement(ctx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
if err != nil {
log.Warn(err)
return err
}
// with the global Wiretrustee config in hand connect (just a connection, no stream yet) Signal
signalClient, err := connectToSignal(ctx, loginResp.GetWiretrusteeConfig(), myPrivateKey)
if err != nil {
log.Error(err)
return err
}
engineConfig, err := createEngineConfig(myPrivateKey, config, loginResp.GetWiretrusteeConfig(), loginResp.GetPeerConfig())
if err != nil {
log.Error(err)
return err
}
// create start the Wiretrustee Engine that will connect to the Signal and Management streams and manage connections to remote peers.
engine := internal.NewEngine(signalClient, mgmClient, engineConfig, cancel)
err = engine.Start()
if err != nil {
log.Errorf("error while starting Wiretrustee Connection Engine: %s", err)
return err
}
SetupCloseHandler()
select {
case <-stopCh:
case <-ctx.Done():
}
log.Infof("receive signal to stop running")
err = mgmClient.Close()
if err != nil {
log.Errorf("failed closing Management Service client %v", err)
return err
}
err = signalClient.Close()
if err != nil {
log.Errorf("failed closing Signal Service client %v", err)
return err
}
err = engine.Stop()
if err != nil {
log.Errorf("failed stopping engine %v", err)
return err
}
return nil
return startCmd.RunE(cmd, args)
},
}
)
func init() {
}
// createEngineConfig converts configuration received from Management Service to EngineConfig
func createEngineConfig(key wgtypes.Key, config *internal.Config, wtConfig *mgmProto.WiretrusteeConfig, peerConfig *mgmProto.PeerConfig) (*internal.EngineConfig, error) {
func createEngineConfig(key wgtypes.Key, config *internal.Config, peerConfig *mgmProto.PeerConfig) (*internal.EngineConfig, error) {
iFaceBlackList := make(map[string]struct{})
for i := 0; i < len(config.IFaceBlackList); i += 2 {
iFaceBlackList[config.IFaceBlackList[i]] = struct{}{}
@@ -167,3 +121,84 @@ func connectToManagement(ctx context.Context, managementAddr string, ourPrivateK
return client, loginResp, nil
}
func runClient() error {
config, err := internal.ReadConfig(managementURL, configPath)
if err != nil {
log.Errorf("failed reading config %s %v", configPath, err)
return err
}
//validate our peer's Wireguard PRIVATE key
myPrivateKey, err := wgtypes.ParseKey(config.PrivateKey)
if err != nil {
log.Errorf("failed parsing Wireguard key %s: [%s]", config.PrivateKey, err.Error())
return err
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
mgmTlsEnabled := false
if config.ManagementURL.Scheme == "https" {
mgmTlsEnabled = true
}
// connect (just a connection, no stream yet) and login to Management Service to get an initial global Wiretrustee config
mgmClient, loginResp, err := connectToManagement(ctx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
if err != nil {
log.Warn(err)
return err
}
// with the global Wiretrustee config in hand connect (just a connection, no stream yet) Signal
signalClient, err := connectToSignal(ctx, loginResp.GetWiretrusteeConfig(), myPrivateKey)
if err != nil {
log.Error(err)
return err
}
peerConfig := loginResp.GetPeerConfig()
engineConfig, err := createEngineConfig(myPrivateKey, config, peerConfig)
if err != nil {
log.Error(err)
return err
}
// create start the Wiretrustee Engine that will connect to the Signal and Management streams and manage connections to remote peers.
engine := internal.NewEngine(signalClient, mgmClient, engineConfig, cancel)
err = engine.Start()
if err != nil {
log.Errorf("error while starting Wiretrustee Connection Engine: %s", err)
return err
}
log.Print("Wiretrustee engine started, my IP is: ", peerConfig.Address)
SetupCloseHandler()
select {
case <-stopCh:
case <-ctx.Done():
}
log.Info("shutting down Wiretrustee client")
err = mgmClient.Close()
if err != nil {
log.Errorf("failed closing Management Service client %v", err)
return err
}
err = signalClient.Close()
if err != nil {
log.Errorf("failed closing Signal Service client %v", err)
return err
}
err = engine.Stop()
if err != nil {
log.Errorf("failed stopping engine %v", err)
return err
}
return nil
}

View File

@@ -1,13 +1,10 @@
package cmd
import (
"errors"
"fmt"
"github.com/wiretrustee/wiretrustee/iface"
mgmt "github.com/wiretrustee/wiretrustee/management/server"
"github.com/wiretrustee/wiretrustee/util"
"net/url"
"os"
"path/filepath"
"testing"
"time"
@@ -37,24 +34,6 @@ func TestUp_Start(t *testing.T) {
}
func TestUp_ShouldFail_On_NoConfig(t *testing.T) {
tempDir := t.TempDir()
confPath := tempDir + "/config.json"
mgmtURL := fmt.Sprintf("http://%s", mgmAddr)
rootCmd.SetArgs([]string{
"up",
"--config",
confPath,
"--management-url",
mgmtURL,
})
err := rootCmd.Execute()
if err == nil || !errors.Is(err, os.ErrNotExist) {
t.Errorf("expecting login command to fail on absence of config")
}
}
func TestUp(t *testing.T) {
defer iface.Close()
@@ -65,24 +44,17 @@ func TestUp(t *testing.T) {
if err != nil {
t.Fatal(err)
}
rootCmd.SetArgs([]string{
"login",
"up",
"--config",
confPath,
"--setup-key",
"A2C8E62B-38F5-4553-B31E-DD66C696CEBB",
"--management-url",
mgmtURL.String(),
})
err = rootCmd.Execute()
if err != nil {
t.Fatal(err)
}
rootCmd.SetArgs([]string{
"up",
"--config",
confPath,
"--log-file",
"console",
})
go func() {
err = rootCmd.Execute()

14
client/cmd/version.go Normal file
View File

@@ -0,0 +1,14 @@
package cmd
import "github.com/spf13/cobra"
var (
Version string
versionCmd = &cobra.Command{
Use: "version",
Short: "prints wiretrustee version",
Run: func(cmd *cobra.Command, args []string) {
cmd.Println(Version)
},
}
)