diff --git a/metadata/interface.go b/metadata/interface.go index bfa05ea8fc856f788d3d05f6ee1dea8965311e94..bc58d16ba114ffbfb78da45f94ba63046a38a5ed 100644 --- a/metadata/interface.go +++ b/metadata/interface.go @@ -11,11 +11,15 @@ var appFS afero.Fs // intent, and make code easier to read. type AbsPath string +// AbsPaths is a slice of `AbsPath`. +type AbsPaths []string + // Manager abstracts over a ksonnet application's metadata, allowing users to do // things like: create and delete environments; search for prototypes; vendor // libraries; and other non-core-application tasks. type Manager interface { Root() AbsPath + ComponentPaths() (AbsPaths, error) // // TODO: Fill in methods as we need them. // diff --git a/metadata/manager.go b/metadata/manager.go index 7f60687810cb3ba2a42362a68b6d75724e6c230e..2bfb04c78525d726e38df915eb27a0e391b0a903 100644 --- a/metadata/manager.go +++ b/metadata/manager.go @@ -125,6 +125,25 @@ func (m *manager) Root() AbsPath { return m.rootPath } +func (m *manager) ComponentPaths() (AbsPaths, error) { + paths := []string{} + err := afero.Walk(m.appFS, string(m.componentsPath), func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() { + paths = append(paths, path) + } + return nil + }) + if err != nil { + return nil, err + } + + return paths, nil +} + func (m *manager) cacheClusterSpecData(name string, specData []byte) error { envPath := string(appendToAbsPath(m.schemaDir, name)) err := m.appFS.MkdirAll(envPath, os.ModePerm) diff --git a/metadata/manager_test.go b/metadata/manager_test.go index 5dabda9ff14d45333d6c4cad62039c7dd560061a..1e591ac85ce206ee7ad320c63868d20986e56c14 100644 --- a/metadata/manager_test.go +++ b/metadata/manager_test.go @@ -3,6 +3,7 @@ package metadata import ( "fmt" "os" + "sort" "testing" "github.com/spf13/afero" @@ -125,6 +126,59 @@ func TestFindSuccess(t *testing.T) { findSuccess(t, appPath, appFile) } +func TestComponentPaths(t *testing.T) { + spec, err := parseClusterSpec(fmt.Sprintf("file:%s", blankSwagger), testFS) + if err != nil { + t.Fatalf("Failed to parse cluster spec: %v", err) + } + + appPath := AbsPath("/componentPaths") + m, err := initManager(appPath, spec, testFS) + if err != nil { + t.Fatalf("Failed to init cluster spec: %v", err) + } + + // Create empty app file. + components := appendToAbsPath(appPath, componentsDir) + appFile1 := appendToAbsPath(components, "component1.jsonnet") + f1, err := testFS.OpenFile(string(appFile1), os.O_RDONLY|os.O_CREATE, 0777) + if err != nil { + t.Fatalf("Failed to touch app file '%s'\n%v", appFile1, err) + } + f1.Close() + + // Create empty file in a nested directory. + appSubdir := appendToAbsPath(components, "appSubdir") + err = testFS.MkdirAll(string(appSubdir), os.ModePerm) + if err != nil { + t.Fatalf("Failed to create directory '%s'\n%v", appSubdir, err) + } + appFile2 := appendToAbsPath(appSubdir, "component2.jsonnet") + f2, err := testFS.OpenFile(string(appFile2), os.O_RDONLY|os.O_CREATE, 0777) + if err != nil { + t.Fatalf("Failed to touch app file '%s'\n%v", appFile1, err) + } + f2.Close() + + // Create a directory that won't be listed in the call to `ComponentPaths`. + unlistedDir := string(appendToAbsPath(components, "doNotListMe")) + err = testFS.MkdirAll(unlistedDir, os.ModePerm) + if err != nil { + t.Fatalf("Failed to create directory '%s'\n%v", unlistedDir, err) + } + + paths, err := m.ComponentPaths() + if err != nil { + t.Fatalf("Failed to find component paths: %v", err) + } + + sort.Slice(paths, func(i, j int) bool { return paths[i] < paths[j] }) + + if len(paths) != 2 || paths[0] != string(appFile2) || paths[1] != string(appFile1) { + t.Fatalf("m.ComponentPaths failed; expected '%s', got '%s'", []string{string(appFile1), string(appFile2)}, paths) + } +} + func TestFindFailure(t *testing.T) { findFailure := func(t *testing.T, currDir AbsPath) { _, err := findManager(currDir, testFS)