mirror of https://github.com/portainer/portainer
fix(stack): unable to delete invalid stack [EE-5753] (#11813)
parent
9c70a43ac3
commit
4a7f96caf6
|
@ -76,15 +76,9 @@ func (manager *ComposeStackManager) Down(ctx context.Context, stack *portainer.S
|
||||||
defer proxy.Close()
|
defer proxy.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
envFilePath, err := createEnvFile(stack)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to create env file")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = manager.deployer.Remove(ctx, stack.Name, nil, libstack.Options{
|
err = manager.deployer.Remove(ctx, stack.Name, nil, libstack.Options{
|
||||||
WorkingDir: stack.ProjectPath,
|
WorkingDir: "",
|
||||||
EnvFilePath: envFilePath,
|
Host: url,
|
||||||
Host: url,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return errors.Wrap(err, "failed to remove a stack")
|
return errors.Wrap(err, "failed to remove a stack")
|
||||||
|
@ -148,28 +142,46 @@ func createEnvFile(stack *portainer.Stack) (string, error) {
|
||||||
}
|
}
|
||||||
defer envfile.Close()
|
defer envfile.Close()
|
||||||
|
|
||||||
copyDefaultEnvFile(stack, envfile)
|
// Copy from default .env file
|
||||||
|
defaultEnvPath := path.Join(stack.ProjectPath, path.Dir(stack.EntryPoint), ".env")
|
||||||
|
if err = copyDefaultEnvFile(envfile, defaultEnvPath); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
for _, v := range stack.Env {
|
// Copy from stack env vars
|
||||||
envfile.WriteString(fmt.Sprintf("%s=%s\n", v.Name, v.Value))
|
if err = copyConfigEnvVars(envfile, stack.Env); err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return "stack.env", nil
|
return "stack.env", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyDefaultEnvFile copies the default .env file if it exists to the provided writer
|
// copyDefaultEnvFile copies the default .env file if it exists to the provided writer
|
||||||
func copyDefaultEnvFile(stack *portainer.Stack, w io.Writer) {
|
func copyDefaultEnvFile(w io.Writer, defaultEnvFilePath string) error {
|
||||||
defaultEnvFile, err := os.Open(path.Join(path.Join(stack.ProjectPath, path.Dir(stack.EntryPoint)), ".env"))
|
defaultEnvFile, err := os.Open(defaultEnvFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If cannot open a default file, then don't need to copy it.
|
// If cannot open a default file, then don't need to copy it.
|
||||||
// We could as well stat it and check if it exists, but this is more efficient.
|
// We could as well stat it and check if it exists, but this is more efficient.
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defer defaultEnvFile.Close()
|
defer defaultEnvFile.Close()
|
||||||
|
|
||||||
if _, err = io.Copy(w, defaultEnvFile); err == nil {
|
if _, err = io.Copy(w, defaultEnvFile); err == nil {
|
||||||
io.WriteString(w, "\n")
|
if _, err = fmt.Fprintf(w, "\n"); err != nil {
|
||||||
|
return fmt.Errorf("failed to copy default env file: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
// If couldn't copy the .env file, then ignore the error and try to continue
|
// If couldn't copy the .env file, then ignore the error and try to continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copyConfigEnvVars write the environment variables from stack configuration to the writer
|
||||||
|
func copyConfigEnvVars(w io.Writer, envs []portainer.Pair) error {
|
||||||
|
for _, v := range envs {
|
||||||
|
if _, err := fmt.Fprintf(w, "%s=%s\n", v.Name, v.Value); err != nil {
|
||||||
|
return fmt.Errorf("failed to copy config env vars: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ func Test_createEnvFile(t *testing.T) {
|
||||||
name: "should not add env file option if stack doesn't have env variables",
|
name: "should not add env file option if stack doesn't have env variables",
|
||||||
stack: &portainer.Stack{
|
stack: &portainer.Stack{
|
||||||
ProjectPath: dir,
|
ProjectPath: dir,
|
||||||
|
Env: nil,
|
||||||
},
|
},
|
||||||
expected: "",
|
expected: "",
|
||||||
},
|
},
|
||||||
|
|
|
@ -157,7 +157,10 @@ func runCommandAndCaptureStdErr(command string, args []string, env []string, wor
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
cmd := exec.Command(command, args...)
|
cmd := exec.Command(command, args...)
|
||||||
cmd.Stderr = &stderr
|
cmd.Stderr = &stderr
|
||||||
cmd.Dir = workingDir
|
|
||||||
|
if workingDir != "" {
|
||||||
|
cmd.Dir = workingDir
|
||||||
|
}
|
||||||
|
|
||||||
if env != nil {
|
if env != nil {
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = os.Environ()
|
||||||
|
|
|
@ -245,7 +245,16 @@ func (handler *Handler) deleteStack(userID portainer.UserID, stack *portainer.St
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := handler.KubernetesDeployer.Remove(userID, endpoint, manifestFiles, stack.Namespace)
|
out, err := handler.KubernetesDeployer.Remove(userID, endpoint, manifestFiles, stack.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
for _, manifest := range manifestFiles {
|
||||||
|
if exists, fileExistsErr := filesystem.FileExists(manifest); fileExistsErr != nil || !exists {
|
||||||
|
// If removal has failed and one of the manifest files is missing,
|
||||||
|
// we can consider this stack as removed
|
||||||
|
log.Warn().Err(fileExistsErr).Msgf("failed to find manifest %s, but stack deletion will continue", manifest)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return errors.WithMessagef(err, "failed to remove kubernetes resources: %q", out)
|
return errors.WithMessagef(err, "failed to remove kubernetes resources: %q", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,12 @@ func (wrapper *PluginWrapper) command(command composeCommand, options libstack.O
|
||||||
args = append(args, command.ToArgs()...)
|
args = append(args, command.ToArgs()...)
|
||||||
|
|
||||||
cmd := exec.Command(program, args...)
|
cmd := exec.Command(program, args...)
|
||||||
cmd.Dir = options.WorkingDir
|
if options.WorkingDir != "" {
|
||||||
|
// Specify an non-exist working directory will cause the failure
|
||||||
|
// of the "docker-compose down" command even if the project name
|
||||||
|
// is correct.
|
||||||
|
cmd.Dir = options.WorkingDir
|
||||||
|
}
|
||||||
|
|
||||||
if wrapper.configPath != "" || len(options.Env) > 0 {
|
if wrapper.configPath != "" || len(options.Env) > 0 {
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = os.Environ()
|
||||||
|
|
Loading…
Reference in New Issue