Skip to content
This repository was archived by the owner on Jun 26, 2023. It is now read-only.

Commit a69a4eb

Browse files
authored
Merge pull request #26 from portainer/fix/EE-3631/dont-install-docker-compose
fix: don't install the docker-compose binary [EE-3631]
2 parents e3cf664 + 2b375bd commit a69a4eb

File tree

5 files changed

+87
-127
lines changed

5 files changed

+87
-127
lines changed

compose/compose_test.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package compose_test
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"log"
78
"os"
@@ -13,7 +14,15 @@ import (
1314
"github.com/portainer/docker-compose-wrapper/compose"
1415
)
1516

17+
func checkPrerequisites(t *testing.T) {
18+
if _, err := os.Stat("docker-compose"); errors.Is(err, os.ErrNotExist) {
19+
t.Fatal("docker-compose binary not found, please run download.sh and re-run this suite")
20+
}
21+
}
22+
1623
func Test_UpAndDown(t *testing.T) {
24+
checkPrerequisites(t)
25+
1726
deployer, _ := compose.NewComposeDeployer("", "")
1827

1928
const composeFileContent = `
@@ -49,7 +58,7 @@ func Test_UpAndDown(t *testing.T) {
4958

5059
ctx := context.Background()
5160

52-
err = deployer.Deploy(ctx, "", "", "test1", []string{filePathOriginal, filePathOverride}, "", false)
61+
err = deployer.Deploy(ctx, "", "", "", []string{filePathOriginal, filePathOverride}, "", false)
5362
if err != nil {
5463
t.Fatal(err)
5564
}
@@ -58,7 +67,7 @@ func Test_UpAndDown(t *testing.T) {
5867
t.Fatal("container should exist")
5968
}
6069

61-
err = deployer.Remove(ctx, "", "", "test1", []string{filePathOriginal, filePathOverride})
70+
err = deployer.Remove(ctx, "", "", "", []string{filePathOriginal, filePathOverride}, "")
6271
if err != nil {
6372
t.Fatal(err)
6473
}

compose/download.sh

Lines changed: 18 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,25 @@
1-
#!/bin/bash
1+
#!/usr/bin/env bash
22
set -euo pipefail
3-
IFS=$'\n\t'
43

5-
PLATFORM=$1
6-
ARCH=$2
7-
DOCKER_COMPOSE_VERSION=$3
4+
PLATFORM=$(go env GOOS)
5+
ARCH=$(go env GOARCH)
6+
COMPOSE_VERSION=v2.5.1
87

9-
function download_binary() {
10-
local PLATFORM=$1
11-
local ARCH=$2
12-
local BINARY_VERSION=$3
13-
14-
if [ "${PLATFORM}" == 'linux' ] && [ "${ARCH}" == 'amd64' ]; then
15-
wget -O "dist/docker-compose" "https://github.com/portainer/docker-compose-linux-amd64-static-binary/releases/download/${BINARY_VERSION}/docker-compose"
16-
chmod +x "dist/docker-compose"
17-
return
18-
fi
19-
20-
if [ "${PLATFORM}" == 'mac' ]; then
21-
wget -O "dist/docker-compose" "https://github.com/docker/compose/releases/download/${BINARY_VERSION}/docker-compose-Darwin-x86_64"
22-
chmod +x "dist/docker-compose"
23-
return
24-
fi
25-
26-
if [ "${PLATFORM}" == 'win' ]; then
27-
wget -O "dist/docker-compose.exe" "https://github.com/docker/compose/releases/download/${BINARY_VERSION}/docker-compose-Windows-x86_64.exe"
28-
chmod +x "dist/docker-compose.exe"
29-
return
30-
fi
31-
}
328

33-
function download_plugin() {
34-
local PLATFORM=$1
35-
local ARCH=$2
36-
local PLUGIN_VERSION=$3
37-
38-
if [ "${PLATFORM}" == 'mac' ]; then
39-
PLATFORM="darwin"
40-
fi
41-
42-
FILENAME="docker-compose-${PLATFORM}-${ARCH}"
43-
TARGET_FILENAME="docker-compose.plugin"
44-
if [[ "$PLATFORM" == "windows" ]]; then
45-
FILENAME="$FILENAME.exe"
46-
TARGET_FILENAME="$TARGET_FILENAME.exe"
47-
fi
48-
49-
wget -O "dist/$TARGET_FILENAME" "https://github.com/docker/compose-cli/releases/download/v$PLUGIN_VERSION/$FILENAME"
50-
chmod +x "dist/$TARGET_FILENAME"
51-
return 0
52-
}
9+
if [[ ${ARCH} == "amd64" ]]; then
10+
ARCH="x86_64"
11+
elif [[ ${ARCH} == "arm" ]]; then
12+
ARCH="armv7"
13+
elif [[ ${ARCH} == "arm64" ]]; then
14+
ARCH="aarch64"
15+
fi
16+
5317

54-
if [ "${PLATFORM}" == 'linux' ] && [ "${ARCH}" != 'amd64' ]; then
55-
download_plugin "$PLATFORM" "$ARCH" "$DOCKER_COMPOSE_VERSION"
18+
if [[ "$PLATFORM" == "windows" ]]; then
19+
wget -O "docker-compose.exe" "https://github.com/docker/compose/releases/download/$COMPOSE_VERSION/docker-compose-windows-${ARCH}.exe"
20+
chmod +x "docker-compose.exe"
21+
else
22+
wget -O "docker-compose" "https://github.com/docker/compose/releases/download/$COMPOSE_VERSION/docker-compose-${PLATFORM}-${ARCH}"
23+
chmod +x "docker-compose"
5624
fi
5725

58-
download_binary "$PLATFORM" "$ARCH" "$DOCKER_COMPOSE_VERSION"

compose/internal/composeplugin/composeplugin.go

Lines changed: 35 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ import (
66
"log"
77
"os"
88
"os/exec"
9-
"path"
109
"strings"
1110

1211
"github.com/pkg/errors"
1312
libstack "github.com/portainer/docker-compose-wrapper"
14-
liberrors "github.com/portainer/docker-compose-wrapper/compose/errors"
1513
"github.com/portainer/docker-compose-wrapper/compose/internal/utils"
1614
)
1715

@@ -27,50 +25,23 @@ type PluginWrapper struct {
2725

2826
// NewPluginWrapper initializes a new ComposeWrapper service with local docker-compose binary.
2927
func NewPluginWrapper(binaryPath, configPath string) (libstack.Deployer, error) {
30-
if !utils.IsBinaryPresent(utils.ProgramPath(binaryPath, "docker")) {
31-
return nil, liberrors.ErrBinaryNotFound
32-
}
33-
34-
if configPath == "" {
35-
homePath, err := os.UserHomeDir()
36-
if err != nil {
37-
return nil, errors.WithMessage(err, "failed fetching user home directory")
38-
}
39-
configPath = path.Join(homePath, ".docker")
40-
}
41-
42-
dockerPluginsPath := path.Join(configPath, "cli-plugins")
43-
pluginPath := utils.ProgramPath(binaryPath, "docker-compose.plugin")
44-
45-
if utils.IsBinaryPresent(pluginPath) {
46-
if !utils.IsBinaryPresent(utils.ProgramPath(dockerPluginsPath, "docker-compose")) {
47-
err := os.MkdirAll(dockerPluginsPath, 0755)
48-
if err != nil {
49-
return nil, errors.WithMessage(err, "failed creating plugins path")
50-
}
51-
}
52-
53-
err := utils.Move(pluginPath, utils.ProgramPath(dockerPluginsPath, "docker-compose"))
54-
if err != nil {
55-
return nil, err
56-
}
57-
} else if !utils.IsBinaryPresent(utils.ProgramPath(dockerPluginsPath, "docker-compose")) {
28+
if !utils.IsBinaryPresent(utils.ProgramPath(binaryPath, "docker-compose")) {
5829
return nil, MissingDockerComposePluginErr
5930
}
6031

6132
return &PluginWrapper{binaryPath: binaryPath, configPath: configPath}, nil
6233
}
6334

6435
// Up create and start containers
65-
func (wrapper *PluginWrapper) Deploy(ctx context.Context, workingDir, host, projectName string, filePaths []string, envFilePath string, forceRereate bool) error {
66-
output, err := wrapper.command(newUpCommand(filePaths, forceRereate), workingDir, host, projectName, envFilePath)
36+
func (wrapper *PluginWrapper) Deploy(ctx context.Context, workingDir, host, projectName string, filePaths []string, envFilePath string, forceRecreate bool) error {
37+
output, err := wrapper.command(newUpCommand(filePaths, forceRecreate), workingDir, host, projectName, envFilePath)
6738
if len(output) != 0 {
6839
if err != nil {
6940
return err
7041
}
7142

72-
log.Printf("[INFO] [compose,internal,composeplugin] [message: Stack deployment successful]")
73-
log.Printf("[DEBUG] [compose,internal,composeplugin] [output: %s]", output)
43+
log.Printf("[INFO] [docker compose] [message: Stack deployment successful]")
44+
log.Printf("[DEBUG] [docker compose] [output: %s]", output)
7445
}
7546

7647
return err
@@ -84,8 +55,8 @@ func (wrapper *PluginWrapper) Remove(ctx context.Context, workingDir, host, proj
8455
return err
8556
}
8657

87-
log.Printf("[INFO] [compose,internal,composeplugin] [message: Stack removal successful]")
88-
log.Printf("[DEBUG] [compose,internal,composeplugin] [output: %s]", output)
58+
log.Printf("[INFO] [docker compose] [message: Stack removal successful]")
59+
log.Printf("[DEBUG] [docker compose] [output: %s]", output)
8960
}
9061

9162
return err
@@ -99,16 +70,16 @@ func (wrapper *PluginWrapper) Pull(ctx context.Context, workingDir, host, projec
9970
return err
10071
}
10172

102-
log.Printf("[INFO] [compose,internal,composeplugin] [message: Stack pull successful]")
103-
log.Printf("[DEBUG] [compose,internal,composeplugin] [output: %s]", output)
73+
log.Printf("[INFO] [docker compose] [message: Stack pull successful]")
74+
log.Printf("[DEBUG] [docker compose] [output: %s]", output)
10475
}
10576

10677
return err
10778
}
10879

109-
// Command exectue a docker-compose commanåd
110-
func (wrapper *PluginWrapper) command(command composeCommand, workingDir, url, projectName, envFilePath string) ([]byte, error) {
111-
program := utils.ProgramPath(wrapper.binaryPath, "docker")
80+
// Command execute a docker-compose command
81+
func (wrapper *PluginWrapper) command(command composeCommand, workingDir, host, projectName, envFilePath string) ([]byte, error) {
82+
program := utils.ProgramPath(wrapper.binaryPath, "docker-compose")
11283

11384
if projectName != "" {
11485
command.WithProjectName(projectName)
@@ -118,24 +89,25 @@ func (wrapper *PluginWrapper) command(command composeCommand, workingDir, url, p
11889
command.WithEnvFilePath(envFilePath)
11990
}
12091

121-
var stderr bytes.Buffer
122-
123-
args := []string{}
124-
125-
if wrapper.configPath != "" {
126-
args = append(args, "--config", wrapper.configPath)
92+
if host != "" {
93+
command.WithHost(host)
12794
}
12895

129-
if url != "" {
130-
args = append(args, "-H", url)
131-
}
96+
var stderr bytes.Buffer
13297

133-
args = append(args, "compose")
98+
args := []string{}
13499
args = append(args, command.ToArgs()...)
135100

136101
cmd := exec.Command(program, args...)
137102
cmd.Dir = workingDir
138103

104+
if wrapper.configPath != "" {
105+
if wrapper.configPath != "" {
106+
cmd.Env = os.Environ()
107+
cmd.Env = append(cmd.Env, "DOCKER_CONFIG="+wrapper.configPath)
108+
}
109+
}
110+
139111
cmd.Stderr = &stderr
140112

141113
output, err := cmd.Output()
@@ -147,19 +119,19 @@ func (wrapper *PluginWrapper) command(command composeCommand, workingDir, url, p
147119
}
148120

149121
type composeCommand struct {
150-
command []string
151-
args []string
122+
globalArgs []string // docker-compose global arguments: --host host -f file.yaml
123+
subCommandAndArgs []string // docker-compose subcommand: up, down folllowed by subcommand arguments
152124
}
153125

154126
func newCommand(command []string, filePaths []string) composeCommand {
155-
var args []string
127+
args := []string{}
156128
for _, path := range filePaths {
157129
args = append(args, "-f")
158130
args = append(args, strings.TrimSpace(path))
159131
}
160132
return composeCommand{
161-
args: args,
162-
command: command,
133+
globalArgs: args,
134+
subCommandAndArgs: command,
163135
}
164136
}
165137

@@ -179,22 +151,20 @@ func newPullCommand(filePaths []string) composeCommand {
179151
return newCommand([]string{"pull"}, filePaths)
180152
}
181153

182-
func (command *composeCommand) WithConfigPath(configPath string) {
183-
command.args = append(command.args, "--config", configPath)
154+
func (command *composeCommand) WithHost(host string) {
155+
// prepend compatibility flags such as this one as they must appear before the
156+
// regular global args otherwise docker-compose will throw an error
157+
command.globalArgs = append([]string{"--host", host}, command.globalArgs...)
184158
}
185159

186160
func (command *composeCommand) WithProjectName(projectName string) {
187-
command.args = append(command.args, "-p", projectName)
161+
command.globalArgs = append(command.globalArgs, "--project-name", projectName)
188162
}
189163

190164
func (command *composeCommand) WithEnvFilePath(envFilePath string) {
191-
command.args = append(command.args, "--env-file", envFilePath)
192-
}
193-
194-
func (command *composeCommand) WithURL(url string) {
195-
command.args = append(command.args, "-H", url)
165+
command.globalArgs = append(command.globalArgs, "--env-file", envFilePath)
196166
}
197167

198168
func (command *composeCommand) ToArgs() []string {
199-
return append(command.args, command.command...)
169+
return append(command.globalArgs, command.subCommandAndArgs...)
200170
}

compose/internal/composeplugin/composeplugin_test.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package composeplugin
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"log"
78
"os"
@@ -14,6 +15,12 @@ import (
1415
libstack "github.com/portainer/docker-compose-wrapper"
1516
)
1617

18+
func checkPrerequisites(t *testing.T) {
19+
if _, err := os.Stat("docker-compose"); errors.Is(err, os.ErrNotExist) {
20+
t.Fatal("docker-compose binary not found, please run download.sh and re-run this test suite")
21+
}
22+
}
23+
1724
func setup(t *testing.T) libstack.Deployer {
1825
w, err := NewPluginWrapper("", "")
1926
if err != nil {
@@ -24,30 +31,37 @@ func setup(t *testing.T) libstack.Deployer {
2431
}
2532

2633
func Test_NewCommand_SingleFilePath(t *testing.T) {
34+
checkPrerequisites(t)
35+
2736
cmd := newCommand([]string{"up", "-d"}, []string{"docker-compose.yml"})
2837
expected := []string{"-f", "docker-compose.yml"}
29-
if !reflect.DeepEqual(cmd.args, expected) {
30-
t.Errorf("wrong output args, want: %v, got: %v", expected, cmd.args)
38+
if !reflect.DeepEqual(cmd.globalArgs, expected) {
39+
t.Errorf("wrong output args, want: %v, got: %v", expected, cmd.globalArgs)
3140
}
3241
}
3342

3443
func Test_NewCommand_MultiFilePaths(t *testing.T) {
44+
checkPrerequisites(t)
45+
3546
cmd := newCommand([]string{"up", "-d"}, []string{"docker-compose.yml", "docker-compose-override.yml"})
3647
expected := []string{"-f", "docker-compose.yml", "-f", "docker-compose-override.yml"}
37-
if !reflect.DeepEqual(cmd.args, expected) {
38-
t.Errorf("wrong output args, want: %v, got: %v", expected, cmd.args)
48+
if !reflect.DeepEqual(cmd.globalArgs, expected) {
49+
t.Errorf("wrong output args, want: %v, got: %v", expected, cmd.globalArgs)
3950
}
4051
}
4152

4253
func Test_NewCommand_MultiFilePaths_WithSpaces(t *testing.T) {
54+
checkPrerequisites(t)
55+
4356
cmd := newCommand([]string{"up", "-d"}, []string{" docker-compose.yml", "docker-compose-override.yml "})
4457
expected := []string{"-f", "docker-compose.yml", "-f", "docker-compose-override.yml"}
45-
if !reflect.DeepEqual(cmd.args, expected) {
46-
t.Errorf("wrong output args, want: %v, got: %v", expected, cmd.args)
58+
if !reflect.DeepEqual(cmd.globalArgs, expected) {
59+
t.Errorf("wrong output args, want: %v, got: %v", expected, cmd.globalArgs)
4760
}
4861
}
4962

5063
func Test_UpAndDown(t *testing.T) {
64+
checkPrerequisites(t)
5165

5266
const composeFileContent = `version: "3.9"
5367
services:
@@ -79,7 +93,7 @@ services:
7993
}
8094

8195
ctx := context.Background()
82-
err = w.Deploy(ctx, "", "", "test1", []string{filePathOriginal, filePathOverride}, "", false)
96+
err = w.Deploy(ctx, "", "", "", []string{filePathOriginal, filePathOverride}, "", false)
8397
if err != nil {
8498
t.Fatal(err)
8599
}
@@ -88,7 +102,7 @@ services:
88102
t.Fatal("container should exist")
89103
}
90104

91-
err = w.Remove(ctx, "", "", "test1", []string{filePathOriginal, filePathOverride})
105+
err = w.Remove(ctx, "", "", "", []string{filePathOriginal, filePathOverride}, "")
92106
if err != nil {
93107
t.Fatal(err)
94108
}

0 commit comments

Comments
 (0)