diff --git a/cmd/composectl/cmd/update/complete.go b/cmd/composectl/cmd/update/complete.go index b96b0c1..5eb757f 100644 --- a/cmd/composectl/cmd/update/complete.go +++ b/cmd/composectl/cmd/update/complete.go @@ -1,6 +1,7 @@ package updatectl import ( + "github.com/foundriesio/composeapp/pkg/compose" v1 "github.com/foundriesio/composeapp/pkg/compose/v1" "github.com/foundriesio/composeapp/pkg/update" "github.com/spf13/cobra" @@ -8,7 +9,8 @@ import ( type ( completeOptions struct { - Prune bool + Prune bool + PruneAllImages bool } ) @@ -25,7 +27,10 @@ This will mark the update as successful after checking whether the update apps a opts := completeOptions{} completeCmd.Flags().BoolVar(&opts.Prune, "prune", false, - "Uninstall and remove the apps that are not included in the update.") + "Uninstall and remove the apps that are not included in the update and images referenced by those apps") + completeCmd.Flags().BoolVar(&opts.PruneAllImages, "prune-all-images", false, + "Remove all unused images, even those that are not associated with the apps being uninstalled and pruned by the update complete process."+ + " This option is only effective when --prune is also specified.") completeCmd.Run = func(cmd *cobra.Command, args []string) { completeUpdateCmd(cmd, args, &opts) } @@ -42,7 +47,11 @@ func completeUpdateCmd(cmd *cobra.Command, args []string, opts *completeOptions) var options []update.CompleteOpt if opts.Prune { - options = append(options, update.CompleteWithPruning()) + imagePruneType := compose.PruneTypeOnlyAppImages + if opts.PruneAllImages { + imagePruneType = compose.PruneTypeAllUnusedImages + } + options = append(options, update.CompleteWithPruning(imagePruneType)) } err = updateCtl.Complete(cmd.Context(), options...) ExitIfNotNil(err) diff --git a/pkg/compose/uninstall.go b/pkg/compose/uninstall.go index cbd40dd..d550a06 100644 --- a/pkg/compose/uninstall.go +++ b/pkg/compose/uninstall.go @@ -41,47 +41,52 @@ func WithImagePruning(pruneType ...PruneType) UninstallOpt { } func UninstallApps(ctx context.Context, cfg *Config, appRefs []string, options ...UninstallOpt) error { - if len(appRefs) == 0 { - return nil - } opts := &UninstallOpts{} for _, o := range options { o(opts) } - status, err := CheckAppsStatus(ctx, cfg, appRefs) - if err != nil { - return err - } - if status.AreRunning() { - return ErrUninstallRunningApps + if len(appRefs) == 0 && (!opts.Prune || opts.PruneType != PruneTypeAllUnusedImages) { + return nil } - store, err := cfg.AppStoreFactory() - if err != nil { - return err - } - appInStoreRefs, err := store.ListApps(ctx) - if err != nil { - return err - } - appsInStore := make(map[string]int) - for _, ref := range appInStoreRefs { - appsInStore[ref.Name] += 1 - } - for _, app := range status.Apps { - if appsInStore[app.Name()] > 1 { - // Multiple versions of the same app exist in the store. - // If the version being removed is not installed, another version may still be - // installed and using the same compose directory. In that case, keep the app - // compose directory; otherwise we could remove compose files needed by the - // other installed version. - if _, isNotInstalled := status.NotInstalledCompose[app.Ref().Digest]; isNotInstalled { - continue - } + var err error + var status *AppsStatus + if len(appRefs) > 0 { + status, err = CheckAppsStatus(ctx, cfg, appRefs) + if err != nil { + return err } - if err = os.RemoveAll(cfg.GetAppComposeDir(app.Name())); err != nil { + if status.AreRunning() { + return ErrUninstallRunningApps + } + + store, err := cfg.AppStoreFactory() + if err != nil { return err } + appInStoreRefs, err := store.ListApps(ctx) + if err != nil { + return err + } + appsInStore := make(map[string]int) + for _, ref := range appInStoreRefs { + appsInStore[ref.Name] += 1 + } + for _, app := range status.Apps { + if appsInStore[app.Name()] > 1 { + // Multiple versions of the same app exist in the store. + // If the version being removed is not installed, another version may still be + // installed and using the same compose directory. In that case, keep the app + // compose directory; otherwise we could remove compose files needed by the + // other installed version. + if _, isNotInstalled := status.NotInstalledCompose[app.Ref().Digest]; isNotInstalled { + continue + } + } + if err = os.RemoveAll(cfg.GetAppComposeDir(app.Name())); err != nil { + return err + } + } } if opts.Prune { diff --git a/pkg/update/complete.go b/pkg/update/complete.go index 44b5567..0c5c73c 100644 --- a/pkg/update/complete.go +++ b/pkg/update/complete.go @@ -3,6 +3,7 @@ package update import ( "context" "fmt" + "github.com/containerd/containerd/platforms" "github.com/foundriesio/composeapp/pkg/compose" v1 "github.com/foundriesio/composeapp/pkg/compose/v1" @@ -10,17 +11,20 @@ import ( type ( CompleteOpts struct { - Prune bool - Force bool - KeepApps []string + Prune bool + ImagePruneType compose.PruneType + Force bool } CompleteOpt func(*CompleteOpts) ) -func CompleteWithPruning(apps ...string) CompleteOpt { +func CompleteWithPruning(imagePruneType ...compose.PruneType) CompleteOpt { return func(opts *CompleteOpts) { opts.Prune = true - opts.KeepApps = apps + opts.ImagePruneType = compose.PruneTypeOnlyAppImages + if len(imagePruneType) > 0 { + opts.ImagePruneType = imagePruneType[0] + } } } @@ -84,31 +88,20 @@ func (u *runnerImpl) complete(ctx context.Context, options ...CompleteOpt) error } if opts.Prune { - isInKeepList := func(appName string) bool { - for _, keepApp := range opts.KeepApps { - if appName == keepApp { - return true - } - } - return false - } currentApps, err := compose.ListApps(ctx, u.config) if err != nil { return err } var appsToPrune []string for _, app := range currentApps { - if isInKeepList(app.Name()) { - continue - } if _, ok := updateApps[app.Ref().String()]; !ok { appsToPrune = append(appsToPrune, app.Ref().String()) } } + if err := compose.UninstallApps(ctx, u.config, appsToPrune, compose.WithImagePruning(opts.ImagePruneType)); err != nil { + return err + } if len(appsToPrune) > 0 { - if err := compose.UninstallApps(ctx, u.config, appsToPrune, compose.WithImagePruning()); err != nil { - return err - } if err := compose.RemoveApps(ctx, u.config, appsToPrune, compose.WithCheckStatus(false)); err != nil { return err }