From 91aa7fb0852c9d36915c82ce2000d2b0a7051b9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Thu, 5 Sep 2024 01:22:04 +0100 Subject: [PATCH 1/5] Create command interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Eugén Cowie --- pkg/docker-compose/action.go | 69 +------------------------- pkg/docker-compose/action_test.go | 3 +- pkg/docker-compose/commands/command.go | 31 ++++++++++++ pkg/docker-compose/commands/compose.go | 47 ++++++++++++++++++ 4 files changed, 82 insertions(+), 68 deletions(-) create mode 100644 pkg/docker-compose/commands/command.go create mode 100644 pkg/docker-compose/commands/compose.go diff --git a/pkg/docker-compose/action.go b/pkg/docker-compose/action.go index 389d4ec..87ae7da 100644 --- a/pkg/docker-compose/action.go +++ b/pkg/docker-compose/action.go @@ -1,6 +1,7 @@ package dockercompose import ( + "get.porter.sh/mixin/docker-compose/pkg/docker-compose/commands" "get.porter.sh/porter/pkg/exec/builder" ) @@ -49,71 +50,5 @@ func (a Action) GetSteps() []builder.ExecutableStep { } type Step struct { - Instruction `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] - } - 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"` - - // See https://porter.sh/mixins/exec/#outputs - JsonPath string `yaml:"jsonPath,omitempty"` - FilePath string `yaml:"path,omitempty"` -} - -func (o Output) GetName() string { - return o.Name -} - -func (o Output) GetJsonPath() string { - return o.JsonPath -} - -func (o Output) GetFilePath() string { - return o.FilePath + commands.ComposeCommand `yaml:"docker-compose"` } diff --git a/pkg/docker-compose/action_test.go b/pkg/docker-compose/action_test.go index 1477049..f3e815f 100644 --- a/pkg/docker-compose/action_test.go +++ b/pkg/docker-compose/action_test.go @@ -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" @@ -23,7 +24,7 @@ func TestMixin_UnmarshalStep(t *testing.T) { 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]) + assert.Equal(t, commands.Output{Name: "containerId", JsonPath: "$Id"}, step.Outputs[0]) require.Len(t, step.Arguments, 2) assert.Equal(t, "up", step.Arguments[0]) diff --git a/pkg/docker-compose/commands/command.go b/pkg/docker-compose/commands/command.go new file mode 100644 index 0000000..9030310 --- /dev/null +++ b/pkg/docker-compose/commands/command.go @@ -0,0 +1,31 @@ +package commands + +import "get.porter.sh/porter/pkg/exec/builder" + +type Command interface { + builder.ExecutableStep + builder.HasOrderedArguments + builder.StepWithOutputs + builder.SuppressesOutput +} + +var _ builder.OutputJsonPath = Output{} +var _ builder.OutputFile = Output{} + +type Output struct { + Name string `yaml:"name"` + JsonPath string `yaml:"jsonPath,omitempty"` + FilePath string `yaml:"path,omitempty"` +} + +func (o Output) GetName() string { + return o.Name +} + +func (o Output) GetJsonPath() string { + return o.JsonPath +} + +func (o Output) GetFilePath() string { + return o.FilePath +} diff --git a/pkg/docker-compose/commands/compose.go b/pkg/docker-compose/commands/compose.go new file mode 100644 index 0000000..7dc4082 --- /dev/null +++ b/pkg/docker-compose/commands/compose.go @@ -0,0 +1,47 @@ +package commands + +import "get.porter.sh/porter/pkg/exec/builder" + +var _ Command = ComposeCommand{} + +type ComposeCommand struct { + Description string `yaml:"description"` + WorkingDirectory string `yaml:"dir,omitempty"` + Arguments []string `yaml:"arguments,omitempty"` + Flags builder.Flags `yaml:"flags,omitempty"` + Outputs []Output `yaml:"outputs,omitempty"` + SuppressOutput bool `yaml:"suppress-output,omitempty"` +} + +func (c ComposeCommand) GetCommand() string { + return "docker-compose" +} + +func (c ComposeCommand) GetArguments() []string { + return c.Arguments +} + +func (c ComposeCommand) GetFlags() builder.Flags { + return c.Flags +} + +func (c ComposeCommand) GetSuffixArguments() []string { + return nil +} + +func (c ComposeCommand) GetOutputs() []builder.Output { + // Go doesn't have generics, nothing to see here... + outputs := make([]builder.Output, len(c.Outputs)) + for i := range c.Outputs { + outputs[i] = c.Outputs[i] + } + return outputs +} + +func (c ComposeCommand) SuppressesOutput() bool { + return c.SuppressOutput +} + +func (c ComposeCommand) GetWorkingDir() string { + return c.WorkingDirectory +} From 1c1b9865169ddba46252cfc4a24f9e7dcc5f5718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Thu, 5 Sep 2024 02:25:34 +0100 Subject: [PATCH 2/5] Add pull command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Eugén Cowie --- pkg/docker-compose/action_test.go | 76 ++++++++++++------- pkg/docker-compose/commands/compose.go | 29 ++++++- pkg/docker-compose/commands/pull.go | 46 +++++++++++ pkg/docker-compose/execute_test.go | 4 + pkg/docker-compose/schema/schema.json | 18 +++++ pkg/docker-compose/schema_test.go | 1 + .../testdata/commands/pull-input.yaml | 15 ++++ 7 files changed, 159 insertions(+), 30 deletions(-) create mode 100644 pkg/docker-compose/commands/pull.go create mode 100644 pkg/docker-compose/testdata/commands/pull-input.yaml diff --git a/pkg/docker-compose/action_test.go b/pkg/docker-compose/action_test.go index f3e815f..1d80184 100644 --- a/pkg/docker-compose/action_test.go +++ b/pkg/docker-compose/action_test.go @@ -13,40 +13,60 @@ 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, + }, + { + "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, + }, + } - 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, commands.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") + }) + } } diff --git a/pkg/docker-compose/commands/compose.go b/pkg/docker-compose/commands/compose.go index 7dc4082..0570630 100644 --- a/pkg/docker-compose/commands/compose.go +++ b/pkg/docker-compose/commands/compose.go @@ -11,6 +11,7 @@ type ComposeCommand struct { Flags builder.Flags `yaml:"flags,omitempty"` Outputs []Output `yaml:"outputs,omitempty"` SuppressOutput bool `yaml:"suppress-output,omitempty"` + Subcommand *PullCommand `yaml:"pull,omitempty"` } func (c ComposeCommand) GetCommand() string { @@ -26,7 +27,24 @@ func (c ComposeCommand) GetFlags() builder.Flags { } func (c ComposeCommand) GetSuffixArguments() []string { - return nil + // Final Command: docker-compose ARGS FLAGS [CMD CMD_ARGS CMD_FLAGS CMD_SUFFIX_ARGS] + // We need to return: CMD CMD_ARGS CMD_FLAGS CMD_SUFFIX_ARGS + if c.Subcommand == nil { + return nil + } + + cmd := (*c.Subcommand).GetCommand() + cmdArgs := (*c.Subcommand).GetArguments() + cmdFlags := (*c.Subcommand).GetFlags() + cmdSuffixArgs := (*c.Subcommand).GetSuffixArguments() + + suffixArgs := make([]string, 0, 1+len(cmdArgs)+(len(cmdFlags)*2)+len(cmdSuffixArgs)) + suffixArgs = append(suffixArgs, cmd) + suffixArgs = append(suffixArgs, cmdArgs...) + suffixArgs = append(suffixArgs, cmdFlags.ToSlice(builder.DefaultFlagDashes)...) + suffixArgs = append(suffixArgs, cmdSuffixArgs...) + + return suffixArgs } func (c ComposeCommand) GetOutputs() []builder.Output { @@ -35,11 +53,18 @@ func (c ComposeCommand) GetOutputs() []builder.Output { for i := range c.Outputs { outputs[i] = c.Outputs[i] } + if c.Subcommand != nil { + outputs = append(outputs, (*c.Subcommand).GetOutputs()...) + } return outputs } func (c ComposeCommand) SuppressesOutput() bool { - return c.SuppressOutput + if c.Subcommand != nil && (*c.Subcommand).SuppressesOutput() { + return true + } else { + return c.SuppressOutput + } } func (c ComposeCommand) GetWorkingDir() string { diff --git a/pkg/docker-compose/commands/pull.go b/pkg/docker-compose/commands/pull.go new file mode 100644 index 0000000..fbf8f07 --- /dev/null +++ b/pkg/docker-compose/commands/pull.go @@ -0,0 +1,46 @@ +package commands + +import "get.porter.sh/porter/pkg/exec/builder" + +var _ Command = PullCommand{} + +type PullCommand struct { + Arguments []string `yaml:"arguments,omitempty"` + Flags builder.Flags `yaml:"flags,omitempty"` + Outputs []Output `yaml:"outputs,omitempty"` + SuppressOutput bool `yaml:"suppress-output,omitempty"` +} + +func (c PullCommand) GetCommand() string { + return "pull" +} + +func (c PullCommand) GetArguments() []string { + // Always use suffix arguments + return nil +} + +func (c PullCommand) GetFlags() builder.Flags { + return c.Flags +} + +func (c PullCommand) GetSuffixArguments() []string { + return c.Arguments +} + +func (c PullCommand) GetOutputs() []builder.Output { + // Go doesn't have generics, nothing to see here... + outputs := make([]builder.Output, len(c.Outputs)) + for i := range c.Outputs { + outputs[i] = c.Outputs[i] + } + return outputs +} + +func (c PullCommand) SuppressesOutput() bool { + return c.SuppressOutput +} + +func (c PullCommand) GetWorkingDir() string { + return "." +} diff --git a/pkg/docker-compose/execute_test.go b/pkg/docker-compose/execute_test.go index 673859a..e684a91 100644 --- a/pkg/docker-compose/execute_test.go +++ b/pkg/docker-compose/execute_test.go @@ -27,6 +27,10 @@ func TestMixin_Execute(t *testing.T) { "install", "testdata/install-input.yaml", "", "docker-compose up --build --scale 2", }, + { + "pull", "testdata/commands/pull-input.yaml", "containerId", + "docker-compose --file test.yml pull --ignore-pull-failures --policy missing serviceA serviceB", + }, } for _, tc := range testcases { diff --git a/pkg/docker-compose/schema/schema.json b/pkg/docker-compose/schema/schema.json index 68dfe4a..6624033 100644 --- a/pkg/docker-compose/schema/schema.json +++ b/pkg/docker-compose/schema/schema.json @@ -102,6 +102,24 @@ "suppress-output": { "description": "Do not print output from the command", "type": "boolean" + }, + "pull": { + "description": "Pull service images", + "type": "object", + "properties": { + "arguments": { + "$ref": "#/definitions/docker-compose/properties/arguments" + }, + "flags": { + "$ref": "#/definitions/docker-compose/properties/flags" + }, + "outputs": { + "$ref": "#/definitions/docker-compose/properties/outputs" + }, + "suppress-output": { + "$ref": "#/definitions/docker-compose/properties/suppress-output" + } + } } }, "additionalProperties": false diff --git a/pkg/docker-compose/schema_test.go b/pkg/docker-compose/schema_test.go index e77df3d..18f23fd 100644 --- a/pkg/docker-compose/schema_test.go +++ b/pkg/docker-compose/schema_test.go @@ -37,6 +37,7 @@ func TestMixin_ValidateSchema(t *testing.T) { {"invoke", "testdata/invoke-input.yaml", ""}, {"uninstall", "testdata/uninstall-input.yaml", ""}, {"invalid property", "testdata/invalid-input.yaml", "Additional property args is not allowed"}, + {"pull", "testdata/commands/pull-input.yaml", ""}, } for _, tc := range testcases { diff --git a/pkg/docker-compose/testdata/commands/pull-input.yaml b/pkg/docker-compose/testdata/commands/pull-input.yaml new file mode 100644 index 0000000..efec695 --- /dev/null +++ b/pkg/docker-compose/testdata/commands/pull-input.yaml @@ -0,0 +1,15 @@ +action: + - docker-compose: + description: Compose Pull + flags: + file: test.yml + pull: + arguments: + - serviceA + - serviceB + flags: + ignore-pull-failures: + policy: missing + outputs: + - name: containerId + jsonPath: $Id From 9d9e7998651f4e641d56e3d3c59755c45c965b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Thu, 5 Sep 2024 02:45:46 +0100 Subject: [PATCH 3/5] Add up and down commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Eugén Cowie --- pkg/docker-compose/action.go | 57 +++++++++++++++++++ pkg/docker-compose/action_test.go | 12 ++++ pkg/docker-compose/commands/compose.go | 2 +- pkg/docker-compose/commands/down.go | 46 +++++++++++++++ pkg/docker-compose/commands/up.go | 46 +++++++++++++++ pkg/docker-compose/execute_test.go | 8 +++ pkg/docker-compose/schema/schema.json | 36 ++++++++++++ pkg/docker-compose/schema_test.go | 2 + .../testdata/commands/down-input.yaml | 15 +++++ .../testdata/commands/up-input.yaml | 15 +++++ 10 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 pkg/docker-compose/commands/down.go create mode 100644 pkg/docker-compose/commands/up.go create mode 100644 pkg/docker-compose/testdata/commands/down-input.yaml create mode 100644 pkg/docker-compose/testdata/commands/up-input.yaml diff --git a/pkg/docker-compose/action.go b/pkg/docker-compose/action.go index 87ae7da..71a9a83 100644 --- a/pkg/docker-compose/action.go +++ b/pkg/docker-compose/action.go @@ -3,6 +3,7 @@ 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{} @@ -52,3 +53,59 @@ func (a Action) GetSteps() []builder.ExecutableStep { type Step struct { commands.ComposeCommand `yaml:"docker-compose"` } + +// 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 + } + + // 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 + } + + // 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 + } + + b, err = yaml.Marshal(value) + if err != nil { + return err + } + err = yaml.Unmarshal(b, cmd) + if err != nil { + return err + } + + s.Subcommand = &cmd + break // There is only 1 command + } + + return nil +} diff --git a/pkg/docker-compose/action_test.go b/pkg/docker-compose/action_test.go index 1d80184..ba4d31d 100644 --- a/pkg/docker-compose/action_test.go +++ b/pkg/docker-compose/action_test.go @@ -33,12 +33,24 @@ func TestMixin_UnmarshalStep(t *testing.T) { "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, + }, } for _, tc := range testcases { diff --git a/pkg/docker-compose/commands/compose.go b/pkg/docker-compose/commands/compose.go index 0570630..ce7d0f8 100644 --- a/pkg/docker-compose/commands/compose.go +++ b/pkg/docker-compose/commands/compose.go @@ -11,7 +11,7 @@ type ComposeCommand struct { Flags builder.Flags `yaml:"flags,omitempty"` Outputs []Output `yaml:"outputs,omitempty"` SuppressOutput bool `yaml:"suppress-output,omitempty"` - Subcommand *PullCommand `yaml:"pull,omitempty"` + Subcommand *Command } func (c ComposeCommand) GetCommand() string { diff --git a/pkg/docker-compose/commands/down.go b/pkg/docker-compose/commands/down.go new file mode 100644 index 0000000..31c8c25 --- /dev/null +++ b/pkg/docker-compose/commands/down.go @@ -0,0 +1,46 @@ +package commands + +import "get.porter.sh/porter/pkg/exec/builder" + +var _ Command = DownCommand{} + +type DownCommand struct { + Arguments []string `yaml:"arguments,omitempty"` + Flags builder.Flags `yaml:"flags,omitempty"` + Outputs []Output `yaml:"outputs,omitempty"` + SuppressOutput bool `yaml:"suppress-output,omitempty"` +} + +func (c DownCommand) GetCommand() string { + return "down" +} + +func (c DownCommand) GetArguments() []string { + // Always use suffix arguments + return nil +} + +func (c DownCommand) GetFlags() builder.Flags { + return c.Flags +} + +func (c DownCommand) GetSuffixArguments() []string { + return c.Arguments +} + +func (c DownCommand) GetOutputs() []builder.Output { + // Go doesn't have generics, nothing to see here... + outputs := make([]builder.Output, len(c.Outputs)) + for i := range c.Outputs { + outputs[i] = c.Outputs[i] + } + return outputs +} + +func (c DownCommand) SuppressesOutput() bool { + return c.SuppressOutput +} + +func (c DownCommand) GetWorkingDir() string { + return "." +} diff --git a/pkg/docker-compose/commands/up.go b/pkg/docker-compose/commands/up.go new file mode 100644 index 0000000..7bd753d --- /dev/null +++ b/pkg/docker-compose/commands/up.go @@ -0,0 +1,46 @@ +package commands + +import "get.porter.sh/porter/pkg/exec/builder" + +var _ Command = UpCommand{} + +type UpCommand struct { + Arguments []string `yaml:"arguments,omitempty"` + Flags builder.Flags `yaml:"flags,omitempty"` + Outputs []Output `yaml:"outputs,omitempty"` + SuppressOutput bool `yaml:"suppress-output,omitempty"` +} + +func (c UpCommand) GetCommand() string { + return "up" +} + +func (c UpCommand) GetArguments() []string { + // Always use suffix arguments + return nil +} + +func (c UpCommand) GetFlags() builder.Flags { + return c.Flags +} + +func (c UpCommand) GetSuffixArguments() []string { + return c.Arguments +} + +func (c UpCommand) GetOutputs() []builder.Output { + // Go doesn't have generics, nothing to see here... + outputs := make([]builder.Output, len(c.Outputs)) + for i := range c.Outputs { + outputs[i] = c.Outputs[i] + } + return outputs +} + +func (c UpCommand) SuppressesOutput() bool { + return c.SuppressOutput +} + +func (c UpCommand) GetWorkingDir() string { + return "." +} diff --git a/pkg/docker-compose/execute_test.go b/pkg/docker-compose/execute_test.go index e684a91..a5974fb 100644 --- a/pkg/docker-compose/execute_test.go +++ b/pkg/docker-compose/execute_test.go @@ -27,10 +27,18 @@ func TestMixin_Execute(t *testing.T) { "install", "testdata/install-input.yaml", "", "docker-compose up --build --scale 2", }, + { + "down", "testdata/commands/down-input.yaml", "containerId", + "docker-compose --file test.yml down --remove-orphans --timeout 25 serviceA serviceB", + }, { "pull", "testdata/commands/pull-input.yaml", "containerId", "docker-compose --file test.yml pull --ignore-pull-failures --policy missing serviceA serviceB", }, + { + "up", "testdata/commands/up-input.yaml", "containerId", + "docker-compose --file test.yml up --detach --timeout 25 serviceA serviceB", + }, } for _, tc := range testcases { diff --git a/pkg/docker-compose/schema/schema.json b/pkg/docker-compose/schema/schema.json index 6624033..58c4129 100644 --- a/pkg/docker-compose/schema/schema.json +++ b/pkg/docker-compose/schema/schema.json @@ -103,6 +103,24 @@ "description": "Do not print output from the command", "type": "boolean" }, + "down": { + "description": "Stop and remove containers, networks", + "type": "object", + "properties": { + "arguments": { + "$ref": "#/definitions/docker-compose/properties/arguments" + }, + "flags": { + "$ref": "#/definitions/docker-compose/properties/flags" + }, + "outputs": { + "$ref": "#/definitions/docker-compose/properties/outputs" + }, + "suppress-output": { + "$ref": "#/definitions/docker-compose/properties/suppress-output" + } + } + }, "pull": { "description": "Pull service images", "type": "object", @@ -120,6 +138,24 @@ "$ref": "#/definitions/docker-compose/properties/suppress-output" } } + }, + "up": { + "description": "Create and start containers", + "type": "object", + "properties": { + "arguments": { + "$ref": "#/definitions/docker-compose/properties/arguments" + }, + "flags": { + "$ref": "#/definitions/docker-compose/properties/flags" + }, + "outputs": { + "$ref": "#/definitions/docker-compose/properties/outputs" + }, + "suppress-output": { + "$ref": "#/definitions/docker-compose/properties/suppress-output" + } + } } }, "additionalProperties": false diff --git a/pkg/docker-compose/schema_test.go b/pkg/docker-compose/schema_test.go index 18f23fd..e55a41d 100644 --- a/pkg/docker-compose/schema_test.go +++ b/pkg/docker-compose/schema_test.go @@ -37,7 +37,9 @@ func TestMixin_ValidateSchema(t *testing.T) { {"invoke", "testdata/invoke-input.yaml", ""}, {"uninstall", "testdata/uninstall-input.yaml", ""}, {"invalid property", "testdata/invalid-input.yaml", "Additional property args is not allowed"}, + {"down", "testdata/commands/down-input.yaml", ""}, {"pull", "testdata/commands/pull-input.yaml", ""}, + {"up", "testdata/commands/up-input.yaml", ""}, } for _, tc := range testcases { diff --git a/pkg/docker-compose/testdata/commands/down-input.yaml b/pkg/docker-compose/testdata/commands/down-input.yaml new file mode 100644 index 0000000..cd9c47a --- /dev/null +++ b/pkg/docker-compose/testdata/commands/down-input.yaml @@ -0,0 +1,15 @@ +action: + - docker-compose: + description: Compose Down + flags: + file: test.yml + down: + arguments: + - serviceA + - serviceB + flags: + remove-orphans: + timeout: 25 + outputs: + - name: containerId + jsonPath: $Id diff --git a/pkg/docker-compose/testdata/commands/up-input.yaml b/pkg/docker-compose/testdata/commands/up-input.yaml new file mode 100644 index 0000000..8d9b226 --- /dev/null +++ b/pkg/docker-compose/testdata/commands/up-input.yaml @@ -0,0 +1,15 @@ +action: + - docker-compose: + description: Compose Up + flags: + file: test.yml + up: + arguments: + - serviceA + - serviceB + flags: + detach: + timeout: 25 + outputs: + - name: containerId + jsonPath: $Id From 705daea38b6b3e98c93b4edff8975a495e2ad591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Thu, 5 Sep 2024 02:57:45 +0100 Subject: [PATCH 4/5] Update readme and examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Eugén Cowie --- README.md | 67 ++++++++++++++++++++++++++----- cmd/docker-compose/main.go | 2 +- examples/compose/porter.yaml | 12 +++--- examples/dockervolume/porter.yaml | 15 ++++--- 4 files changed, 70 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 5c0c0aa..3001d7a 100644 --- a/README.md +++ b/README.md @@ -153,11 +153,7 @@ outputs: path: /root/.kube/config ``` ---- - -## Examples - -### Docker Compose Up +### Example ```yaml docker-compose: @@ -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` + +### 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` + +### 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` + See full bundle examples in the `examples` directory. ## Invocation diff --git a/cmd/docker-compose/main.go b/cmd/docker-compose/main.go index 3043000..0701438 100644 --- a/cmd/docker-compose/main.go +++ b/cmd/docker-compose/main.go @@ -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 diff --git a/examples/compose/porter.yaml b/examples/compose/porter.yaml index f77ca7b..da2126f 100644 --- a/examples/compose/porter.yaml +++ b/examples/compose/porter.yaml @@ -24,9 +24,9 @@ mixins: install: - docker-compose: description: Docker Compose up - arguments: - - up - - -d + up: + flags: + detach: ps: - docker-compose: @@ -37,6 +37,6 @@ ps: uninstall: - docker-compose: description: Docker Compose down - arguments: - - down - - --remove-orphans + down: + flags: + remove-orphans: diff --git a/examples/dockervolume/porter.yaml b/examples/dockervolume/porter.yaml index 9b1809d..391c70e 100644 --- a/examples/dockervolume/porter.yaml +++ b/examples/dockervolume/porter.yaml @@ -28,9 +28,9 @@ install: - docker-compose: description: "Docker Compose Up" - arguments: - - up - - -d + up: + flags: + detach: - exec: description: "Print app logs" @@ -47,9 +47,9 @@ upgrade: - docker-compose: description: "Docker Compose Up" - arguments: - - up - - -d + up: + flags: + detach: - exec: description: "Print app logs" @@ -60,8 +60,7 @@ upgrade: uninstall: - docker-compose: description: "Docker Compose Down" - arguments: - - down + down: - exec: description: "Remove Docker Volume" From a44ca5c09c0b3496441d9e2a0e59cd94730e6a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Fri, 27 Sep 2024 21:34:43 +0100 Subject: [PATCH 5/5] Fix spelling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply suggestions from code review. Co-authored-by: Kim Christensen <2461567+kichristensen@users.noreply.github.com> Signed-off-by: Eugén Cowie --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3001d7a..78e0ceb 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ docker-compose: policy: missing ``` -This is equivilent to: `docker-compose --file compose.yml pull --ignore-pull-failures --policy missing serviceA serviceB` +This is equivalent to: `docker-compose --file compose.yml pull --ignore-pull-failures --policy missing serviceA serviceB` ### Docker Compose Up @@ -211,7 +211,7 @@ docker-compose: timeout: 30 ``` -This is equivilent to: `docker-compose up --detach --timeout 30 serviceA serviceB` +This is equivalent to: `docker-compose up --detach --timeout 30 serviceA serviceB` ### Docker Compose Down @@ -224,7 +224,7 @@ docker-compose: timeout: 30 ``` -This is equivilent to: `docker-compose down --remove-orphans --timeout 30` +This is equivalent to: `docker-compose down --remove-orphans --timeout 30` See full bundle examples in the `examples` directory.