From 55f8a97f506d64ccdcd0d8f232cb2d6cdf1d2cc9 Mon Sep 17 00:00:00 2001 From: Aurora Gaffney Date: Wed, 21 Feb 2024 16:39:35 -0600 Subject: [PATCH] feat: basic uninstall support Fixes #11 --- cmd/cardano-up/install.go | 1 + cmd/cardano-up/main.go | 1 + cmd/cardano-up/uninstall.go | 63 +++++++++++++++++++++++++++++++++++++ pkgmgr/docker.go | 10 +++++- pkgmgr/package.go | 57 +++++++++++++++++++++++++++++++-- pkgmgr/pkgmgr.go | 27 +++++++++++++++- 6 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 cmd/cardano-up/uninstall.go diff --git a/cmd/cardano-up/install.go b/cmd/cardano-up/install.go index 168593b..ddb1232 100644 --- a/cmd/cardano-up/install.go +++ b/cmd/cardano-up/install.go @@ -57,6 +57,7 @@ func installCommand() *cobra.Command { slog.Error(fmt.Sprintf("no such package: %s", args[0])) os.Exit(1) } + slog.Info(fmt.Sprintf("Successfully installed package %s", args[0])) }, } } diff --git a/cmd/cardano-up/main.go b/cmd/cardano-up/main.go index 506a44c..bbfebe0 100644 --- a/cmd/cardano-up/main.go +++ b/cmd/cardano-up/main.go @@ -66,6 +66,7 @@ func main() { versionCommand(), listAvailableCommand(), installCommand(), + uninstallCommand(), ) if err := rootCmd.Execute(); err != nil { diff --git a/cmd/cardano-up/uninstall.go b/cmd/cardano-up/uninstall.go new file mode 100644 index 0000000..ea65b35 --- /dev/null +++ b/cmd/cardano-up/uninstall.go @@ -0,0 +1,63 @@ +// Copyright 2024 Blink Labs Software +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "log/slog" + "os" + + "github.com/blinklabs-io/cardano-up/pkgmgr" + "github.com/spf13/cobra" +) + +func uninstallCommand() *cobra.Command { + return &cobra.Command{ + Use: "uninstall", + Short: "Uninstall package", + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + slog.Error("no package provided") + os.Exit(1) + } + if len(args) > 1 { + slog.Error("only one package may be specified a a time") + os.Exit(1) + } + pm, err := pkgmgr.NewDefaultPackageManager() + if err != nil { + slog.Error(fmt.Sprintf("failed to create package manager: %s", err)) + os.Exit(1) + } + installedPackages := pm.InstalledPackages() + foundPackage := false + for _, tmpPackage := range installedPackages { + if tmpPackage.Package.Name == args[0] { + foundPackage = true + if err := pm.Uninstall(tmpPackage); err != nil { + slog.Error(err.Error()) + os.Exit(1) + } + break + } + } + if !foundPackage { + slog.Error(fmt.Sprintf("no such installed package: %s", args[0])) + os.Exit(1) + } + slog.Info(fmt.Sprintf("Successfully uninstalled package %s", args[0])) + }, + } +} diff --git a/pkgmgr/docker.go b/pkgmgr/docker.go index b134132..02249b4 100644 --- a/pkgmgr/docker.go +++ b/pkgmgr/docker.go @@ -91,6 +91,7 @@ func (d *DockerService) Start() error { if err != nil { return err } + d.logger.Debug(fmt.Sprintf("starting container %s", d.ContainerName)) if err := client.ContainerStart( context.Background(), d.ContainerId, @@ -112,6 +113,7 @@ func (d *DockerService) Stop() error { if err != nil { return err } + d.logger.Debug(fmt.Sprintf("stopping container %s", d.ContainerName)) stopTimeout := 60 if err := client.ContainerStop( context.Background(), @@ -148,6 +150,7 @@ func (d *DockerService) Create() error { if err != nil { return err } + d.logger.Debug(fmt.Sprintf("creating container %s", d.ContainerName)) resp, err := client.ContainerCreate( context.Background(), &container.Config{ @@ -190,6 +193,7 @@ func (d *DockerService) Remove() error { if err != nil { return err } + d.logger.Debug(fmt.Sprintf("removing container %s", d.ContainerName)) if err := client.ContainerRemove( context.Background(), d.ContainerId, @@ -201,11 +205,11 @@ func (d *DockerService) Remove() error { } func (d *DockerService) pullImage() error { - d.logger.Info(fmt.Sprintf("pulling image %s", d.Image)) client, err := d.getClient() if err != nil { return err } + d.logger.Debug(fmt.Sprintf("pulling image %s", d.Image)) out, err := client.ImagePull(context.Background(), d.Image, types.ImagePullOptions{}) if err != nil { return err @@ -264,6 +268,10 @@ func (d *DockerService) refresh() error { d.Binds = tmpBinds[:] var tmpPorts []string for port, portBindings := range container.NetworkSettings.Ports { + // Skip exposed container ports without a mapping + if len(portBindings) == 0 { + continue + } tmpPort := fmt.Sprintf( "0.0.0.0:%s:%s", portBindings[0].HostPort, diff --git a/pkgmgr/package.go b/pkgmgr/package.go index ceab3a7..c42e757 100644 --- a/pkgmgr/package.go +++ b/pkgmgr/package.go @@ -28,8 +28,8 @@ type Package struct { InstallSteps []PackageInstallStep `yaml:"installSteps"` } -func (p Package) install(cfg Config) error { - pkgName := fmt.Sprintf("%s-%s", p.Name, p.Version) +func (p Package) install(cfg Config, context string) error { + pkgName := fmt.Sprintf("%s-%s-%s", p.Name, p.Version, context) for _, installStep := range p.InstallSteps { // Make sure only one install method is specified per install step if installStep.Docker != nil && @@ -51,6 +51,31 @@ func (p Package) install(cfg Config) error { return nil } +func (p Package) uninstall(cfg Config, context string) error { + pkgName := fmt.Sprintf("%s-%s-%s", p.Name, p.Version, context) + // Iterate over install steps in reverse + for idx := len(p.InstallSteps) - 1; idx >= 0; idx-- { + installStep := p.InstallSteps[idx] + // Make sure only one install method is specified per install step + if installStep.Docker != nil && + installStep.File != nil { + return ErrMultipleInstallMethods + } + if installStep.Docker != nil { + if err := installStep.Docker.uninstall(cfg, pkgName); err != nil { + return err + } + } else if installStep.File != nil { + if err := installStep.File.uninstall(cfg, pkgName); err != nil { + return err + } + } else { + return ErrNoInstallMethods + } + } + return nil +} + type PackageInstallStep struct { Docker *PackageInstallStepDocker `yaml:"docker,omitempty"` File *PackageInstallStepFile `yaml:"file,omitempty"` @@ -87,6 +112,23 @@ func (p *PackageInstallStepDocker) install(cfg Config, pkgName string) error { return nil } +func (p *PackageInstallStepDocker) uninstall(cfg Config, pkgName string) error { + containerName := fmt.Sprintf("%s-%s", pkgName, p.ContainerName) + svc, err := NewDockerServiceFromContainerName(containerName, cfg.Logger) + if err != nil { + return err + } + if running, _ := svc.Running(); running { + if err := svc.Stop(); err != nil { + return err + } + } + if err := svc.Remove(); err != nil { + return err + } + return nil +} + type PackageInstallStepFile struct { Filename string `yaml:"filename"` Content string `yaml:"content"` @@ -116,3 +158,14 @@ func (p *PackageInstallStepFile) install(cfg Config, pkgName string) error { cfg.Logger.Debug(fmt.Sprintf("wrote file %s", filePath)) return nil } + +func (p *PackageInstallStepFile) uninstall(cfg Config, pkgName string) error { + filePath := filepath.Join( + cfg.ConfigDir, + "data", + pkgName, + p.Filename, + ) + cfg.Logger.Debug(fmt.Sprintf("deleting file %s", filePath)) + return os.Remove(filePath) +} diff --git a/pkgmgr/pkgmgr.go b/pkgmgr/pkgmgr.go index f8ffc50..d85347c 100644 --- a/pkgmgr/pkgmgr.go +++ b/pkgmgr/pkgmgr.go @@ -67,8 +67,12 @@ func (p *PackageManager) AvailablePackages() []Package { return p.availablePackages[:] } +func (p *PackageManager) InstalledPackages() []InstalledPackage { + return p.state.InstalledPackages +} + func (p *PackageManager) Install(pkg Package) error { - if err := pkg.install(p.config); err != nil { + if err := pkg.install(p.config, p.state.ActiveContext); err != nil { return err } installedPkg := NewInstalledPackage(pkg, p.state.ActiveContext) @@ -78,3 +82,24 @@ func (p *PackageManager) Install(pkg Package) error { } return nil } + +func (p *PackageManager) Uninstall(installedPkg InstalledPackage) error { + if err := installedPkg.Package.uninstall(p.config, installedPkg.Context); err != nil { + return err + } + // Remove package from installed packages + var tmpInstalledPackages []InstalledPackage + for _, tmpInstalledPkg := range p.state.InstalledPackages { + if tmpInstalledPkg.Context == installedPkg.Context && + tmpInstalledPkg.Package.Name == installedPkg.Package.Name && + tmpInstalledPkg.Package.Version == installedPkg.Package.Version { + continue + } + tmpInstalledPackages = append(tmpInstalledPackages, tmpInstalledPkg) + } + p.state.InstalledPackages = tmpInstalledPackages[:] + if err := p.state.Save(); err != nil { + return err + } + return nil +}