Skip to content

Commit 95d3a2e

Browse files
committed
Merge pull request #3785 from Joshua-Anderson/deisctl-ssh
feat(deisctl): Add deisctl ssh
2 parents c6c65d1 + 128b65b commit 95d3a2e

8 files changed

Lines changed: 103 additions & 22 deletions

File tree

deisctl/backend/backend.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type Backend interface {
99
Start([]string, *sync.WaitGroup, chan string, chan error)
1010
Stop([]string, *sync.WaitGroup, chan string, chan error)
1111
Scale(string, int, *sync.WaitGroup, chan string, chan error)
12+
SSH(string) error
1213
ListUnits() error
1314
ListUnitFiles() error
1415
Status(string) error

deisctl/backend/fleet/journal.go

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package fleet
22

33
import (
44
"fmt"
5-
"os"
65
)
76

87
// Journal prints the systemd journal of target unit(s)
@@ -19,24 +18,12 @@ func (c *FleetClient) Journal(target string) (err error) {
1918

2019
// runJournal tails the systemd journal for a given unit
2120
func runJournal(name string) (exit int) {
21+
machineID, err := findUnit(name)
2222

23-
u, err := cAPI.Unit(name)
2423
if err != nil {
25-
fmt.Fprintf(os.Stderr, "Error retrieving Unit %s: %v", name, err)
26-
return 1
27-
}
28-
if suToGlobal(*u) {
29-
fmt.Fprintf(os.Stderr, "Unable to get journal for global unit %s. Check the logs on the host using journalctl.\n", name)
30-
return 1
31-
}
32-
if u == nil {
33-
fmt.Fprintf(os.Stderr, "Unit %s does not exist.\n", name)
34-
return 1
35-
} else if u.CurrentState == "" {
36-
fmt.Fprintf(os.Stderr, "Unit %s does not appear to be running.\n", name)
3724
return 1
3825
}
3926

4027
command := fmt.Sprintf("journalctl --unit %s --no-pager -n 40 -f", name)
41-
return runCommand(command, u.MachineID)
28+
return runCommand(command, machineID)
4229
}

deisctl/backend/fleet/ssh.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,54 @@ import (
1212
"github.com/coreos/fleet/ssh"
1313
)
1414

15+
// SSH opens an interactive shell to a machine in the cluster
16+
func (c *FleetClient) SSH(name string) (err error) {
17+
var sshClient *ssh.SSHForwardingClient
18+
19+
timeout := time.Duration(Flags.SSHTimeout*1000) * time.Millisecond
20+
21+
ms, err := machineState(name)
22+
if err != nil {
23+
return err
24+
}
25+
26+
// If name isn't a machine ID, try it as a unit instead
27+
if ms == nil {
28+
units, err := c.Units(name)
29+
30+
if err != nil {
31+
return err
32+
}
33+
34+
machID, err := findUnit(units[0])
35+
36+
if err != nil {
37+
return err
38+
}
39+
40+
ms, err = machineState(machID)
41+
42+
if err != nil || ms == nil {
43+
return err
44+
}
45+
}
46+
47+
addr := ms.PublicIP
48+
49+
if tun := getTunnelFlag(); tun != "" {
50+
sshClient, err = ssh.NewTunnelledSSHClient("core", tun, addr, getChecker(), false, timeout)
51+
} else {
52+
sshClient, err = ssh.NewSSHClient("core", addr, getChecker(), false, timeout)
53+
}
54+
if err != nil {
55+
return err
56+
}
57+
58+
defer sshClient.Close()
59+
err = ssh.Shell(sshClient)
60+
return err
61+
}
62+
1563
// runCommand will attempt to run a command on a given machine. It will attempt
1664
// to SSH to the machine if it is identified as being remote.
1765
func runCommand(cmd string, machID string) (retcode int) {
@@ -76,6 +124,24 @@ func runRemoteCommand(cmd string, addr string, timeout time.Duration) (exit int,
76124
return
77125
}
78126

127+
// findUnits returns the machine ID of a running unit
128+
func findUnit(name string) (machID string, err error) {
129+
u, err := cAPI.Unit(name)
130+
if err != nil {
131+
return "", fmt.Errorf("Error retrieving Unit %s: %v", name, err)
132+
}
133+
if suToGlobal(*u) {
134+
return "", fmt.Errorf("Unable to connect to global unit %s.\n", name)
135+
}
136+
if u == nil {
137+
return "", fmt.Errorf("Unit %s does not exist.\n", name)
138+
} else if u.CurrentState == "" {
139+
return "", fmt.Errorf("Unit %s does not appear to be running.\n", name)
140+
}
141+
142+
return u.MachineID, nil
143+
}
144+
79145
func machineState(machID string) (*machine.MachineState, error) {
80146
machines, err := cAPI.Machines()
81147
if err != nil {

deisctl/client/client.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type DeisCtlClient interface {
1717
RefreshUnits(argv []string) error
1818
Restart(argv []string) error
1919
Scale(argv []string) error
20+
SSH(argv []string) error
2021
Start(argv []string) error
2122
Status(argv []string) error
2223
Stop(argv []string) error
@@ -90,6 +91,11 @@ func (c *Client) Scale(argv []string) error {
9091
return cmd.Scale(argv, c.Backend)
9192
}
9293

94+
// SSH opens an interactive shell with a machine in the cluster.
95+
func (c *Client) SSH(argv []string) error {
96+
return cmd.SSH(argv, c.Backend)
97+
}
98+
9399
// Start activates the specified components.
94100
func (c *Client) Start(argv []string) error {
95101
return cmd.Start(argv, c.Backend)

deisctl/cmd/cmd.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,3 +589,23 @@ Options:
589589
}
590590
return nil
591591
}
592+
593+
// SSH opens an interactive shell on a machine in the cluster
594+
func SSH(argv []string, b backend.Backend) error {
595+
usage := `Open an interactive shell on a machine in the cluster given a unit or machine id.
596+
597+
Usage:
598+
deisctl ssh <target>
599+
`
600+
// parse command-line arguments
601+
args, err := docopt.Parse(usage, argv, true, "", false)
602+
if err != nil {
603+
return err
604+
}
605+
606+
if err := b.SSH(args["<target>"].(string)); err != nil {
607+
return err
608+
}
609+
610+
return nil
611+
}

deisctl/cmd/cmd_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ func (backend stubBackend) Status(string) error {
4444
func (backend stubBackend) Journal(string) error {
4545
return fmt.Errorf("Journal not implemented yet.")
4646
}
47+
func (backend stubBackend) SSH(string) error {
48+
return fmt.Errorf("SSH not implemented yet.")
49+
}
4750

4851
var b stubBackend
4952

deisctl/deisctl.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Commands, use "deisctl help <command>" to learn more:
3939
journal print the log output of a component
4040
config set platform or component values
4141
refresh-units refresh unit files from GitHub
42+
ssh open an interacive shell on a machine in the cluster
4243
help show the help screen for a command
4344
4445
Options:
@@ -106,6 +107,8 @@ Options:
106107
err = c.Config(argv)
107108
case "refresh-units":
108109
err = c.RefreshUnits(argv)
110+
case "ssh":
111+
err = c.SSH(argv)
109112
case "help":
110113
fmt.Print(usage)
111114
return 0

docs/troubleshooting_deis/index.rst

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,11 @@ Common issues that users have run into when provisioning Deis are detailed below
1818
Logging in to the cluster
1919
-------------------------
2020

21-
Deis runs on CoreOS, so connecting is as simple as using ``ssh``.
22-
23-
CoreOS's default username is ``core``. Use the SSH key you provisioned the cluster with.
24-
25-
Connect to the public IP address of one of your nodes (or use "convenience" DNS records if you've set them up).
21+
To open a interactive shell on a machine in your cluster:
2622

2723
.. code-block:: console
2824
29-
$ ssh core@deis-1.example.com -i ~/.ssh/deis
30-
25+
$ deisctl ssh <unit>
3126
3227
Troubleshooting etcd
3328
--------------------

0 commit comments

Comments
 (0)