/
main.go
256 lines (231 loc) · 6.93 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package main
import "flag"
import "fmt"
import "os"
import "path/filepath"
import "sort"
import "strconv"
import "github.com/juju/errors"
import "./jetpack"
import "./run"
var Host *jetpack.Host
func die(err error) {
if err != nil {
fmt.Fprintln(os.Stderr, errors.ErrorStack(err))
os.Exit(1)
}
}
func show(obj ...interface{}) {
die(Show("", obj...))
}
func subcommand(def string, args []string) (string, []string) {
if len(args) == 0 {
return def, args
}
return args[0], args[1:]
}
func image(name string) *jetpack.Image {
img, err := Host.Images.Get(name)
if err == jetpack.ErrNotFound {
die(errors.Errorf("No such image: %#v", name))
}
die(err)
return img
}
func main() {
configPath := jetpack.DefaultConfigPath
help := false
if cfg := os.Getenv("JETPACK_CONF"); cfg != "" {
configPath = cfg
}
flag.StringVar(&configPath, "config", configPath, "Configuration file")
flag.BoolVar(&help, "h", false, "Show help")
flag.BoolVar(&help, "help", false, "Show help")
flag.Parse()
args := flag.Args()
if help || len(args) == 0 || args[0] == "help" {
fmt.Fprintf(os.Stderr, `Usage: %s [OPTIONS] COMMAND...
Options:
-config=PATH Configuration file (%s)
-help, -h Display this help screen
Commands:
help Display this help screen
init Initialize host
info Show global information
test Run integration tests
image list [QUERY] List images
image import ARCHIVE [MANIFEST] Import image from an archive
image IMAGE build [OPTIONS] COMMAND... Build new image from an existing one
-dir=. Location on build directory on host
-cp=PATH... Copy additional files from host
image IMAGE show Display image details
image IMAGE destroy Destroy image
container list List containers
container create IMAGE Create new container from image
container CONTAINER show Display container details
container CONTAINER run Run container's application
container CONTAINER console [USER] Open console inside the container
container CONTAINER ps|top|killall [OPTIONS...]
Manage container's processes
container CONTAINER kill Kill running container
container CONTAINER destroy Destroy container
Needs Explanation:
ARCHIVE, MANIFEST May be filesystem paths or URLs.
cp=PATH This option can be given multiple times
QUERY Is an expression that looks like this:
- NAME[,LABEL=VALUE[,LABEL=VALUE[,...]]]
- NAME:VERSION (alias for NAME:version=VERSION)
IMAGE Can be:
- an UUID (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX),
- a checksum (sha512-...), or
- a QUERY (which can't be ambiguous).
CONTAINER Has to be an UUID for now
Helpful Aliases:
i ... -- image ...
c ... -- container ...
image, images -- image list
container, containers -- container list
image build|show|destroy IMAGE ... -- image IMAGE build|show|destroy ...
`)
return
}
command := args[0]
args = args[1:]
if host, err := jetpack.NewHost(configPath); err != nil {
die(err)
} else {
Host = host
}
if command == "init" {
// Init is special: it doesn't need an initialized host
die(Host.Initialize())
show(Host)
return
}
if Host.Dataset == nil {
die(errors.New("Host is not initialized"))
}
switch command {
case "info":
show(Host)
case "test":
die(run.Command(filepath.Join(jetpack.LibexecPath, "test.integration"),
append(args, "dataset="+Host.Dataset.Name)...).Run())
case "images":
command = "image"
args = append([]string{"list"}, args...)
fallthrough
case "image", "i":
switch command, args := subcommand("list", args); command {
case "import":
var archive, manifest string
switch len(args) {
case 2:
manifest = args[1]
fallthrough
case 1:
archive = args[0]
default:
die(errors.New("Usage: import ARCHIVE_URI [MANIFEST_URI]"))
}
image, err := Host.Images.Import(archive, manifest)
die(err)
show(image)
case "list":
images, err := Host.Images.All()
die(err)
if len(images) == 0 {
show("No images")
} else {
sort.Sort(images)
show(images.Table()) // FIXME: Table() doesn't really belong in images
}
case "build", "show", "destroy":
// be nice to people who prefer to type UUID after command
command, args[0] = args[0], command
fallthrough
default:
img := image(command)
switch command, args := subcommand("show", args); command {
case "build":
var copyFiles sliceFlag
var buildDir string
fs := flag.NewFlagSet("build", flag.ExitOnError)
fs.Var(©Files, "cp", "")
fs.StringVar(&buildDir, "dir", ".", "")
die(fs.Parse(args))
newImage, err := img.Build(buildDir, copyFiles, fs.Args())
die(err)
show(newImage)
case "show":
show(img)
case "destroy":
die(img.Destroy())
default:
die(errors.Errorf("Unknown command %#v", command))
}
}
case "containers":
command = "container"
args = append([]string{"list"}, args...)
fallthrough
case "container", "c":
switch command, args := subcommand("list", args); command {
case "create":
container, err := Host.Containers.Clone(image(args[0]))
die(err)
show(container)
case "list":
if containers, err := Host.Containers.All(); err != nil {
die(err)
} else {
if len(containers) == 0 {
show("No containers")
} else {
sort.Sort(containers)
show(containers.Table()) // FIXME: Table() doesn't really belong in containers
}
}
case "show", "run", "ps", "top", "killall", "kill", "destroy":
// be nice to people who prefer to type UUID after command
command, args[0] = args[0], command
fallthrough
default:
container, err := Host.Containers.Get(command)
if err == jetpack.ErrNotFound {
die(errors.Errorf("No such container: %#v", command))
}
die(err)
switch command, args := subcommand("show", args); command {
case "show":
show(container)
case "run":
die(container.Run(nil))
case "console":
user := "root"
if len(args) != 0 {
user = args[0]
}
die(container.Run(jetpack.ConsoleApp(user)))
case "ps", "top", "killall":
jid := container.Jid()
if jid == 0 {
die(errors.New("Container is not running"))
}
flag := "-J"
if command == "killall" {
flag = "-j"
}
die(run.Command(command, append([]string{flag, strconv.Itoa(jid)}, args...)...).Run())
case "kill":
die(container.Kill())
case "destroy":
die(container.Destroy())
default:
die(errors.Errorf("Unknown command %#v", command))
}
}
default:
die(errors.Errorf("Unknown command %#v", command))
}
}