From 0772454e8ac00348bbbec8591d9882e8dd59261b Mon Sep 17 00:00:00 2001 From: petergmurphy Date: Wed, 16 Feb 2022 17:16:23 +0000 Subject: [PATCH] (GH-349) Move install logic from config_processor Prior to this commit, the private processor included the ProcessConfig public function which called the private functions readConfig and setupTemplateNamespace to place a package in the right folder structure so that it is recognized as an installed package. This is an install function placed in the config processor. This commit replaces the ProcessConfig function in the config_processor.ConfigProcessorI interface with GetConfigMetadata, which returns the new ConfigMetadata struct also newly defined in config_processor. This enables a consumer of a ConfigProcessorI implementation to retrieve the metadata needed to install a package generically. The prior implementation conflated configuration processing with install logic. This commit adds a new public function to the install package, InstallFromConfig. This function combines the logic from the removed ProcessConfig and setupTemplateNamespace functions from the private config processor, genericising it in the process. This commit extends the install.Installer struct to take a config file name, so that no hard coding of the config file name is needed in the package; this makes it generically reusable regardless of config file. Additionally, this bug corrects a minor issue in the cleanup for an installed package, ensuring the temp folder the package is gunzipped to can actually be deleted on Windows by replacing the `AFS.Remove()` call with one to `AFS.RemoveAll()`, which removes a folder and its children recursively. `AFS.Remove()` previously errored on Windows due to the gunzipped tar file remaining after the untarred contents were moved. --- .../pct_config_processor.go | 61 +++++-------------- main.go | 1 + pkg/config_processor/config_processor.go | 8 ++- pkg/install/install.go | 53 ++++++++++++++-- 4 files changed, 72 insertions(+), 51 deletions(-) diff --git a/internal/pkg/pct_config_processor/pct_config_processor.go b/internal/pkg/pct_config_processor/pct_config_processor.go index f2a1fd6a..25a99598 100644 --- a/internal/pkg/pct_config_processor/pct_config_processor.go +++ b/internal/pkg/pct_config_processor/pct_config_processor.go @@ -3,9 +3,9 @@ package pct_config_processor import ( "bytes" "fmt" - "path/filepath" "github.com/puppetlabs/pdkgo/internal/pkg/pct" + "github.com/puppetlabs/pdkgo/pkg/config_processor" "github.com/spf13/afero" "github.com/spf13/viper" ) @@ -14,23 +14,27 @@ type PctConfigProcessor struct { AFS *afero.Afero } -func (p *PctConfigProcessor) ProcessConfig(sourceDir, targetDir string, force bool) (string, error) { - // Read config to determine template properties - info, err := p.readConfig(filepath.Join(sourceDir, "pct-config.yml")) +func (p *PctConfigProcessor) GetConfigMetadata(configFile string) (metadata config_processor.ConfigMetadata, err error) { + configInfo, err := p.ReadConfig(configFile) if err != nil { - return "", fmt.Errorf("Invalid config: %v", err.Error()) + return metadata, err } - // Create namespaced directory and move contents of temp folder to it - namespacedPath, err := p.setupTemplateNamespace(targetDir, info, sourceDir, force) + err = p.CheckConfig(configFile) if err != nil { - return "", fmt.Errorf("Unable to install in namespace: %v", err.Error()) + return metadata, err } - return namespacedPath, nil + + metadata = config_processor.ConfigMetadata{ + Author: configInfo.Template.Author, + Id: configInfo.Template.Id, + Version: configInfo.Template.Version, + } + return metadata, nil } func (p *PctConfigProcessor) CheckConfig(configFile string) error { - info, err := p.readConfig(configFile) + info, err := p.ReadConfig(configFile) if err != nil { return err } @@ -55,7 +59,7 @@ func (p *PctConfigProcessor) CheckConfig(configFile string) error { return nil } -func (p *PctConfigProcessor) readConfig(configFile string) (info pct.PuppetContentTemplateInfo, err error) { +func (p *PctConfigProcessor) ReadConfig(configFile string) (info pct.PuppetContentTemplateInfo, err error) { fileBytes, err := p.AFS.ReadFile(configFile) if err != nil { return info, err @@ -75,38 +79,3 @@ func (p *PctConfigProcessor) readConfig(configFile string) (info pct.PuppetConte return info, err } - -func (p *PctConfigProcessor) setupTemplateNamespace(targetDir string, info pct.PuppetContentTemplateInfo, untarPath string, force bool) (string, error) { - // author/id/version - templatePath := filepath.Join(targetDir, info.Template.Author, info.Template.Id) - - err := p.AFS.MkdirAll(templatePath, 0750) - if err != nil { - return "", err - } - - namespacePath := filepath.Join(targetDir, info.Template.Author, info.Template.Id, info.Template.Version) - - // finally move to the full path - err = p.AFS.Rename(untarPath, namespacePath) - if err != nil { - // if a template already exists - if !force { - // error unless forced - return "", fmt.Errorf("Template already installed (%s)", namespacePath) - } else { - // remove the exiting template - err = p.AFS.RemoveAll(namespacePath) - if err != nil { - return "", fmt.Errorf("Unable to overwrite existing template: %v", err) - } - // perform the move again - err = p.AFS.Rename(untarPath, namespacePath) - if err != nil { - return "", fmt.Errorf("Unable to force install: %v", err) - } - } - } - - return namespacePath, err -} diff --git a/main.go b/main.go index 49f44c91..1ac65385 100644 --- a/main.go +++ b/main.go @@ -84,6 +84,7 @@ func main() { ConfigProcessor: &pct_config_processor.PctConfigProcessor{ AFS: &afs, }, + ConfigFileName: "pct-config.yml", }, AFS: &afs, } diff --git a/pkg/config_processor/config_processor.go b/pkg/config_processor/config_processor.go index 88b23d9f..4fc0dc93 100644 --- a/pkg/config_processor/config_processor.go +++ b/pkg/config_processor/config_processor.go @@ -1,6 +1,12 @@ package config_processor type ConfigProcessorI interface { - ProcessConfig(sourceDir, targetDir string, force bool) (string, error) + GetConfigMetadata(configFile string) (metadata ConfigMetadata, err error) CheckConfig(configFile string) error } + +type ConfigMetadata struct { + Id string + Author string + Version string +} diff --git a/pkg/install/install.go b/pkg/install/install.go index 25b56236..55fccbe8 100644 --- a/pkg/install/install.go +++ b/pkg/install/install.go @@ -32,6 +32,7 @@ type Installer struct { HTTPClient httpclient.HTTPClientI Exec exec_runner.ExecI ConfigProcessor config_processor.ConfigProcessorI + ConfigFileName string } type InstallerI interface { @@ -56,8 +57,10 @@ func (p *Installer) Install(templatePkg, targetDir string, force bool) (string, // create a temporary Directory to extract the tar.gz to tempDir, err := p.AFS.TempDir("", "") defer func() { - err := p.AFS.Remove(tempDir) - log.Debug().Msgf("Failed to remove temp dir: %v", err) + err := p.AFS.RemoveAll(tempDir) + if err != nil { + log.Debug().Msgf("Failed to remove temp dir: %v", err) + } }() if err != nil { return "", fmt.Errorf("Could not create tempdir to gunzip package: %v", err) @@ -76,7 +79,7 @@ func (p *Installer) Install(templatePkg, targetDir string, force bool) (string, } // Process the configuration file and set up namespacedPath and relocate config and content to it - namespacedPath, err := p.ConfigProcessor.ProcessConfig(untarPath, targetDir, force) + namespacedPath, err := p.InstallFromConfig(filepath.Join(untarPath, p.ConfigFileName), targetDir, force) if err != nil { return "", fmt.Errorf("Invalid config: %v", err.Error()) } @@ -125,7 +128,7 @@ func (p *Installer) InstallClone(gitUri, targetDir, tempDir string, force bool) return "", fmt.Errorf("Failed to remove '.git' directory") } - namespacedPath, err := p.ConfigProcessor.ProcessConfig(folderPath, targetDir, force) + namespacedPath, err := p.InstallFromConfig(filepath.Join(folderPath, p.ConfigFileName), targetDir, force) if err != nil { return "", err } @@ -177,3 +180,45 @@ func (p *Installer) downloadTemplate(targetURL *url.URL, downloadDir string) (st return downloadPath, nil } + +func (p *Installer) InstallFromConfig(configFile, targetDir string, force bool) (string, error) { + info, err := p.ConfigProcessor.GetConfigMetadata(configFile) + if err != nil { + return "", err + } + + // Create namespaced directory and move contents of temp folder to it + installedPkgPath := filepath.Join(targetDir, info.Author, info.Id) + + err = p.AFS.MkdirAll(installedPkgPath, 0750) + if err != nil { + return "", err + } + + installedPkgPath = filepath.Join(installedPkgPath, info.Version) + untarredPkgDir := filepath.Dir(configFile) + + // finally move to the full path + errMsgPrefix := "Unable to install in namespace:" + err = p.AFS.Rename(untarredPkgDir, installedPkgPath) + if err != nil { + // if a template already exists + if !force { + // error unless forced + return "", fmt.Errorf("%s Template already installed (%s)", errMsgPrefix, installedPkgPath) + } else { + // remove the exiting template + err = p.AFS.RemoveAll(installedPkgPath) + if err != nil { + return "", fmt.Errorf("%s Unable to overwrite existing template: %v", errMsgPrefix, err) + } + // perform the move again + err = p.AFS.Rename(untarredPkgDir, installedPkgPath) + if err != nil { + return "", fmt.Errorf("%s Unable to force install: %v", errMsgPrefix, err) + } + } + } + + return installedPkgPath, err +}