Skip to content

Commit 4a4de39

Browse files
authored
Merge pull request #54 from stevemurr/master
Updated SSH docker example
2 parents c986e7f + 2d7ad48 commit 4a4de39

File tree

3 files changed

+128
-4
lines changed

3 files changed

+128
-4
lines changed

_examples/ssh-docker/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM alpine
2+
RUN apk add -U jq
3+
ENTRYPOINT ["jq"]

_examples/ssh-docker/README.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# SSH Docker Example
2+
Run docker containers over SSH. You can even pipe things into them too!
3+
4+
# Installation / Prep
5+
We're going to build JQ as an SSH service using the Glider Labs SSH package. If you haven't installed GoLang and docker yet, see the doc's for help getting your environment setup.
6+
7+
Install the Glider Labs SSH package
8+
`go get github.com/gliderlabs/ssh`
9+
10+
Build the example docker container with
11+
`docker build --rm -t jq .`
12+
13+
# Usage
14+
Run the SSH server with
15+
16+
`go run docker.go`
17+
18+
This SSH service uses the user name passed into the connection to key the desired docker image. So to use jq over our SSH server we would say:
19+
20+
`ssh jq@localhost -p 2222`
21+
22+
If we run this command we will see
23+
24+
```
25+
jq - commandline JSON processor [version 1.5]
26+
Usage: jq [options] <jq filter> [file...]
27+
28+
jq is a tool for processing JSON inputs, applying the
29+
given filter to its JSON text inputs and producing the
30+
filter's results as JSON on standard output.
31+
The simplest filter is ., which is the identity filter,
32+
copying jq's input to its output unmodified (except for
33+
formatting).
34+
For more advanced filters see the jq(1) manpage ("man jq")
35+
and/or https://stedolan.github.io/jq
36+
37+
Some of the options include:
38+
-c compact instead of pretty-printed output;
39+
-n use `null` as the single input value;
40+
-e set the exit status code based on the output;
41+
-s read (slurp) all inputs into an array; apply filter to it;
42+
-r output raw strings, not JSON texts;
43+
-R read raw strings, not JSON texts;
44+
-C colorize JSON;
45+
-M monochrome (don't colorize JSON);
46+
-S sort keys of objects on output;
47+
--tab use tabs for indentation;
48+
--arg a v set variable $a to value <v>;
49+
--argjson a v set variable $a to JSON value <v>;
50+
--slurpfile a f set variable $a to an array of JSON texts read from <f>;
51+
See the manpage for more options.
52+
Connection to localhost closed.
53+
```
54+
55+
JQ's help text! It's working! Now let's pipe some json into our SSH service and marvel at the awesomeness.
56+
57+
`curl -s https://api.github.com/orgs/gliderlabs | ssh jq@localhost -p 2222 .`
58+
59+
```json
60+
{
61+
"login": "gliderlabs",
62+
"id": 8484931,
63+
"url": "https://api.github.com/orgs/gliderlabs",
64+
"repos_url": "https://api.github.com/orgs/gliderlabs/repos",
65+
"events_url": "https://api.github.com/orgs/gliderlabs/events",
66+
"hooks_url": "https://api.github.com/orgs/gliderlabs/hooks",
67+
"issues_url": "https://api.github.com/orgs/gliderlabs/issues",
68+
"members_url": "https://api.github.com/orgs/gliderlabs/members{/member}",
69+
"public_members_url": "https://api.github.com/orgs/gliderlabs/public_members{/member}",
70+
"avatar_url": "https://avatars3.githubusercontent.com/u/8484931?v=4",
71+
"description": "",
72+
"name": "Glider Labs",
73+
"company": null,
74+
"blog": "http://gliderlabs.com",
75+
"location": "Austin, TX",
76+
"email": "[email protected]",
77+
"has_organization_projects": true,
78+
"has_repository_projects": true,
79+
"public_repos": 29,
80+
"public_gists": 0,
81+
"followers": 0,
82+
"following": 0,
83+
"html_url": "https://github.com/gliderlabs",
84+
"created_at": "2014-08-18T23:25:37Z",
85+
"updated_at": "2017-08-21T09:52:17Z",
86+
"type": "Organization"
87+
}
88+
```
89+
90+
# Conclusion
91+
We built JQ as a service over SSH in Go using the Glider Labs SSH package. We showed how you can run docker containers through the service as well as how to pipe stuff into your SSH service.
92+
93+
94+
# Troubleshooting
95+
96+
A new host key is generated each time we run the server so you'll probably see this error on the second run.
97+
```
98+
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
99+
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
100+
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
101+
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
102+
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
103+
It is also possible that a host key has just been changed.
104+
The fingerprint for the RSA key sent by the remote host is
105+
SHA256:9dyZ8KrMCPGvDqECggoDFyz51gA6DdHa9zpfl/5Kgos.
106+
Please contact your system administrator.
107+
Add correct host key in /Users/murr/.ssh/known_hosts to get rid of this message.
108+
Offending RSA key in /Users/murr/.ssh/known_hosts:7
109+
RSA host key for [localhost]:2222 has changed and you have requested strict checking.
110+
Host key verification failed.
111+
```
112+
113+
To bypass this error, regnerate your host key with
114+
`ssh-keygen -R "[localhost]:2222"`

_examples/ssh-docker/docker.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func main() {
2828
StdinOnce: true,
2929
Volumes: make(map[string]struct{}),
3030
}
31-
err, status, cleanup := dockerRun(cfg, sess)
31+
status, cleanup, err := dockerRun(cfg, sess)
3232
defer cleanup()
3333
if err != nil {
3434
fmt.Fprintln(sess, err)
@@ -41,7 +41,7 @@ func main() {
4141
log.Fatal(ssh.ListenAndServe(":2222", nil))
4242
}
4343

44-
func dockerRun(cfg *container.Config, sess ssh.Session) (err error, status int64, cleanup func()) {
44+
func dockerRun(cfg *container.Config, sess ssh.Session) (status int64, cleanup func(), err error) {
4545
docker, err := client.NewEnvClient()
4646
if err != nil {
4747
panic(err)
@@ -70,7 +70,9 @@ func dockerRun(cfg *container.Config, sess ssh.Session) (err error, status int64
7070
docker.ContainerRemove(ctx, res.ID, types.ContainerRemoveOptions{})
7171
stream.Close()
7272
}
73+
7374
outputErr := make(chan error)
75+
7476
go func() {
7577
var err error
7678
if cfg.Tty {
@@ -80,10 +82,12 @@ func dockerRun(cfg *container.Config, sess ssh.Session) (err error, status int64
8082
}
8183
outputErr <- err
8284
}()
85+
8386
go func() {
8487
defer stream.CloseWrite()
8588
io.Copy(stream.Conn, sess)
8689
}()
90+
8791
err = docker.ContainerStart(ctx, res.ID, types.ContainerStartOptions{})
8892
if err != nil {
8993
return
@@ -103,9 +107,12 @@ func dockerRun(cfg *container.Config, sess ssh.Session) (err error, status int64
103107
}
104108
}()
105109
}
106-
status, err = docker.ContainerWait(ctx, res.ID)
107-
if err != nil {
110+
resultC, errC := docker.ContainerWait(ctx, res.ID, container.WaitConditionNotRunning)
111+
select {
112+
case err = <-errC:
108113
return
114+
case result := <-resultC:
115+
status = result.StatusCode
109116
}
110117
err = <-outputErr
111118
return

0 commit comments

Comments
 (0)