Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

Commit

Permalink
Merge pull request #359 from puppetlabs/gh-349/main/move_install_logic
Browse files Browse the repository at this point in the history
(GH-349) Genericize `install` package logic and move it from the private config processor into the package
  • Loading branch information
chelnak authored Apr 9, 2022
2 parents 18afa6c + 3f208b0 commit 2c581ae
Show file tree
Hide file tree
Showing 7 changed files with 460 additions and 197 deletions.
61 changes: 15 additions & 46 deletions internal/pkg/pct_config_processor/pct_config_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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
}
261 changes: 169 additions & 92 deletions internal/pkg/pct_config_processor/pct_config_processor_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package pct_config_processor_test

import (
"github.com/puppetlabs/pdkgo/internal/pkg/pct"
"github.com/puppetlabs/pdkgo/pkg/config_processor"
"github.com/puppetlabs/pdkgo/pkg/install"
"path/filepath"
"reflect"
"regexp"
"testing"

Expand All @@ -10,98 +14,6 @@ import (
"github.com/stretchr/testify/assert"
)

type ProcessConfigTest struct {
name string
args args
expected expected
mocks mocks
}

type args struct {
targetDir string
sourceDir string
force bool
}

type expected struct {
errorMsg string
namespacedPath string
}

type mocks struct {
dirs []string
files map[string]string
}

func TestProcessConfig(t *testing.T) {
configDir := "path/to/config"

tests := []ProcessConfigTest{
{
name: "Config file is present and is correctly constructed",
args: args{targetDir: "templates", sourceDir: configDir, force: false},
expected: expected{errorMsg: "", namespacedPath: filepath.Join("templates", "test-user/test-template/0.1.0")},
mocks: mocks{
dirs: []string{"templates"},
files: map[string]string{
filepath.Join(configDir, "pct-config.yml"): `---
template:
id: test-template
author: test-user
version: 0.1.0
`,
},
},
},
{
name: "Config file does not exist",
args: args{targetDir: "templates", sourceDir: configDir, force: false},
expected: expected{errorMsg: "Invalid config: "},
},
{
name: "Config files exists but has invalid yaml",
args: args{targetDir: "templates", sourceDir: configDir, force: false},
expected: expected{errorMsg: "Invalid config: "},
mocks: mocks{
dirs: []string{"templates"},
files: map[string]string{
filepath.Join(configDir, "pct-config.yml"): `---
template: id: test-template author: test-user version: 0.1.0
`,
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fs := afero.NewMemMapFs()
afs := &afero.Afero{Fs: fs}

for _, path := range tt.mocks.dirs {
afs.Mkdir(path, 0750) //nolint:gosec,errcheck // this result is not used in a secure application
}

for file, content := range tt.mocks.files {
config, _ := afs.Create(file) //nolint:gosec,errcheck // this result is not used in a secure application
config.Write([]byte(content)) //nolint:errcheck
}

configProcessor := pct_config_processor.PctConfigProcessor{AFS: afs}

returnedPath, err := configProcessor.ProcessConfig(tt.args.sourceDir, tt.args.targetDir, tt.args.force)

if tt.expected.errorMsg != "" && err != nil {
assert.Contains(t, err.Error(), tt.expected.errorMsg)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.expected.namespacedPath, returnedPath)
})
}

}

type CheckConfigTest struct {
name string
mockConfigFile bool
Expand Down Expand Up @@ -222,3 +134,168 @@ foo: bar
})
}
}

func TestPctConfigProcessor_GetConfigMetadata(t *testing.T) {
type args struct {
configFile string
}
configParentPath := "path/to/extract/to/"

tests := []struct {
name string
args args
wantMetadata config_processor.ConfigMetadata
wantErr bool
templateConfig string // Leave blank for config file not to be created
}{
{
name: "Successfully gets config metadata",
args: args{
configFile: filepath.Join(configParentPath, "pct-config.yml"),
},
wantMetadata: config_processor.ConfigMetadata{Author: "test-user", Id: "full-project", Version: "0.1.0"},
templateConfig: `---
template:
id: full-project
author: test-user
version: 0.1.0
`,
},
{
name: "Missing vital metadata from pct-config.yml (id omitted)",
args: args{
configFile: filepath.Join(configParentPath, "pct-config.yml"),
},
wantErr: true,
wantMetadata: config_processor.ConfigMetadata{},
templateConfig: `---
template:
author: test-user
version: 0.1.0
`,
},
{
name: "Malformed pct-config (extra indentation)",
args: args{
configFile: filepath.Join(configParentPath, "pct-config.yml"),
},
wantErr: true,
wantMetadata: config_processor.ConfigMetadata{},
templateConfig: `---
template:
id: full-project
author: test-user
version: 0.1.0
`, // Contains an erroneous extra indent
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Instantiate afs
fs := afero.NewMemMapFs()
afs := &afero.Afero{Fs: fs}
p := &pct_config_processor.PctConfigProcessor{
AFS: afs,
}

// Create all useful directories
afs.MkdirAll(configParentPath, 0750) //nolint:gosec,errcheck
if tt.templateConfig != "" {
config, _ := afs.Create(tt.args.configFile)
config.Write([]byte(tt.templateConfig)) //nolint:errcheck
}

gotMetadata, err := p.GetConfigMetadata(tt.args.configFile)
if (err != nil) != tt.wantErr {
t.Errorf("GetConfigMetadata() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotMetadata, tt.wantMetadata) {
t.Errorf("GetConfigMetadata() gotMetadata = %v, want %v", gotMetadata, tt.wantMetadata)
}
})
}
}

func TestPctConfigProcessor_ReadConfig(t *testing.T) {
type fields struct {
AFS *afero.Afero
}
type args struct {
configFile string
}
configParentPath := "path/to/extract/to/"

tests := []struct {
name string
fields fields
args args
wantInfo pct.PuppetContentTemplateInfo
wantErr bool
templateConfig string
}{
{
name: "Successfully read file and return filled struct",
wantInfo: pct.PuppetContentTemplateInfo{
Template: pct.PuppetContentTemplate{
ConfigParams: install.ConfigParams{
Id: "full-project",
Author: "test-user",
Version: "0.1.0",
},
Type: "class",
Display: "Full Project",
URL: "github.com/puppetlabs/pdkgo",
},
},
templateConfig: `---
template:
id: full-project
author: test-user
version: 0.1.0
type: class
display: Full Project
url: github.com/puppetlabs/pdkgo
`,
},
{
name: "Returns an error because of the malformed template config",
wantErr: true,
templateConfig: `---
template:
id: full-project
author: test-user
version: 0.1.0
type: class
display: Full Project
url: github.com/puppetlabs/pdkgo
`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Instantiate afs
fs := afero.NewMemMapFs()
afs := &afero.Afero{Fs: fs}
p := &pct_config_processor.PctConfigProcessor{
AFS: afs,
}

// Create all useful directories
afs.MkdirAll(configParentPath, 0750) //nolint:gosec,errcheck
if tt.templateConfig != "" {
config, _ := afs.Create(tt.args.configFile)
config.Write([]byte(tt.templateConfig)) //nolint:errcheck
}

gotInfo, err := p.ReadConfig(tt.args.configFile)
if (err != nil) != tt.wantErr {
t.Errorf("ReadConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotInfo, tt.wantInfo) {
t.Errorf("ReadConfig() gotInfo = %v, want %v", gotInfo, tt.wantInfo)
}
})
}
}
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func main() {
ConfigProcessor: &pct_config_processor.PctConfigProcessor{
AFS: &afs,
},
ConfigFileName: "pct-config.yml",
},
AFS: &afs,
}
Expand Down
8 changes: 7 additions & 1 deletion pkg/config_processor/config_processor.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 2c581ae

Please sign in to comment.