diff --git a/package_list.hcl b/package_list.hcl index d967e8c..e41e630 100644 --- a/package_list.hcl +++ b/package_list.hcl @@ -1,5 +1,7 @@ package "python" { image="packageless/python" + base_dir="./python/" + volume { path="./python/packages/" mount="/usr/local/lib/python3.9/site-packages/" diff --git a/subcommands/install_sc.go b/subcommands/install_sc.go index 1ad3c8f..121473e 100644 --- a/subcommands/install_sc.go +++ b/subcommands/install_sc.go @@ -102,20 +102,21 @@ func (ic *InstallCommand) Run() error { fmt.Println("Creating package directories") + //Create the base directory for the package + err = MakeDir(pack.BaseDir) + + if err != nil { + return err + } + //Check the volumes and create the directories for them if they don't already exist for _, vol := range pack.Volumes { //Make sure that a path is given. If not we already assume that the working directory will be mounted if vol.Path != "" { - if _, err := os.Stat(vol.Path); err != nil { - if os.IsNotExist(err) { - err = os.MkdirAll(vol.Path, 0755) - - if err != nil { - return err - } - } else { - return err - } + err = MakeDir(vol.Path) + + if err != nil { + return err } } } @@ -155,3 +156,19 @@ func (ic *InstallCommand) Run() error { return nil } + +//MakeDir makes a directory if it doesnt exist given the path +func MakeDir(path string) error { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + err = os.MkdirAll(path, 0755) + + if err != nil { + return err + } + } else { + return err + } + } + return nil +} diff --git a/subcommands/subcommand.go b/subcommands/subcommand.go index b171783..277192c 100644 --- a/subcommands/subcommand.go +++ b/subcommands/subcommand.go @@ -23,6 +23,7 @@ func SubCommand(args []string) error { NewInstallCommand(), NewUpgradeCommand(), NewRunCommand(), + NewUninstallCommand(), } subcommand := os.Args[1] diff --git a/subcommands/uninstall_sc.go b/subcommands/uninstall_sc.go new file mode 100644 index 0000000..700ccc5 --- /dev/null +++ b/subcommands/uninstall_sc.go @@ -0,0 +1,148 @@ +package subcommands + +import ( + "errors" + "flag" + "fmt" + "os" + + "github.com/everettraven/packageless/utils" +) + +//Uninstall Sub-Command Object +type UninstallCommand struct { + //FlagSet so that we can create a custom flag + fs *flag.FlagSet + + //String for the name of the package to Uninstall + name string +} + +//Instantiation method for a new UninstallCommand +func NewUninstallCommand() *UninstallCommand { + //Create a new UninstallCommand and set the FlagSet + uc := &UninstallCommand{ + fs: flag.NewFlagSet("uninstall", flag.ContinueOnError), + } + + return uc +} + +//Name - Gets the name of the Sub-Command +func (uc *UninstallCommand) Name() string { + return uc.fs.Name() +} + +//Init - Parses and Populates values of the Uninstall subcommand +func (uc *UninstallCommand) Init(args []string) error { + + if len(args) <= 0 { + return errors.New("No package name was found. You must include the name of the package you wish to uninstall.") + } + + uc.name = args[0] + + return nil +} + +//Uninstall - Uninstalls the Uninstall subcommand +func (uc *UninstallCommand) Run() error { + //Create variables to use later + var found bool + var pack utils.Package + + //Default location of the package list + packageList := "./package_list.hcl" + + //Parse the package list + parseOut, err := utils.Parse(packageList, utils.PackageHCLUtil{}) + + //Check for errors + if err != nil { + return err + } + + packages := parseOut.(utils.PackageHCLUtil) + + //Check for errors + if err != nil { + return err + } + + //Look for the package we want in the package list + for _, packs := range packages.Packages { + //If we find it, set some variables and break + if packs.Name == uc.name { + found = true + pack = packs + break + } + } + + //Make sure we have found the package in the package list + if !found { + return errors.New("Could not find package " + uc.name + " in the package list") + } + + //Check if the corresponding package image is already Uninstalled + imgExist, err := utils.ImageExists(pack.Image) + + //Check for errors + if err != nil { + return err + } + + //If the image doesn't exist it can't be uninstalled + if !imgExist { + return errors.New("Package " + pack.Name + " is not installed.") + } + + fmt.Println("Removing package", pack.Name) + + //Check for the directories that correspond to this packages volumes + fmt.Println("Removing package directories") + + //Check the volumes and remove the directories if they exist + for _, vol := range pack.Volumes { + //Make sure that a path is given. + if vol.Path != "" { + err = RemoveDir(vol.Path) + + if err != nil { + return err + } + } + } + + //Remove the base directory for the package + err = RemoveDir(pack.BaseDir) + + if err != nil { + return err + } + + //Remove the image + err = utils.RemoveImage(pack.Image) + + //Check for errors + if err != nil { + return err + } + + return nil +} + +//RemoveDir removes a given directory if it exists +func RemoveDir(path string) error { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + return nil + } else { + return err + } + } else { + err = os.RemoveAll(path) + } + + return nil +} diff --git a/utils/docker.go b/utils/docker.go index 5ccb0da..93ecade 100644 --- a/utils/docker.go +++ b/utils/docker.go @@ -53,7 +53,7 @@ func ImageExists(imageID string) (bool, error) { //Check for errors if err != nil { - return false, nil + return false, err } //Create a context and get a list of images on the system @@ -62,7 +62,7 @@ func ImageExists(imageID string) (bool, error) { //Check for errors if err != nil { - return false, nil + return false, err } //Loop through all the images and check if a match is found @@ -279,3 +279,44 @@ func RunContainer(image string, ports []string, volumes []string, containerName return nil } + +//RemoveImage removes the image with the given name from local Docker +func RemoveImage(image string) error { + //Create the client + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + + //Check for errors + if err != nil { + return err + } + + //Create the context and search for the image in the list of images + ctx := context.Background() + + images, err := cli.ImageList(ctx, types.ImageListOptions{}) + + //Check for errors + if err != nil { + return err + } + + //Create a variable to hold the ID of the image we want to remove + var imageID string + + //Loop through all the images and check if a match is found + for _, img := range images { + if strings.Split(img.RepoTags[0], ":")[0] == image { + imageID = img.ID + } + } + + //Remove the image + _, err = cli.ImageRemove(ctx, imageID, types.ImageRemoveOptions{Force: true}) + + //Check for errors + if err != nil { + return err + } + + return nil +} diff --git a/utils/hcl_parse.go b/utils/hcl_parse.go index 0e38c26..b5d9bbd 100644 --- a/utils/hcl_parse.go +++ b/utils/hcl_parse.go @@ -23,6 +23,7 @@ type Volume struct { type Package struct { Name string `hcl:"name,label"` Image string `hcl:"image,attr"` + BaseDir string `hcl:"base_dir,attr"` Volumes []Volume `hcl:"volume,block"` Copies []*Copy `hcl:"copy,block"` Port string `hcl:"port,optional"`