Skip to content

Commit

Permalink
Implement shortcut commands
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelsauter committed Apr 2, 2018
1 parent bc572cf commit 22fc7bc
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 24 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@

* [Feature] Add an easy way to share the SSH socket with a container by adding the `share-ssh-socket` configuration.

* [Feature] Implement shortcut commands. It is now possible to define commands
in the `crane.yml`, which can be executed by running `crane cmd <name>`. For
example, one could define `console: run web rails c` to run a Rails console in
an ad-hoc `web` container with `crane cmd console`. Another use case is e.g.
`psql: exec postgres psql` to run PSQL inside the `postgres` container via
`crane cmd psql`.

## 3.3.4 (2018-03-22)

* [Bugfix] Attach --volumes flag to `rm` command, not `provision`
Expand Down
7 changes: 7 additions & 0 deletions crane.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ services:
workdir: /go/src/github.com/michaelsauter/crane
cmd: ["bash"]

commands:
test: run crane make test
build: run crane make build
build-darwin: run crane make build-darwin
build-darwin-pro: run crane make build-darwin-pro
gofmt: run crane gofmt -w crane

accelerated-mounts:
crane:
uid: 1000
Expand Down
41 changes: 39 additions & 2 deletions crane/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package crane

import (
"fmt"
"github.com/alecthomas/kingpin"
"os"
"sort"
"strings"

"github.com/alecthomas/kingpin"
)

var cfg Config
Expand Down Expand Up @@ -42,6 +42,13 @@ var (
"Override image tags.",
).String()

cmdCommand = app.Command(
"cmd",
"Execute predefined command",
)
cmdCmdArg = cmdCommand.Arg("command", "Command to execute").String()
cmdArgumentsArg = cmdCommand.Arg("arguments", "Arguments for the command").Strings()

upCommand = app.Command(
"up",
"Build or pull images if they don't exist, then run or start the containers. Alias of `lift`.",
Expand Down Expand Up @@ -356,6 +363,36 @@ func runCli() {
}

switch command {
case cmdCommand.FullCommand():
cfg = NewConfig(*configFlag, *prefixFlag, *tagFlag)
printCmds := func() {
cmds := cfg.Cmds()
cmdNames := []string{}
for name, _ := range cmds {
cmdNames = append(cmdNames, name)
}
sort.Strings(cmdNames)
for _, name := range cmdNames {
fmt.Fprintf(os.Stdout, "%s: ", name)
printInfof("%s\n", strings.Join(cmds[name], " "))
}
}
if len(*cmdCmdArg) == 0 {
printCmds()
return
}
definedCmd := cfg.Cmd(*cmdCmdArg)
if definedCmd == nil {
printErrorf("No such command: %s\n\n", *cmdCmdArg)
fmt.Fprintf(os.Stdout, "Available commands:\n")
printCmds()
return
}
args := []string{}
args = append(args, definedCmd...)
args = append(args, *cmdArgumentsArg...)
executeCommand("crane", args, os.Stdout, os.Stderr)

case upCommand.FullCommand():
commandAction(*upTargetArg, func(uow *UnitOfWork) {
uow.Up(*upCmdArg, *upDetachFlag, *upNoCacheFlag, *upParallelFlag)
Expand Down
18 changes: 18 additions & 0 deletions crane/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ type Config interface {
Tag() string
NetworkNames() []string
VolumeNames() []string
Cmds() map[string][]string
AcceleratedMountNames() []string
Network(name string) Network
Volume(name string) Volume
Cmd(name string) []string
AcceleratedMount(volume string) AcceleratedMount
ContainerMap() ContainerMap
Container(name string) Container
Expand All @@ -41,13 +43,15 @@ type config struct {
RawHooks map[string]hooks `json:"hooks" yaml:"hooks"`
RawNetworks map[string]*network `json:"networks" yaml:"networks"`
RawVolumes map[string]*volume `json:"volumes" yaml:"volumes"`
RawCmds map[string]interface{} `json:"commands" yaml:"commands"`
RawAcceleratedMounts map[string]*acceleratedMount `json:"accelerated-mounts" yaml:"accelerated-mounts"`
RawMacSyncs map[string]*acceleratedMount `json:"mac-syncs" yaml:"mac-syncs"`
containerMap ContainerMap
networkMap NetworkMap
volumeMap VolumeMap
acceleratedMountMap AcceleratedMountMap
groups map[string][]string
cmds map[string][]string
path string
prefix string
tag string
Expand Down Expand Up @@ -239,6 +243,10 @@ func (c *config) VolumeNames() []string {
return volumes
}

func (c *config) Cmds() map[string][]string {
return c.cmds
}

func (c *config) AcceleratedMountNames() []string {
acceleratedMounts := []string{}
for name, _ := range c.acceleratedMountMap {
Expand All @@ -260,6 +268,10 @@ func (c *config) AcceleratedMount(name string) AcceleratedMount {
return c.acceleratedMountMap[name]
}

func (c *config) Cmd(name string) []string {
return c.cmds[name]
}

// Load configuration into the internal structs from the raw, parsed ones
func (c *config) initialize(prefixFlag string) {
// Local container map to query by expanded name
Expand Down Expand Up @@ -289,6 +301,12 @@ func (c *config) initialize(prefixFlag string) {
}
}
}
// Cmds
c.cmds = make(map[string][]string)
for cmdRawName, rawCmd := range c.RawCmds {
cmdName := expandEnv(cmdRawName)
c.cmds[cmdName] = stringSlice(rawCmd)
}
// Container map
c.containerMap = make(map[string]Container)
for name, container := range containerMap {
Expand Down
26 changes: 4 additions & 22 deletions crane/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bufio"
"encoding/json"
"fmt"
"github.com/flynn/go-shlex"
"io"
"os"
"path/filepath"
Expand Down Expand Up @@ -865,29 +864,12 @@ func (c *container) Workdir() string {

func (c *container) Cmd() []string {
var cmd []string
var rCmd = c.RawCommand
var rawCmd = c.RawCommand
if c.RawCmd != nil {
rCmd = c.RawCmd
rawCmd = c.RawCmd
}
if rCmd != nil {
switch rawCmd := rCmd.(type) {
case string:
if len(rawCmd) > 0 {
cmds, err := shlex.Split(expandEnv(rawCmd))
if err != nil {
printErrorf("Error when parsing cmd `%v`: %v. Proceeding with %q.", rawCmd, err, cmds)
}
cmd = append(cmd, cmds...)
}
case []interface{}:
cmds := make([]string, len(rawCmd))
for i, v := range rawCmd {
cmds[i] = expandEnv(fmt.Sprintf("%v", v))
}
cmd = append(cmd, cmds...)
default:
panic(StatusError{fmt.Errorf("unknown type: %v", rawCmd), 65})
}
if rawCmd != nil {
cmd = append(cmd, stringSlice(rawCmd)...)
}
return cmd
}
Expand Down
24 changes: 24 additions & 0 deletions crane/crane.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,30 @@ func checkDockerClient() {
}
}

// Assemble slice of strings from slice or string with spaces
func stringSlice(sliceLike interface{}) []string {
var strSlice []string
switch sl := sliceLike.(type) {
case string:
if len(sl) > 0 {
parts, err := shlex.Split(expandEnv(sl))
if err != nil {
printErrorf("Error when parsing cmd `%v`: %v. Proceeding with %q.", sl, err, parts)
}
strSlice = append(strSlice, parts...)
}
case []interface{}:
parts := make([]string, len(sl))
for i, v := range sl {
parts[i] = expandEnv(fmt.Sprintf("%v", v))
}
strSlice = append(strSlice, parts...)
default:
panic(StatusError{fmt.Errorf("unknown type: %v", sl), 65})
}
return strSlice
}

// Similar to strings.Join() for int slices.
func intJoin(intSlice []int, sep string) string {
var stringSlice []string
Expand Down

0 comments on commit 22fc7bc

Please sign in to comment.