Files
dokku/plugins/apps/functions.go
Jose Diaz-Gonzalez 5dc2f20432 fix: remove all containers and images by label on app destroy
Previously, we would filter containers and images by name, which could be fail if the app was renamed or no longer was tagged "correctly" due to a rebuild. Now, we filter by label, ensuring an app is completely cleaned up on deletion.

Also:

- Move docker image cleanup from apps plugin to builder plugin: This isn't where it belongs - images should be cleaned up in a builder plugin (or the builders plugin) and not in the apps plugin, which has nothing to do with docker images anyways.
- Remove the code from the scheduler plugin, as that will only do anything if the scheduler is enabled for the app.
- Bypass image removal if there are no images to remove
- Always delete app containers: The herokuish builder creates intermediate build containers - something that should be refactored - and ideally this gets run in that builder plugin, but keeping the logic here ensures we don't have code duplication. This is otherwise safe as it will be a no-op if there are no containers to clean up.
2023-08-05 10:58:57 -04:00

114 lines
3.0 KiB
Go

package apps
import (
"errors"
"fmt"
"os"
"strings"
"time"
"github.com/dokku/dokku/plugins/common"
)
// checks if an app exists
func appExists(appName string) error {
return common.VerifyAppName(appName)
}
// checks if an app is locked
func appIsLocked(appName string) bool {
lockfilePath := fmt.Sprintf("%v/.deploy.lock", common.AppRoot(appName))
_, err := os.Stat(lockfilePath)
return !os.IsNotExist(err)
}
// verifies app name and creates an app
func createApp(appName string) error {
if err := common.IsValidAppName(appName); err != nil {
return err
}
if err := appExists(appName); err == nil {
return errors.New("Name is already taken")
}
common.LogInfo1Quiet(fmt.Sprintf("Creating %s...", appName))
os.MkdirAll(common.AppRoot(appName), 0755)
if err := common.PropertyWrite("apps", appName, "created-at", fmt.Sprintf("%d", time.Now().Unix())); err != nil {
return err
}
if err := common.PlugnTrigger("post-create", []string{appName}...); err != nil {
return err
}
return nil
}
// destroys an app
func destroyApp(appName string) error {
if os.Getenv("DOKKU_APPS_FORCE_DELETE") != "1" {
if err := common.AskForDestructiveConfirmation(appName, "app"); err != nil {
return err
}
}
common.LogInfo1(fmt.Sprintf("Destroying %s (including all add-ons)", appName))
imageTag, _ := common.GetRunningImageTag(appName, "")
if err := common.PlugnTrigger("pre-delete", []string{appName, imageTag}...); err != nil {
return err
}
scheduler := common.GetAppScheduler(appName)
removeContainers := "true"
if err := common.PlugnTrigger("scheduler-stop", []string{scheduler, appName, removeContainers}...); err != nil {
return err
}
if err := common.PlugnTrigger("scheduler-post-delete", []string{scheduler, appName, imageTag}...); err != nil {
return err
}
if err := common.PlugnTrigger("post-delete", []string{appName, imageTag}...); err != nil {
return err
}
forceCleanup := true
common.DockerCleanup(appName, forceCleanup)
common.LogInfo1("Retiring old containers and images")
if err := common.PlugnTrigger("scheduler-retire", []string{scheduler, appName}...); err != nil {
return err
}
// remove contents for apps that are symlinks to other folders
if err := os.RemoveAll(fmt.Sprintf("%v/", common.AppRoot(appName))); err != nil {
common.LogWarn(err.Error())
}
// then remove the folder and/or the symlink
if err := os.RemoveAll(common.AppRoot(appName)); err != nil {
common.LogWarn(err.Error())
}
return nil
}
// creates an app if allowed
func maybeCreateApp(appName string) error {
if err := appExists(appName); err == nil {
return nil
}
b, _ := common.PlugnTriggerOutput("config-get-global", []string{"DOKKU_DISABLE_APP_AUTOCREATION"}...)
disableAutocreate := strings.TrimSpace(string(b[:]))
if disableAutocreate == "true" {
common.LogWarn("App auto-creation disabled.")
return fmt.Errorf("Re-enable app auto-creation or create an app with 'dokku apps:create %s'", appName)
}
return common.SuppressOutput(func() error {
return createApp(appName)
})
}