Skip to content

Commit

Permalink
Merge pull request #55 from portainer/revert-54-feat/BE-11416/update-…
Browse files Browse the repository at this point in the history
…libstack

Revert "feat(libstack): remove the dependency on the docker-compose binary BE-11416"
  • Loading branch information
oscarzhou-portainer authored Dec 11, 2024
2 parents d9ff365 + 333a084 commit 256b5f7
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 969 deletions.
4 changes: 0 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ build: binary download-binaries
image: build
docker build -f build/$(PLATFORM)/Dockerfile -t $(image) .

CE=develop
upgrade-libportainer: ## Upgrade the portainer-ce dependency ; use `make upgrade-libportainer CE="branch/ref/on/CE"` to reference any CE branch
GOPROXY=direct go get -v github.com/portainer/portainer@$(CE) && go mod tidy -v

clean:
rm -rf $(dist)
rm -rf .tmp
30 changes: 30 additions & 0 deletions build/download_docker_compose_binary.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -ne 3 ]]; then
echo "Illegal number of parameters" >&2
exit 1
fi

PLATFORM=$1
ARCH=$2
COMPOSE_VERSION=$3


if [[ ${ARCH} == "amd64" ]]; then
ARCH="x86_64"
elif [[ ${ARCH} == "arm" ]]; then
ARCH="armv7"
elif [[ ${ARCH} == "arm64" ]]; then
ARCH="aarch64"
fi


if [[ "$PLATFORM" == "windows" ]]; then
wget -O "dist/docker-compose.exe" "https://github.com/docker/compose/releases/download/$COMPOSE_VERSION/docker-compose-windows-${ARCH}.exe"
chmod +x "dist/docker-compose.exe"
else
wget -O "dist/docker-compose" "https://github.com/docker/compose/releases/download/$COMPOSE_VERSION/docker-compose-${PLATFORM}-${ARCH}"
chmod +x "dist/docker-compose"
fi

134 changes: 66 additions & 68 deletions deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@ import (
"runtime"
"strings"

"github.com/portainer/portainer/pkg/libstack"
"github.com/portainer/portainer/pkg/libstack/compose"

"github.com/docker/cli/cli/config/types"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/portainer/portainer/pkg/libstack"
"github.com/portainer/portainer/pkg/libstack/compose"
"github.com/rs/zerolog/log"
)

Expand All @@ -32,6 +30,11 @@ func (cmd *DeployCommand) Run(cmdCtx *CommandExecutionContext) error {
Bool("skipTLSVerify", cmd.SkipTLSVerify).
Msg("Deploying Compose stack from Git repository")

if err := dockerLogin(cmd.Registry); err != nil {
return fmt.Errorf("an error occured in docker login. Error: %w", err)
}
defer dockerLogout(cmd.Registry)

if cmd.User != "" && cmd.Password != "" {
log.Info().
Str("user", cmd.User).
Expand All @@ -40,6 +43,7 @@ func (cmd *DeployCommand) Run(cmdCtx *CommandExecutionContext) error {

i := strings.LastIndex(cmd.GitRepository, "/")
if i == -1 {

log.Error().
Str("repository", cmd.GitRepository).
Msg("Invalid Git repository URL")
Expand All @@ -53,17 +57,20 @@ func (cmd *DeployCommand) Run(cmdCtx *CommandExecutionContext) error {

mountPath := makeWorkingDir(cmd.Destination, cmd.ProjectName)
clonePath := path.Join(mountPath, repositoryName)
if !cmd.Keep { // Stack create request
if _, err := os.Stat(mountPath); err == nil {
if err := os.RemoveAll(mountPath); err != nil {
if !cmd.Keep { //stack create request
_, err := os.Stat(mountPath)
if err == nil {
err = os.RemoveAll(mountPath)
if err != nil {
log.Error().
Err(err).
Msg("Failed to remove previous directory")
return errDeployComposeFailure
}
}

if err := os.MkdirAll(mountPath, 0755); err != nil {
err = os.MkdirAll(mountPath, 0755)
if err != nil {
log.Error().
Err(err).
Msg("Failed to create destination directory")
Expand All @@ -90,15 +97,22 @@ func (cmd *DeployCommand) Run(cmdCtx *CommandExecutionContext) error {
Int("depth", gitOptions.Depth).
Msg("Cloning git repository")

if _, err := git.PlainCloneContext(cmdCtx.context, clonePath, false, &gitOptions); err != nil {
_, err = git.PlainCloneContext(cmdCtx.context, clonePath, false, &gitOptions)
if err != nil {
log.Error().
Err(err).
Msg("Failed to clone Git repository")
return errDeployComposeFailure
}
}

deployer := compose.NewComposeDeployer()
deployer, err := compose.NewComposeDeployer(BIN_PATH, PORTAINER_DOCKER_CONFIG_PATH)
if err != nil {
log.Error().
Err(err).
Msg("Failed to create Compose deployer")
return errDeployComposeFailure
}

composeFilePaths := make([]string, len(cmd.ComposeRelativeFilePaths))
for i := 0; i < len(cmd.ComposeRelativeFilePaths); i++ {
Expand All @@ -111,35 +125,16 @@ func (cmd *DeployCommand) Run(cmdCtx *CommandExecutionContext) error {
Str("projectName", cmd.ProjectName).
Msg("Deploying Compose stack")

var registries []types.AuthConfig

for _, r := range cmd.Registry {
credentials := strings.Split(r, ":")
if len(credentials) != 3 {
log.Warn().
Str("registry", r).
Msg("Registry is malformed, skipping login")

continue
}

registries = append(registries, types.AuthConfig{
Username: credentials[0],
Password: credentials[1],
ServerAddress: credentials[2],
})
}

if err := deployer.Deploy(cmdCtx.context, composeFilePaths, libstack.DeployOptions{
err = deployer.Deploy(cmdCtx.context, composeFilePaths, libstack.DeployOptions{
Options: libstack.Options{
WorkingDir: clonePath,
ProjectName: cmd.ProjectName,
Env: cmd.Env,
Registries: registries,
},
ForceRecreate: cmd.ForceRecreateStack,
RemoveOrphans: cmd.Prune,
}); err != nil {
})

if err != nil {
log.Error().
Err(err).
Msg("Failed to deploy Compose stack")
Expand All @@ -157,7 +152,8 @@ func (cmd *SwarmDeployCommand) Run(cmdCtx *CommandExecutionContext) error {
Str("destination", cmd.Destination).
Msg("Deploying Swarm stack from a Git repository")

if err := dockerLogin(cmd.Registry); err != nil {
err := dockerLogin(cmd.Registry)
if err != nil {
return fmt.Errorf("an error occured in swarm docker login. Error: %w", err)
}
defer dockerLogout(cmd.Registry)
Expand Down Expand Up @@ -205,17 +201,19 @@ func (cmd *SwarmDeployCommand) Run(cmdCtx *CommandExecutionContext) error {
log.Info().Msg("Set to force update")
}

if !cmd.Keep { // Stack create request
if _, err := os.Stat(mountPath); err == nil {
if err := os.RemoveAll(mountPath); err != nil {
if !cmd.Keep { //stack create request
_, err := os.Stat(mountPath)
if err == nil {
err = os.RemoveAll(mountPath)
if err != nil {
log.Error().
Err(err).
Msg("Failed to remove previous directory")
return errDeployComposeFailure
}
}

if err := os.MkdirAll(mountPath, 0755); err != nil {
err = os.MkdirAll(mountPath, 0755)
if err != nil {
log.Error().
Err(err).
Msg("Failed to create destination directory")
Expand All @@ -230,7 +228,7 @@ func (cmd *SwarmDeployCommand) Run(cmdCtx *CommandExecutionContext) error {
URL: cmd.GitRepository,
ReferenceName: plumbing.ReferenceName(cmd.Reference),
Auth: getAuth(cmd.User, cmd.Password),
Depth: 1,
Depth: 100,
InsecureSkipTLS: cmd.SkipTLSVerify,
Tags: git.NoTags,
}
Expand All @@ -242,16 +240,17 @@ func (cmd *SwarmDeployCommand) Run(cmdCtx *CommandExecutionContext) error {
Int("depth", gitOptions.Depth).
Msg("Cloning git repository")

if _, err = git.PlainCloneContext(cmdCtx.context, clonePath, false, &gitOptions); err != nil {
_, err = git.PlainCloneContext(cmdCtx.context, clonePath, false, &gitOptions)
if err != nil {
log.Error().
Err(err).
Msg("Failed to clone Git repository")

return errDeployComposeFailure
}
}

if err := deploySwarmStack(*cmd, clonePath); err != nil {
err = deploySwarmStack(*cmd, clonePath)
if err != nil {
return err
}

Expand Down Expand Up @@ -289,15 +288,16 @@ func dockerLogin(registries []string) error {
args := make([]string, 0)
args = append(args, "--config", PORTAINER_DOCKER_CONFIG_PATH, "login", "--username", credentials[0], "--password", credentials[1], credentials[2])

if err := runCommandAndCaptureStdErr(command, args, nil, ""); err != nil {
err := runCommandAndCaptureStdErr(command, args, nil, "")
if err != nil {
log.Warn().
Err(err).
Msgf("Docker login %s failed, skipping login", credentials[2])
Msg(fmt.Sprintf("Docker login %s failed. Skip login it.", credentials[2]))

continue
}

log.Info().Msgf("Docker login %s succedeed", credentials[2])
log.Info().
Msg(fmt.Sprintf("Docker login %s successed", credentials[2]))
}

return nil
Expand All @@ -311,31 +311,31 @@ func dockerLogout(registries []string) error {
if len(credentials) != 3 {
log.Warn().
Str("registry", registry).
Msg("Registry is malformed, skipping logout")
Msg("registry is malformed. Skip logout it.")

continue
}

args := make([]string, 0)
args = append(args, "--config", PORTAINER_DOCKER_CONFIG_PATH, "logout", credentials[2])

if err := runCommandAndCaptureStdErr(command, args, nil, ""); err != nil {
err := runCommandAndCaptureStdErr(command, args, nil, "")
if err != nil {
log.Warn().
Err(err).
Msgf("Docker logout %s failed, skipping logout", credentials[2])
Msg(fmt.Sprintf("Docker logout %s failed. Skip logout it.", credentials[2]))

continue
}

log.Info().Msgf("Docker logout %s succedeed", credentials[2])
log.Info().
Msg(fmt.Sprintf("Docker logout %s successed", credentials[2]))
}

return nil
}

func runCommandAndCaptureStdErr(command string, args []string, env []string, workingDir string) error {
var stderr bytes.Buffer

cmd := exec.Command(command, args...)
cmd.Stderr = &stderr
cmd.Dir = workingDir
Expand All @@ -345,10 +345,10 @@ func runCommandAndCaptureStdErr(command string, args []string, env []string, wor
cmd.Env = append(cmd.Env, env...)
}

if err := cmd.Run(); err != nil {
err := cmd.Run()
if err != nil {
return errors.New(stderr.String())
}

return nil
}

Expand All @@ -357,31 +357,29 @@ func runCommand(command string, args []string) (string, error) {
stderr bytes.Buffer
stdout bytes.Buffer
)

cmd := exec.Command(command, args...)
cmd.Stderr = &stderr
cmd.Stdout = &stdout

if err := cmd.Run(); err != nil {
err := cmd.Run()
if err != nil {
return stdout.String(), errors.New(stderr.String())
}

return stdout.String(), nil
}

func getAuth(username, password string) *http.BasicAuth {
if password == "" {
return nil
}

if username == "" {
username = "token"
}

return &http.BasicAuth{
Username: username,
Password: password,
if password != "" {
if username == "" {
username = "token"
}
return &http.BasicAuth{
Username: username,
Password: password,
}
}
return nil
}

func makeWorkingDir(target, stackName string) string {
Expand Down
Loading

0 comments on commit 256b5f7

Please sign in to comment.