Skip to content

Commit

Permalink
Add enter command (#188)
Browse files Browse the repository at this point in the history
cmd: Add enter command
  • Loading branch information
niemeyer authored Apr 6, 2023
2 parents 5f77f10 + a346470 commit 0c8bb88
Show file tree
Hide file tree
Showing 27 changed files with 520 additions and 60 deletions.
2 changes: 1 addition & 1 deletion cmd/pebble/cmd_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"fmt"
"io/ioutil"

"github.com/jessevdk/go-flags"
"github.com/canonical/go-flags"

"github.com/canonical/pebble/client"
)
Expand Down
2 changes: 1 addition & 1 deletion cmd/pebble/cmd_autostart.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
package main

import (
"github.com/jessevdk/go-flags"
"github.com/canonical/go-flags"

"github.com/canonical/pebble/client"
)
Expand Down
2 changes: 1 addition & 1 deletion cmd/pebble/cmd_changes.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"regexp"
"sort"

"github.com/jessevdk/go-flags"
"github.com/canonical/go-flags"

"github.com/canonical/pebble/client"
)
Expand Down
2 changes: 1 addition & 1 deletion cmd/pebble/cmd_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package main
import (
"fmt"

"github.com/jessevdk/go-flags"
"github.com/canonical/go-flags"

"github.com/canonical/pebble/client"
)
Expand Down
186 changes: 186 additions & 0 deletions cmd/pebble/cmd_enter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package main

import (
"fmt"

"github.com/canonical/go-flags"
"github.com/canonical/pebble/internal/logger"
)

const shortEnterHelp = "Run subcommand under a container environment"
const longEnterHelp = `
The enter command facilitates the use of Pebble as an entrypoint for containers.
When used without a subcommand it mimics the behavior of the run command
alone, while if used with a subcommand it runs that subcommand in the most
appropriate environment taking into account its purpose.
These subcommands are currently supported:
help (1)(2)
version (1)(2)
plan (1)(2)
services (1)(2)
ls (1)(2)
exec (2)
start (3)
stop (3)
(1) Services are not started.
(2) No logs on stdout unless -v is used.
(3) Services continue running after the subcommand succeeds.
`

type enterFlags int

const (
enterSilenceLogging enterFlags = 1 << iota
enterNoServiceManager
enterKeepServiceManager
enterRequireServiceAutostart
enterProhibitServiceAutostart
)

type cmdEnter struct {
clientMixin
sharedRunEnterOpts
Run bool `long:"run"`
Positional struct {
Cmd []string `positional-arg-name:"<subcommand>"`
} `positional-args:"yes"`
parser *flags.Parser
}

func init() {
optsHelp := map[string]string{
"run": "Start default services before executing subcommand",
}
for k, v := range sharedRunEnterOptsHelp {
optsHelp[k] = v
}
cmdInfo := addCommand("enter", shortEnterHelp, longEnterHelp, func() flags.Commander { return &cmdEnter{} }, optsHelp, nil)
cmdInfo.extra = func(cmd *flags.Command) {
cmd.PassAfterNonOption = true
}
}

func commandEnterFlags(commander flags.Commander) (enterFlags enterFlags, supported bool) {
supported = true
switch commander.(type) {
case *cmdExec:
enterFlags = enterSilenceLogging
case *cmdHelp:
enterFlags = enterNoServiceManager
case *cmdLs, *cmdPlan, *cmdServices, *cmdVersion:
enterFlags = enterSilenceLogging | enterProhibitServiceAutostart
case *cmdStart:
enterFlags = enterKeepServiceManager
case *cmdStop:
enterFlags = enterKeepServiceManager | enterRequireServiceAutostart
default:
supported = false
}
return
}

func (cmd *cmdEnter) Execute(args []string) error {
if len(args) > 0 {
return ErrExtraArgs
}

runCmd := cmdRun{
sharedRunEnterOpts: cmd.sharedRunEnterOpts,
}
runCmd.setClient(cmd.client)

if len(cmd.Positional.Cmd) == 0 {
runCmd.run(nil)
return nil
}

runCmd.Hold = !cmd.Run

var (
commander flags.Commander
extraArgs []string
)

parser := Parser(cmd.client)
parser.CommandHandler = func(c flags.Commander, a []string) error {
commander = c
extraArgs = a
return nil
}

if _, err := parser.ParseArgs(cmd.Positional.Cmd); err != nil {
cmd.parser.Command.Active = parser.Command.Active
return err
}

if parser.Active == nil {
panic("internal error: expected subcommand (parser.Active == nil)")
}

enterFlags, supported := commandEnterFlags(commander)

if !supported {
return fmt.Errorf("enter: subcommand %q is not supported", parser.Active.Name)
}

if enterFlags&enterRequireServiceAutostart != 0 && !cmd.Run {
return fmt.Errorf("enter: must use --run before %q subcommand", parser.Active.Name)
}

if enterFlags&(enterProhibitServiceAutostart|enterNoServiceManager) != 0 && cmd.Run {
return fmt.Errorf("enter: cannot provide --run before %q subcommand", parser.Active.Name)
}

if enterFlags&enterNoServiceManager != 0 {
if err := commander.Execute(extraArgs); err != nil {
cmd.parser.Command.Active = parser.Command.Active
return err
}
return nil
}

if enterFlags&enterSilenceLogging != 0 && !cmd.Verbose {
logger.SetLogger(logger.NullLogger)
}

runReadyCh := make(chan func(), 1)
runResultCh := make(chan interface{})
var runStop func()

go func() {
defer func() { runResultCh <- recover() }()
runCmd.run(runReadyCh)
}()

select {
case runStop = <-runReadyCh:
case runPanic := <-runResultCh:
if runPanic == nil {
panic("internal error: pebble daemon stopped early")
}
panic(runPanic)
}

err := commander.Execute(extraArgs)

if err != nil {
cmd.parser.Command.Active = parser.Command.Active
}

if err != nil || enterFlags&enterKeepServiceManager == 0 {
runStop()
}

if runPanic := <-runResultCh; runPanic != nil {
panic(runPanic)
}

return err
}

func (cmd *cmdEnter) setParser(parser *flags.Parser) {
cmd.parser = parser
}
Loading

0 comments on commit 0c8bb88

Please # to comment.