Skip to content

Commit

Permalink
Add import subcommand to generate a library from a directory of opsfiles
Browse files Browse the repository at this point in the history
  • Loading branch information
cjnosal committed Oct 23, 2019
1 parent e514073 commit e477c2d
Show file tree
Hide file tree
Showing 13 changed files with 863 additions and 15 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ mock_*

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Integration test output
test/data/generated.yml
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ combine the library and template to compose the final document.
- compose: interpolate for all snippets defined in a set of scenarios

# subcommands
## import
```
import [--recursive] --path <import path> --out <library path>:
create a library from a directory of opsfiles.
-o string
Path to save generated library file
-out string
Path to save generated library file
-p string
Directory or opsfile to import
-path string
Directory or opsfile to import
-r Import opsfiles from subdirectories
-recursive
Import opsfiles from subdirectories
```
## list
```
./manifer list [--all] (--library <library path>...):
Expand Down
87 changes: 87 additions & 0 deletions cmd/commands/import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package commands

import (
"context"
"flag"
"io"
"log"

"github.com/google/subcommands"

"github.com/cjnosal/manifer/lib"
"github.com/cjnosal/manifer/pkg/file"
"github.com/cjnosal/manifer/pkg/library"
"github.com/cjnosal/manifer/pkg/yaml"
)

type importCmd struct {
out string
path string
recursive bool

logger *log.Logger
writer io.Writer
manifer lib.Manifer
}

func NewImportCommand(l io.Writer, w io.Writer, m lib.Manifer) subcommands.Command {
return &importCmd{
logger: log.New(l, "", 0),
writer: w,
manifer: m,
}
}

func (*importCmd) Name() string { return "import" }
func (*importCmd) Synopsis() string { return "create a library from a directory of opsfiles." }
func (*importCmd) Usage() string {
return `import [--recursive] --path <import path> --out <library path>:
create a library from a directory of opsfiles.
`
}

func (p *importCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&p.out, "out", "", "Path to save generated library file")
f.StringVar(&p.out, "o", "", "Path to save generated library file")
f.StringVar(&p.path, "path", "", "Directory or opsfile to import")
f.StringVar(&p.path, "p", "", "Directory or opsfile to import")
f.BoolVar(&p.recursive, "recursive", false, "Import opsfiles from subdirectories")
f.BoolVar(&p.recursive, "r", false, "Import opsfiles from subdirectories")
}

func (p *importCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {

if len(p.path) == 0 {
p.logger.Printf("Import path not specified")
p.logger.Printf(p.Usage())
return subcommands.ExitFailure
}
if len(p.out) == 0 {
p.logger.Printf("Output path not specified")
p.logger.Printf(p.Usage())
return subcommands.ExitFailure
}

lib, err := p.manifer.Import(library.OpsFile, p.path, p.recursive, p.out)

if err != nil {
p.logger.Printf("%v\n while generating library", err)
return subcommands.ExitFailure
}

yaml := &yaml.Yaml{}
outBytes, err := yaml.Marshal(lib)
if err != nil {
p.logger.Printf("%v\n while marshaling generated library", err)
return subcommands.ExitFailure
}

file := &file.FileIO{}
err = file.Write(p.out, outBytes, 0644)
if err != nil {
p.logger.Printf("%v\n while writing generated library", err)
return subcommands.ExitFailure
}

return subcommands.ExitSuccess
}
1 change: 1 addition & 0 deletions cmd/manifer/manifer.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func main() {
subcommands.Register(commands.NewListCommand(logger, writer, maniferLib), "")
subcommands.Register(commands.NewSearchCommand(logger, writer, maniferLib), "")
subcommands.Register(commands.NewInspectCommand(logger, writer, maniferLib), "")
subcommands.Register(commands.NewImportCommand(logger, writer, maniferLib), "")

// run
flag.Parse()
Expand Down
117 changes: 117 additions & 0 deletions cmd/manifer/manifer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,4 +381,121 @@ dependencies:
})

})

t.Run("TestImport file", func(t *testing.T) {
cmd := exec.Command(
"../../manifer",
"import",
"-p",
"../../test/data/opsfile.yml",
"-o",
"../../test/data/generated.yml",
)

err := cmd.Run()
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

cat := exec.Command(
"cat",
"../../test/data/generated.yml",
)
outWriter := &test.StringWriter{}
cat.Stdout = outWriter

err = cat.Run()
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

expectedOut := `libraries: []
type: opsfile
scenarios:
- name: opsfile
description: imported from opsfile.yml
global_args: []
args: []
snippets:
- path: opsfile.yml
args: []
scenarios: []
`

if !cmp.Equal(outWriter.String(), expectedOut) {
t.Errorf("Expected Stdout:\n'''%v'''\nActual:\n'''%v'''\nDiff:\n'''%v'''\n",
expectedOut, outWriter.String(), cmp.Diff(expectedOut, outWriter.String()))
}
})

t.Run("TestImport directory", func(t *testing.T) {
cmd := exec.Command(
"../../manifer",
"import",
"-r",
"-p",
"../../test/data",
"-o",
"../../test/data/generated.yml",
)

err := cmd.Run()
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

cat := exec.Command(
"cat",
"../../test/data/generated.yml",
)
outWriter := &test.StringWriter{}
cat.Stdout = outWriter

err = cat.Run()
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

expectedOut := `libraries: []
type: opsfile
scenarios:
- name: empty_opsfile
description: imported from empty_opsfile.yml
global_args: []
args: []
snippets:
- path: empty_opsfile.yml
args: []
scenarios: []
- name: opsfile
description: imported from opsfile.yml
global_args: []
args: []
snippets:
- path: opsfile.yml
args: []
scenarios: []
- name: opsfile_with_vars
description: imported from opsfile_with_vars.yml
global_args: []
args: []
snippets:
- path: opsfile_with_vars.yml
args: []
scenarios: []
- name: placeholder_opsfile
description: imported from placeholder_opsfile.yml
global_args: []
args: []
snippets:
- path: placeholder_opsfile.yml
args: []
scenarios: []
`

if !cmp.Equal(outWriter.String(), expectedOut) {
t.Errorf("Expected Stdout:\n'''%v'''\nActual:\n'''%v'''\nDiff:\n'''%v'''\n",
expectedOut, outWriter.String(), cmp.Diff(expectedOut, outWriter.String()))
}
})
}
17 changes: 14 additions & 3 deletions lib/maniferlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/cjnosal/manifer/pkg/composer"
"github.com/cjnosal/manifer/pkg/diff"
"github.com/cjnosal/manifer/pkg/file"
"github.com/cjnosal/manifer/pkg/importer"
"github.com/cjnosal/manifer/pkg/interpolator"
"github.com/cjnosal/manifer/pkg/interpolator/opsfile"
"github.com/cjnosal/manifer/pkg/library"
Expand All @@ -18,12 +19,15 @@ import (

// logger used for Composer's showDiff/showPlan
func NewManifer(logger io.Writer) Manifer {
fileIO := &file.FileIO{}
opsFileInterpolator := opsfile.NewOpsFileInterpolator(&yaml.Yaml{}, fileIO)
return &libImpl{
composer: newComposer(logger),
lister: newLister(),
loader: newLoader(),
file: &file.FileIO{},
opInt: opsfile.NewOpsFileInterpolator(&yaml.Yaml{}),
file: fileIO,
opInt: opsFileInterpolator,
importer: importer.NewImporter(fileIO, opsFileInterpolator),
}
}

Expand All @@ -49,6 +53,8 @@ type Manifer interface {
GetScenarioTree(libraryPaths []string, name string) (*library.ScenarioNode, error)

GetScenarioNode(passthroughArgs []string) (*library.ScenarioNode, error)

Import(libType library.Type, path string, recursive bool, outPath string) (*library.Library, error)
}

type libImpl struct {
Expand All @@ -57,6 +63,7 @@ type libImpl struct {
loader *library.Loader
file *file.FileIO
opInt interpolator.Interpolator
importer importer.Importer
}

func (l *libImpl) Compose(
Expand Down Expand Up @@ -108,6 +115,10 @@ func (l *libImpl) GetScenarioNode(passthroughArgs []string) (*library.ScenarioNo
return l.opInt.ParsePassthroughFlags(passthroughArgs)
}

func (l *libImpl) Import(libType library.Type, path string, recursive bool, outPath string) (*library.Library, error) {
return l.importer.Import(libType, path, recursive, outPath)
}

func (l *libImpl) makePathsRelative(node *library.ScenarioNode) error {
for i, snippet := range node.Snippets {
rel, err := l.file.ResolveRelativeFromWD(snippet.Path)
Expand Down Expand Up @@ -166,7 +177,7 @@ func newComposer(logger io.Writer) composer.Composer {
File: file,
Yaml: yaml,
}
opsFileInterpolator := opsfile.NewOpsFileInterpolator(yaml)
opsFileInterpolator := opsfile.NewOpsFileInterpolator(yaml, file)
resolver := &composer.Resolver{
Loader: loader,
SnippetResolver: opsFileInterpolator,
Expand Down
39 changes: 30 additions & 9 deletions pkg/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type FileAccess interface {
ResolveRelativeFrom(targetFile string, sourceFile string) (string, error)
ResolveRelativeFromWD(targetFile string) (string, error)
GetWorkingDirectory() (string, error)
IsDir(path string) (bool, error)
Walk(path string, callback func(path string, info os.FileInfo, err error) error) error
}

type FileIO struct{}
Expand Down Expand Up @@ -57,30 +59,37 @@ func (f *FileIO) ResolveRelativeTo(targetFile string, sourceFile string) (string
return targetFile, nil
} else {
dir := sourceFile
dirInfo, err := os.Stat(dir)
isDir, err := f.IsDir(dir)
if err != nil {
return "", err
}
if !dirInfo.IsDir() {
if !isDir {
dir = filepath.Dir(sourceFile)
}
return filepath.Clean(filepath.Join(dir, targetFile)), nil
}
}

func (f *FileIO) ResolveRelativeFrom(targetFile string, sourceFile string) (string, error) {
if !filepath.IsAbs(targetFile) {
return targetFile, nil
dir, err := filepath.Abs(sourceFile)
if err != nil {
return "", err
}
dir := sourceFile
dirInfo, err := os.Stat(dir)
isDir, err := f.IsDir(dir)
if err != nil {
return "", err
}
if !dirInfo.IsDir() {
dir = filepath.Dir(sourceFile)
if !isDir {
dir, err = filepath.Abs(filepath.Dir(sourceFile))
if err != nil {
return "", err
}
}
return filepath.Rel(dir, targetFile)
target, err := filepath.Abs(targetFile)
if err != nil {
return "", err
}
return filepath.Rel(dir, target)
}

func (f *FileIO) ResolveRelativeFromWD(targetFile string) (string, error) {
Expand All @@ -97,3 +106,15 @@ func (f *FileIO) ResolveRelativeFromWD(targetFile string) (string, error) {
func (f *FileIO) GetWorkingDirectory() (string, error) {
return os.Getwd()
}

func (f *FileIO) IsDir(path string) (bool, error) {
pathInfo, err := os.Stat(path)
if err != nil {
return false, err
}
return pathInfo.IsDir(), nil
}

func (f *FileIO) Walk(path string, callback func(path string, info os.FileInfo, err error) error) error {
return filepath.Walk(path, callback)
}
Loading

0 comments on commit e477c2d

Please sign in to comment.