// RunPort is the handler for 'scw port' func RunPort(ctx CommandContext, args PortArgs) error { serverID := ctx.API.GetServerID(args.Server) server, err := ctx.API.GetServer(serverID) if err != nil { return fmt.Errorf("failed to get server information for %s: %v", serverID, err) } // Resolve gateway if args.Gateway == "" { args.Gateway = ctx.Getenv("SCW_GATEWAY") } var gateway string if args.Gateway == serverID || args.Gateway == args.Server { gateway = "" } else { gateway, err = api.ResolveGateway(ctx.API, args.Gateway) if err != nil { return fmt.Errorf("cannot resolve Gateway '%s': %v", args.Gateway, err) } } command := []string{"netstat -lutn 2>/dev/null | grep LISTEN"} err = utils.SSHExec(server.PublicAddress.IP, server.PrivateIP, command, true, gateway) if err != nil { return fmt.Errorf("command execution failed: %v", err) } return nil }
// RunLogs is the handler for 'scw logs' func RunLogs(ctx CommandContext, args LogsArgs) error { serverID, err := ctx.API.GetServerID(args.Server) if err != nil { return err } server, err := ctx.API.GetServer(serverID) if err != nil { return fmt.Errorf("failed to get server information for %s: %v", serverID, err) } // FIXME: switch to serial history when API is ready // Resolve gateway if args.Gateway == "" { args.Gateway = ctx.Getenv("SCW_GATEWAY") } var gateway string if args.Gateway == serverID || args.Gateway == args.Server { gateway = "" } else { gateway, err = api.ResolveGateway(ctx.API, args.Gateway) if err != nil { return fmt.Errorf("cannot resolve Gateway '%s': %v", args.Gateway, err) } } command := []string{"dmesg"} err = utils.SSHExec(server.PublicAddress.IP, server.PrivateIP, args.SSHUser, args.SSHPort, command, true, gateway) if err != nil { return fmt.Errorf("command execution failed: %v", err) } return nil }
func runShowBoot(ctx CommandContext, args RunArgs, serverID string, closeTimeout chan struct{}, timeoutExit chan struct{}) error { // Attach to server serial logrus.Info("Attaching to server console ...") gottycli, done, err := utils.AttachToSerial(serverID, ctx.API.Token) if err != nil { close(closeTimeout) return fmt.Errorf("cannot attach to server serial: %v", err) } utils.Quiet(true) notif, gateway, err := waitSSHConnection(ctx, args, serverID) if err != nil { close(closeTimeout) gottycli.ExitLoop() <-done return err } select { case <-timeoutExit: gottycli.ExitLoop() <-done utils.Quiet(false) return fmt.Errorf("Operation timed out") case sshConnection := <-notif: close(closeTimeout) gottycli.ExitLoop() <-done utils.Quiet(false) if sshConnection.err != nil { return sshConnection.err } if fingerprints := ctx.API.GetSSHFingerprintFromServer(serverID); len(fingerprints) > 0 { for i := range fingerprints { fmt.Fprintf(ctx.Stdout, "%s\n", fingerprints[i]) } } server := sshConnection.server logrus.Info("Connecting to server ...") if err = utils.SSHExec(server.PublicAddress.IP, server.PrivateIP, []string{}, false, gateway); err != nil { return fmt.Errorf("Connection to server failed: %v", err) } } return nil }
// Run is the handler for 'scw run' func Run(ctx CommandContext, args RunArgs) error { if args.Gateway == "" { args.Gateway = ctx.Getenv("SCW_GATEWAY") } if args.TmpSSHKey { err := AddSSHKeyToTags(ctx, &args.Tags, args.Image) if err != nil { return err } } env := strings.Join(args.Tags, " ") volume := strings.Join(args.Volumes, " ") // create IMAGE logrus.Info("Server creation ...") serverID, err := api.CreateServer(ctx.API, &api.ConfigCreateServer{ ImageName: args.Image, Name: args.Name, Bootscript: args.Bootscript, Env: env, AdditionalVolumes: volume, DynamicIPRequired: args.Gateway == "", IP: args.IP, }) if err != nil { return fmt.Errorf("failed to create server: %v", err) } logrus.Infof("Server created: %s", serverID) if args.AutoRemove { defer ctx.API.DeleteServerSafe(serverID) } // start SERVER logrus.Info("Server start requested ...") if err = api.StartServer(ctx.API, serverID, false); err != nil { return fmt.Errorf("failed to start server %s: %v", serverID, err) } logrus.Info("Server is starting, this may take up to a minute ...") if args.Userdata != "" { addUserData(ctx, strings.Split(args.Userdata, " "), serverID) } // Sync cache on disk ctx.API.Sync() if args.Detach { fmt.Fprintln(ctx.Stdout, serverID) return nil } closeTimeout := make(chan struct{}) timeoutExit := make(chan struct{}) if args.Timeout > 0 { go func() { select { case <-time.After(time.Duration(args.Timeout) * time.Second): close(timeoutExit) case <-closeTimeout: break } }() } if args.ShowBoot { return runShowBoot(ctx, args, serverID, closeTimeout, timeoutExit) } else if args.Attach { // Attach to server serial logrus.Info("Attaching to server console ...") gottycli, done, err := utils.AttachToSerial(serverID, ctx.API.Token) close(closeTimeout) if err != nil { return fmt.Errorf("cannot attach to server serial: %v", err) } <-done gottycli.Close() } else { notif, gateway, err := waitSSHConnection(ctx, args, serverID) if err != nil { close(closeTimeout) return err } select { case <-timeoutExit: return fmt.Errorf("Operation timed out") case sshConnection := <-notif: close(closeTimeout) if sshConnection.err != nil { return sshConnection.err } if fingerprints := ctx.API.GetSSHFingerprintFromServer(serverID); len(fingerprints) > 0 { for i := range fingerprints { fmt.Fprintf(ctx.Stdout, "%s\n", fingerprints[i]) } } server := sshConnection.server // exec -w SERVER COMMAND ARGS... if len(args.Command) < 1 { logrus.Info("Connecting to server ...") if err = utils.SSHExec(server.PublicAddress.IP, server.PrivateIP, []string{}, false, gateway); err != nil { return fmt.Errorf("Connection to server failed: %v", err) } } else { logrus.Infof("Executing command: %s ...", args.Command) if err = utils.SSHExec(server.PublicAddress.IP, server.PrivateIP, args.Command, false, gateway); err != nil { return fmt.Errorf("command execution failed: %v", err) } logrus.Info("Command successfuly executed") } } } return nil }
// WaitForServerReady wait for a server state to be running, then wait for the SSH port to be available func WaitForServerReady(api *ScalewayAPI, serverID, gateway string) (*ScalewayServer, error) { promise := make(chan bool) var server *ScalewayServer var err error var currentState string go func() { defer close(promise) for { server, err = api.GetServer(serverID) if err != nil { promise <- false return } if currentState != server.State { log.Infof("Server changed state to '%s'", server.State) currentState = server.State } if server.State == "running" { break } if server.State == "stopped" { err = fmt.Errorf("The server has been stopped") promise <- false return } time.Sleep(1 * time.Second) } if gateway == "" { dest := fmt.Sprintf("%s:22", server.PublicAddress.IP) log.Debugf("Waiting for server SSH port %s", dest) err = utils.WaitForTCPPortOpen(dest) if err != nil { promise <- false return } } else { dest := fmt.Sprintf("%s:22", gateway) log.Debugf("Waiting for server SSH port %s", dest) err = utils.WaitForTCPPortOpen(dest) if err != nil { promise <- false return } timeout := time.Tick(120 * time.Second) for { select { case <-timeout: err = fmt.Errorf("Timeout: unable to ping %s", server.PrivateIP) fmt.Println("timeout") goto OUT default: if utils.SSHExec("", server.PrivateIP, "root", 22, []string{ "nc", "-z", "-w", "1", server.PrivateIP, "22", }, false, gateway) == nil { goto OUT } } } OUT: if err != nil { promise <- false return } log.Debugf("Check for SSH port through the gateway: %s", server.PrivateIP) } promise <- true }() loop := 0 for { select { case done := <-promise: utils.LogQuiet("\r \r") if !done { return nil, err } return server, nil case <-time.After(time.Millisecond * 100): utils.LogQuiet(fmt.Sprintf("\r%c\r", "-\\|/"[loop%4])) loop = loop + 1 if loop == 5 { loop = 0 } } } }
// RunExec is the handler for 'scw exec' func RunExec(ctx CommandContext, args ExecArgs) error { var fingerprints []string done := make(chan struct{}) serverID, err := ctx.API.GetServerID(args.Server) if err != nil { return err } go func() { fingerprints = ctx.API.GetSSHFingerprintFromServer(serverID) close(done) }() // Resolve gateway if args.Gateway == "" { args.Gateway = ctx.Getenv("SCW_GATEWAY") } var gateway string if args.Gateway == serverID || args.Gateway == args.Server { logrus.Debugf("The server and the gateway are the same host, using direct access to the server") gateway = "" } else { gateway, err = api.ResolveGateway(ctx.API, args.Gateway) if err != nil { return fmt.Errorf("Cannot resolve Gateway '%s': %v", args.Gateway, err) } if gateway != "" { logrus.Debugf("The server will be accessed using the gateway '%s' as a SSH relay", gateway) } } var server *api.ScalewayServer if args.Wait { // --wait logrus.Debugf("Waiting for server to be ready") server, err = api.WaitForServerReady(ctx.API, serverID, gateway) if err != nil { return fmt.Errorf("Failed to wait for server to be ready, %v", err) } } else { // no --wait logrus.Debugf("scw won't wait for the server to be ready, if it is not, the command will fail") server, err = ctx.API.GetServer(serverID) if err != nil { rerr := fmt.Errorf("Failed to get server information for %s: %v", serverID, err) if err.Error() == `"`+serverID+`" not found` { return fmt.Errorf("%v\nmaybe try to flush the cache with : scw _flush-cache", rerr) } return rerr } } if server.PublicAddress.IP == "" && gateway == "" { logrus.Warn(`Your host has no public IP address, you should use '--gateway', see 'scw help exec'`) } // --timeout if args.Timeout > 0 { logrus.Debugf("Setting up a global timeout of %d seconds", args.Timeout) // FIXME: avoid use of log.Fatalf here go func() { time.Sleep(time.Duration(args.Timeout*1000) * time.Millisecond) logrus.Fatalf("Operation timed out") }() } <-done if len(fingerprints) > 0 { for i := range fingerprints { fmt.Fprintf(ctx.Stdout, "%s\n", fingerprints[i]) } } logrus.Debugf("PublicDNS %s", serverID+api.URLPublicDNS) logrus.Debugf("PrivateDNS %s", serverID+api.URLPrivateDNS) if err = utils.SSHExec(server.PublicAddress.IP, server.PrivateIP, args.SSHUser, args.SSHPort, args.Command, !args.Wait, gateway); err != nil { return fmt.Errorf("Failed to run the command: %v", err) } logrus.Debugf("Command successfully executed") return nil }
// Run is the handler for 'scw run' func Run(ctx CommandContext, args RunArgs) error { if args.Gateway == "" { args.Gateway = ctx.Getenv("SCW_GATEWAY") } if args.TmpSSHKey { err := AddSSHKeyToTags(ctx, &args.Tags, args.Image) if err != nil { return err } } env := strings.Join(args.Tags, " ") volume := strings.Join(args.Volumes, " ") // create IMAGE logrus.Info("Server creation ...") dynamicIPRequired := args.Gateway == "" serverID, err := api.CreateServer(ctx.API, args.Image, args.Name, args.Bootscript, env, volume, dynamicIPRequired) if err != nil { return fmt.Errorf("failed to create server: %v", err) } logrus.Infof("Server created: %s", serverID) if args.AutoRemove { defer ctx.API.DeleteServerSafe(serverID) } // start SERVER logrus.Info("Server start requested ...") err = api.StartServer(ctx.API, serverID, false) if err != nil { return fmt.Errorf("failed to start server %s: %v", serverID, err) } logrus.Info("Server is starting, this may take up to a minute ...") if args.Detach { fmt.Fprintln(ctx.Stdout, serverID) return nil } else { // Sync cache on disk ctx.API.Sync() } if args.Attach { // Attach to server serial logrus.Info("Attaching to server console ...") err = utils.AttachToSerial(serverID, ctx.API.Token, true) if err != nil { return fmt.Errorf("cannot attach to server serial: %v", err) } } else { // Resolve gateway gateway, err := api.ResolveGateway(ctx.API, args.Gateway) if err != nil { return fmt.Errorf("cannot resolve Gateway '%s': %v", args.Gateway, err) } // waiting for server to be ready logrus.Debug("Waiting for server to be ready") // We wait for 30 seconds, which is the minimal amount of time needed by a server to boot server, err := api.WaitForServerReady(ctx.API, serverID, gateway) if err != nil { return fmt.Errorf("cannot get access to server %s: %v", serverID, err) } logrus.Debugf("SSH server is available: %s:22", server.PublicAddress.IP) logrus.Info("Server is ready !") // exec -w SERVER COMMAND ARGS... if len(args.Command) < 1 { logrus.Info("Connecting to server ...") err = utils.SSHExec(server.PublicAddress.IP, server.PrivateIP, []string{}, false, gateway) } else { logrus.Infof("Executing command: %s ...", args.Command) err = utils.SSHExec(server.PublicAddress.IP, server.PrivateIP, args.Command, false, gateway) } if err != nil { return fmt.Errorf("command execution failed: %v", err) } logrus.Info("Command successfuly executed") } return nil }