Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add up/down/pull commands #49

Merged
merged 5 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 56 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,7 @@ outputs:
path: /root/.kube/config
```

---

## Examples

### Docker Compose Up
### Example

```yaml
docker-compose:
Expand All @@ -169,18 +165,67 @@ docker-compose:
timeout: 30
```

### Docker Compose Down
## Mixin Commands

This mixin has built-in support for common Docker Compose commands. Supported
commands can be defined within the `docker-compose` step and support the same
fields and options.

Arguments and flags can be set on both the `docker-compose` step and on the
specified sub-command. Arguments and flags set on the `docker-compose` step
will be applied to the `docker-compose` part of the command while arguments
and flags set on the sub-command will be applied to the sub-command.

You may only specify one command within a single `docker-compose` step. To
execute multiple commands, define multiple `docker-compose` steps.

### Docker Compose Pull

```yaml
docker-compose:
description: "Docker Compose Down"
arguments:
- down
- --remove-orphans
description: Docker Compose Pull
flags:
timeout: 30
file: compose.yml
pull:
arguments:
- serviceA
- serviceB
flags:
ignore-pull-failures:
policy: missing
```

This is equivilent to: `docker-compose --file compose.yml pull --ignore-pull-failures --policy missing serviceA serviceB`
eugencowie marked this conversation as resolved.
Show resolved Hide resolved

### Docker Compose Up

```yaml
docker-compose:
description: Docker Compose Up
up:
arguments:
- serviceA
- serviceB
flags:
detach:
timeout: 30
```

This is equivilent to: `docker-compose up --detach --timeout 30 serviceA serviceB`
eugencowie marked this conversation as resolved.
Show resolved Hide resolved

### Docker Compose Down

```yaml
docker-compose:
description: Docker Compose Down
down:
flags:
remove-orphans:
timeout: 30
```

This is equivilent to: `docker-compose down --remove-orphans --timeout 30`
eugencowie marked this conversation as resolved.
Show resolved Hide resolved

See full bundle examples in the `examples` directory.

## Invocation
Expand Down
2 changes: 1 addition & 1 deletion cmd/docker-compose/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func main() {
func buildRootCommand(m *dockercompose.Mixin, in io.Reader) *cobra.Command {
cmd := &cobra.Command{
Use: "docker-compose",
Long: "A skeleton mixin to use for building other mixins for porter 👩🏽‍✈️",
Long: "Use the Docker Compose CLI with Porter",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
// Enable swapping out stdout/stderr for testing
m.In = in
Expand Down
12 changes: 6 additions & 6 deletions examples/compose/porter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ mixins:
install:
- docker-compose:
description: Docker Compose up
arguments:
- up
- -d
up:
flags:
detach:

ps:
- docker-compose:
Expand All @@ -37,6 +37,6 @@ ps:
uninstall:
- docker-compose:
description: Docker Compose down
arguments:
- down
- --remove-orphans
down:
flags:
remove-orphans:
15 changes: 7 additions & 8 deletions examples/dockervolume/porter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ install:

- docker-compose:
description: "Docker Compose Up"
arguments:
- up
- -d
up:
flags:
detach:

- exec:
description: "Print app logs"
Expand All @@ -47,9 +47,9 @@ upgrade:

- docker-compose:
description: "Docker Compose Up"
arguments:
- up
- -d
up:
flags:
detach:

- exec:
description: "Print app logs"
Expand All @@ -60,8 +60,7 @@ upgrade:
uninstall:
- docker-compose:
description: "Docker Compose Down"
arguments:
- down
down:

- exec:
description: "Remove Docker Volume"
Expand Down
110 changes: 51 additions & 59 deletions pkg/docker-compose/action.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package dockercompose

import (
"get.porter.sh/mixin/docker-compose/pkg/docker-compose/commands"
"get.porter.sh/porter/pkg/exec/builder"
"gopkg.in/yaml.v3"
)

var _ builder.ExecutableAction = Action{}
Expand Down Expand Up @@ -49,71 +51,61 @@ func (a Action) GetSteps() []builder.ExecutableStep {
}

type Step struct {
Instruction `yaml:"docker-compose"`
commands.ComposeCommand `yaml:"docker-compose"`
}

var _ builder.ExecutableStep = Step{}
var _ builder.StepWithOutputs = Step{}
var _ builder.SuppressesOutput = Step{}

type Instruction struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Arguments []string `yaml:"arguments,omitempty"`
Flags builder.Flags `yaml:"flags,omitempty"`
Outputs []Output `yaml:"outputs,omitempty"`
SuppressOutput bool `yaml:"suppress-output,omitempty"`
WorkingDirectory string `yaml:"dir,omitempty"`
}

func (s Step) GetCommand() string {
return "docker-compose"
}

func (s Step) GetArguments() []string {
return s.Arguments
}

func (s Step) GetFlags() builder.Flags {
return s.Flags
}

func (s Step) GetWorkingDir() string {
return s.WorkingDirectory
}

func (s Step) GetOutputs() []builder.Output {
// Go doesn't have generics, nothing to see here...
outputs := make([]builder.Output, len(s.Outputs))
for i := range s.Outputs {
outputs[i] = s.Outputs[i]
// UnmarshalYAML takes any yaml in this form
//
// docker-compose:
// description: something
// COMMAND: # e.g. pull/up/down -> make the PullCommand/UpCommand/DownCommand for us
func (s *Step) UnmarshalYAML(unmarshal func(interface{}) error) error {
// Turn the yaml into a raw map so we can iterate over the values and
// look for which command was used
stepMap := map[string]map[string]interface{}{}
err := unmarshal(&stepMap)
if err != nil {
return err
}
return outputs
}

func (s Step) SuppressesOutput() bool {
return s.SuppressOutput
}

var _ builder.OutputJsonPath = Output{}
var _ builder.OutputFile = Output{}

type Output struct {
Name string `yaml:"name"`
// Get at the values defined under "docker-compose"
composeStep := stepMap["docker-compose"]
b, err := yaml.Marshal(composeStep)
if err != nil {
return err
}
err = yaml.Unmarshal(b, &s.ComposeCommand)
if err != nil {
return err
}

// See https://porter.sh/mixins/exec/#outputs
JsonPath string `yaml:"jsonPath,omitempty"`
FilePath string `yaml:"path,omitempty"`
}
// Turn the command into its typed data structure
for key, value := range composeStep {
var cmd commands.Command

switch key {
case "down":
cmd = &commands.DownCommand{}
case "pull":
cmd = &commands.PullCommand{}
case "up":
cmd = &commands.UpCommand{}
default:
continue
}

func (o Output) GetName() string {
return o.Name
}
b, err = yaml.Marshal(value)
if err != nil {
return err
}
err = yaml.Unmarshal(b, cmd)
if err != nil {
return err
}

func (o Output) GetJsonPath() string {
return o.JsonPath
}
s.Subcommand = &cmd
break // There is only 1 command
}

func (o Output) GetFilePath() string {
return o.FilePath
return nil
}
89 changes: 61 additions & 28 deletions pkg/docker-compose/action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"io/ioutil"
"testing"

"get.porter.sh/mixin/docker-compose/pkg/docker-compose/commands"
"get.porter.sh/porter/pkg/exec/builder"

"github.com/stretchr/testify/assert"
Expand All @@ -12,40 +13,72 @@ import (
)

func TestMixin_UnmarshalStep(t *testing.T) {
b, err := ioutil.ReadFile("testdata/step-input.yaml")
require.NoError(t, err)
testcases := []struct {
name string // Test case name
file string // Path to the test input yaml
wantDescription string // Description that you expect to be found
wantArguments []string // Arguments that you expect to be found
wantFlags builder.Flags // Flags that you expect to be found
wantSuffixArgs []string // Suffix arguments that you expect to be found
wantOutputs []commands.Output // Outputs that you expect to be found
wantSuppress bool
}{
{
"step", "testdata/step-input.yaml", "Compose Up",
[]string{"up", "-d"},
builder.Flags{builder.NewFlag("timeout", "25")}, nil,
[]commands.Output{{Name: "containerId", JsonPath: "$Id"}}, false,
},
{
"step-suppress-output", "testdata/step-input-suppress-output.yaml",
"Supressed Surprise", []string{"surprise", "me"}, nil, nil, nil, true,
},
{
"down", "testdata/commands/down-input.yaml", "Compose Down", nil,
builder.Flags{builder.NewFlag("file", "test.yml")},
[]string{"down", "--remove-orphans", "--timeout", "25", "serviceA", "serviceB"},
[]commands.Output{{Name: "containerId", JsonPath: "$Id"}}, false,
},
{
"pull", "testdata/commands/pull-input.yaml", "Compose Pull", nil,
builder.Flags{builder.NewFlag("file", "test.yml")},
[]string{"pull", "--ignore-pull-failures", "--policy", "missing", "serviceA", "serviceB"},
[]commands.Output{{Name: "containerId", JsonPath: "$Id"}}, false,
},
{
"up", "testdata/commands/up-input.yaml", "Compose Up", nil,
builder.Flags{builder.NewFlag("file", "test.yml")},
[]string{"up", "--detach", "--timeout", "25", "serviceA", "serviceB"},
[]commands.Output{{Name: "containerId", JsonPath: "$Id"}}, false,
},
}

var action Action
err = yaml.Unmarshal(b, &action)
require.NoError(t, err)
require.Len(t, action.Steps, 1)
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
b, err := ioutil.ReadFile(tc.file)
require.NoError(t, err)

step := action.Steps[0]
assert.Equal(t, "Compose Up", step.Description)
assert.NotEmpty(t, step.Outputs)
assert.Equal(t, Output{Name: "containerId", JsonPath: "$Id"}, step.Outputs[0])
var action Action
err = yaml.Unmarshal(b, &action)
require.NoError(t, err)
require.Len(t, action.Steps, 1)

require.Len(t, step.Arguments, 2)
assert.Equal(t, "up", step.Arguments[0])
assert.Equal(t, "-d", step.Arguments[1])
step := action.Steps[0]
assert.Equal(t, tc.wantDescription, step.Description)

require.Len(t, step.Flags, 1)
assert.Equal(t, builder.NewFlag("timeout", "25"), step.Flags[0])
args := step.GetArguments()
assert.Equal(t, tc.wantArguments, args)

assert.Equal(t, false, step.SuppressOutput)
assert.Equal(t, false, step.SuppressesOutput())
}
flags := step.GetFlags()
assert.Equal(t, tc.wantFlags, flags)

func TestStep_SuppressesOutput(t *testing.T) {
b, err := ioutil.ReadFile("testdata/step-input-suppress-output.yaml")
require.NoError(t, err)
suffixArgs := step.GetSuffixArguments()
assert.Equal(t, tc.wantSuffixArgs, suffixArgs)

var action Action
err = yaml.Unmarshal(b, &action)
require.NoError(t, err)
require.Len(t, action.Steps, 1)
outputs := step.GetOutputs()
assert.ElementsMatch(t, tc.wantOutputs, outputs)

step := action.Steps[0]
assert.Equal(t, true, step.SuppressOutput)
assert.Equal(t, true, step.SuppressesOutput())
assert.Equal(t, tc.wantSuppress, step.SuppressesOutput(), "invalid suppress-output")
})
}
}
Loading