// 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 }
func waitSSHConnection(ctx CommandContext, args RunArgs, serverID string) (chan notifSSHConnection, string, error) { notif := make(chan notifSSHConnection) // Resolve gateway gateway, err := api.ResolveGateway(ctx.API, args.Gateway) if err != nil { return nil, "", 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 go func() { server, err := api.WaitForServerReady(ctx.API, serverID, gateway) if err != nil { notif <- notifSSHConnection{ err: fmt.Errorf("cannot get access to server %s: %v", serverID, err), } return } logrus.Debugf("SSH server is available: %s:22", server.PublicAddress.IP) logrus.Info("Server is ready !") notif <- notifSSHConnection{ server: server, } }() return notif, gateway, nil }
// RunTop is the handler for 'scw top' func RunTop(ctx CommandContext, args TopArgs) error { serverID := ctx.API.GetServerID(args.Server) command := "ps" 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) } } sshCommand := utils.NewSSHExecCmd(server.PublicAddress.IP, server.PrivateIP, true, []string{command}, gateway) logrus.Debugf("Executing: %s", sshCommand) out, err := exec.Command("ssh", sshCommand.Slice()[1:]...).CombinedOutput() fmt.Printf("%s", out) return err }
// 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 }
// RunKill is the handler for 'scw kill' func RunKill(ctx CommandContext, args KillArgs) error { serverID := ctx.API.GetServerID(args.Server) command := "halt" 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) } } sshCommand := utils.NewSSHExecCmd(server.PublicAddress.IP, server.PrivateIP, true, []string{command}, gateway) logrus.Debugf("Executing: %s", sshCommand) spawn := exec.Command("ssh", sshCommand.Slice()[1:]...) spawn.Stdout = ctx.Stdout spawn.Stdin = ctx.Stdin spawn.Stderr = ctx.Stderr return spawn.Run() }
// TarFromSource creates a stream buffer with the tarballed content of the user source func TarFromSource(ctx CommandContext, source string, gateway string) (*io.ReadCloser, error) { var tarOutputStream io.ReadCloser // source is a server address + path (scp-like uri) if strings.Contains(source, ":") { logrus.Debugf("Creating a tarball remotely and streaming it using SSH") serverParts := strings.Split(source, ":") if len(serverParts) != 2 { return nil, fmt.Errorf("invalid source uri, see 'scw cp -h' for usage") } serverID, err := ctx.API.GetServerID(serverParts[0]) if err != nil { return nil, err } server, err := ctx.API.GetServer(serverID) if err != nil { return nil, err } dir, base := utils.PathToTARPathparts(serverParts[1]) logrus.Debugf("Equivalent to 'scp root@%s:%s/%s ...'", server.PublicAddress.IP, dir, base) // remoteCommand is executed on the remote server // it streams a tarball raw content remoteCommand := []string{"tar"} remoteCommand = append(remoteCommand, "-C", dir) if ctx.Getenv("DEBUG") == "1" { remoteCommand = append(remoteCommand, "-v") } remoteCommand = append(remoteCommand, "-cf", "-") remoteCommand = append(remoteCommand, base) // Resolve gateway if gateway == "" { gateway = ctx.Getenv("SCW_GATEWAY") } if gateway == serverID || gateway == serverParts[0] { gateway = "" } else { gateway, err = api.ResolveGateway(ctx.API, gateway) if err != nil { return nil, fmt.Errorf("cannot resolve Gateway '%s': %v", gateway, err) } } // execCmd contains the ssh connection + the remoteCommand sshCommand := utils.NewSSHExecCmd(server.PublicAddress.IP, server.PrivateIP, false, remoteCommand, gateway) logrus.Debugf("Executing: %s", sshCommand) spawnSrc := exec.Command("ssh", sshCommand.Slice()[1:]...) tarOutputStream, err = spawnSrc.StdoutPipe() if err != nil { return nil, err } tarErrorStream, err := spawnSrc.StderrPipe() if err != nil { return nil, err } defer tarErrorStream.Close() io.Copy(ctx.Stderr, tarErrorStream) err = spawnSrc.Start() if err != nil { return nil, err } defer spawnSrc.Wait() return &tarOutputStream, nil } // source is stdin if source == "-" { logrus.Debugf("Streaming tarball from stdin") // FIXME: should be ctx.Stdin tarOutputStream = os.Stdin return &tarOutputStream, nil } // source is a path on localhost logrus.Debugf("Taring local path %s", source) path, err := filepath.Abs(source) if err != nil { return nil, err } path, err = filepath.EvalSymlinks(path) if err != nil { return nil, err } logrus.Debugf("Real local path is %s", path) dir, base := utils.PathToTARPathparts(path) tarOutputStream, err = archive.TarWithOptions(dir, &archive.TarOptions{ Compression: archive.Uncompressed, IncludeFiles: []string{base}, }) if err != nil { return nil, err } return &tarOutputStream, nil }
// UntarToDest writes to user destination the streamed tarball in input func UntarToDest(ctx CommandContext, sourceStream *io.ReadCloser, destination string, gateway string) error { // destination is a server address + path (scp-like uri) if strings.Contains(destination, ":") { logrus.Debugf("Streaming using ssh and untaring remotely") serverParts := strings.Split(destination, ":") if len(serverParts) != 2 { return fmt.Errorf("invalid destination uri, see 'scw cp -h' for usage") } serverID, err := ctx.API.GetServerID(serverParts[0]) if err != nil { return err } server, err := ctx.API.GetServer(serverID) if err != nil { return err } // remoteCommand is executed on the remote server // it streams a tarball raw content remoteCommand := []string{"tar"} remoteCommand = append(remoteCommand, "-C", serverParts[1]) if ctx.Getenv("DEBUG") == "1" { remoteCommand = append(remoteCommand, "-v") } remoteCommand = append(remoteCommand, "-xf", "-") // Resolve gateway if gateway == "" { gateway = ctx.Getenv("SCW_GATEWAY") } if gateway == serverID || gateway == serverParts[0] { gateway = "" } else { gateway, err = api.ResolveGateway(ctx.API, gateway) if err != nil { return fmt.Errorf("cannot resolve Gateway '%s': %v", gateway, err) } } // execCmd contains the ssh connection + the remoteCommand sshCommand := utils.NewSSHExecCmd(server.PublicAddress.IP, server.PrivateIP, false, remoteCommand, gateway) logrus.Debugf("Executing: %s", sshCommand) spawnDst := exec.Command("ssh", sshCommand.Slice()[1:]...) untarInputStream, err := spawnDst.StdinPipe() if err != nil { return err } defer untarInputStream.Close() // spawnDst.Stderr = ctx.Stderr // spawnDst.Stdout = ctx.Stdout err = spawnDst.Start() if err != nil { return err } _, err = io.Copy(untarInputStream, *sourceStream) return err } // destination is stdout if destination == "-" { // stdout logrus.Debugf("Writing sourceStream(%v) to ctx.Stdout(%v)", sourceStream, ctx.Stdout) _, err := io.Copy(ctx.Stdout, *sourceStream) return err } // destination is a path on localhost logrus.Debugf("Untaring to local path: %s", destination) err := archive.Untar(*sourceStream, destination, &archive.TarOptions{NoLchown: true}) return err }
// 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 }