Skip to content

Commit 0c8bb88

Browse files
authored
Add enter command (canonical#188)
cmd: Add enter command
2 parents 5f77f10 + a346470 commit 0c8bb88

27 files changed

+520
-60
lines changed

cmd/pebble/cmd_add.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
"fmt"
1919
"io/ioutil"
2020

21-
"github.com/jessevdk/go-flags"
21+
"github.com/canonical/go-flags"
2222

2323
"github.com/canonical/pebble/client"
2424
)

cmd/pebble/cmd_autostart.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
package main
1616

1717
import (
18-
"github.com/jessevdk/go-flags"
18+
"github.com/canonical/go-flags"
1919

2020
"github.com/canonical/pebble/client"
2121
)

cmd/pebble/cmd_changes.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
"regexp"
2020
"sort"
2121

22-
"github.com/jessevdk/go-flags"
22+
"github.com/canonical/go-flags"
2323

2424
"github.com/canonical/pebble/client"
2525
)

cmd/pebble/cmd_checks.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ package main
1717
import (
1818
"fmt"
1919

20-
"github.com/jessevdk/go-flags"
20+
"github.com/canonical/go-flags"
2121

2222
"github.com/canonical/pebble/client"
2323
)

cmd/pebble/cmd_enter.go

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/canonical/go-flags"
7+
"github.com/canonical/pebble/internal/logger"
8+
)
9+
10+
const shortEnterHelp = "Run subcommand under a container environment"
11+
const longEnterHelp = `
12+
The enter command facilitates the use of Pebble as an entrypoint for containers.
13+
When used without a subcommand it mimics the behavior of the run command
14+
alone, while if used with a subcommand it runs that subcommand in the most
15+
appropriate environment taking into account its purpose.
16+
17+
These subcommands are currently supported:
18+
19+
help (1)(2)
20+
version (1)(2)
21+
plan (1)(2)
22+
services (1)(2)
23+
ls (1)(2)
24+
exec (2)
25+
start (3)
26+
stop (3)
27+
28+
(1) Services are not started.
29+
(2) No logs on stdout unless -v is used.
30+
(3) Services continue running after the subcommand succeeds.
31+
`
32+
33+
type enterFlags int
34+
35+
const (
36+
enterSilenceLogging enterFlags = 1 << iota
37+
enterNoServiceManager
38+
enterKeepServiceManager
39+
enterRequireServiceAutostart
40+
enterProhibitServiceAutostart
41+
)
42+
43+
type cmdEnter struct {
44+
clientMixin
45+
sharedRunEnterOpts
46+
Run bool `long:"run"`
47+
Positional struct {
48+
Cmd []string `positional-arg-name:"<subcommand>"`
49+
} `positional-args:"yes"`
50+
parser *flags.Parser
51+
}
52+
53+
func init() {
54+
optsHelp := map[string]string{
55+
"run": "Start default services before executing subcommand",
56+
}
57+
for k, v := range sharedRunEnterOptsHelp {
58+
optsHelp[k] = v
59+
}
60+
cmdInfo := addCommand("enter", shortEnterHelp, longEnterHelp, func() flags.Commander { return &cmdEnter{} }, optsHelp, nil)
61+
cmdInfo.extra = func(cmd *flags.Command) {
62+
cmd.PassAfterNonOption = true
63+
}
64+
}
65+
66+
func commandEnterFlags(commander flags.Commander) (enterFlags enterFlags, supported bool) {
67+
supported = true
68+
switch commander.(type) {
69+
case *cmdExec:
70+
enterFlags = enterSilenceLogging
71+
case *cmdHelp:
72+
enterFlags = enterNoServiceManager
73+
case *cmdLs, *cmdPlan, *cmdServices, *cmdVersion:
74+
enterFlags = enterSilenceLogging | enterProhibitServiceAutostart
75+
case *cmdStart:
76+
enterFlags = enterKeepServiceManager
77+
case *cmdStop:
78+
enterFlags = enterKeepServiceManager | enterRequireServiceAutostart
79+
default:
80+
supported = false
81+
}
82+
return
83+
}
84+
85+
func (cmd *cmdEnter) Execute(args []string) error {
86+
if len(args) > 0 {
87+
return ErrExtraArgs
88+
}
89+
90+
runCmd := cmdRun{
91+
sharedRunEnterOpts: cmd.sharedRunEnterOpts,
92+
}
93+
runCmd.setClient(cmd.client)
94+
95+
if len(cmd.Positional.Cmd) == 0 {
96+
runCmd.run(nil)
97+
return nil
98+
}
99+
100+
runCmd.Hold = !cmd.Run
101+
102+
var (
103+
commander flags.Commander
104+
extraArgs []string
105+
)
106+
107+
parser := Parser(cmd.client)
108+
parser.CommandHandler = func(c flags.Commander, a []string) error {
109+
commander = c
110+
extraArgs = a
111+
return nil
112+
}
113+
114+
if _, err := parser.ParseArgs(cmd.Positional.Cmd); err != nil {
115+
cmd.parser.Command.Active = parser.Command.Active
116+
return err
117+
}
118+
119+
if parser.Active == nil {
120+
panic("internal error: expected subcommand (parser.Active == nil)")
121+
}
122+
123+
enterFlags, supported := commandEnterFlags(commander)
124+
125+
if !supported {
126+
return fmt.Errorf("enter: subcommand %q is not supported", parser.Active.Name)
127+
}
128+
129+
if enterFlags&enterRequireServiceAutostart != 0 && !cmd.Run {
130+
return fmt.Errorf("enter: must use --run before %q subcommand", parser.Active.Name)
131+
}
132+
133+
if enterFlags&(enterProhibitServiceAutostart|enterNoServiceManager) != 0 && cmd.Run {
134+
return fmt.Errorf("enter: cannot provide --run before %q subcommand", parser.Active.Name)
135+
}
136+
137+
if enterFlags&enterNoServiceManager != 0 {
138+
if err := commander.Execute(extraArgs); err != nil {
139+
cmd.parser.Command.Active = parser.Command.Active
140+
return err
141+
}
142+
return nil
143+
}
144+
145+
if enterFlags&enterSilenceLogging != 0 && !cmd.Verbose {
146+
logger.SetLogger(logger.NullLogger)
147+
}
148+
149+
runReadyCh := make(chan func(), 1)
150+
runResultCh := make(chan interface{})
151+
var runStop func()
152+
153+
go func() {
154+
defer func() { runResultCh <- recover() }()
155+
runCmd.run(runReadyCh)
156+
}()
157+
158+
select {
159+
case runStop = <-runReadyCh:
160+
case runPanic := <-runResultCh:
161+
if runPanic == nil {
162+
panic("internal error: pebble daemon stopped early")
163+
}
164+
panic(runPanic)
165+
}
166+
167+
err := commander.Execute(extraArgs)
168+
169+
if err != nil {
170+
cmd.parser.Command.Active = parser.Command.Active
171+
}
172+
173+
if err != nil || enterFlags&enterKeepServiceManager == 0 {
174+
runStop()
175+
}
176+
177+
if runPanic := <-runResultCh; runPanic != nil {
178+
panic(runPanic)
179+
}
180+
181+
return err
182+
}
183+
184+
func (cmd *cmdEnter) setParser(parser *flags.Parser) {
185+
cmd.parser = parser
186+
}

0 commit comments

Comments
 (0)