Commit 6983dd2b authored by Oren Shomron's avatar Oren Shomron
Browse files

ks component list aggregates from all modules



Fixes #765
Signed-off-by: default avatarOren Shomron <shomron@gmail.com>
parent 50db0567
......@@ -67,12 +67,7 @@ func NewComponentList(m map[string]interface{}) (*ComponentList, error) {
// Run runs the ComponentList action.
func (cl *ComponentList) Run() error {
module, err := cl.cm.Module(cl.app, cl.module)
if err != nil {
return err
}
components, err := module.Components()
components, err := cl.cm.Components(cl.app, cl.module)
if err != nil {
return err
}
......
......@@ -41,28 +41,15 @@ func TestComponentList_wide(t *testing.T) {
cs := []component.Component{c1, c2}
module := &cmocks.Module{}
module.On("Components").Return(cs, nil)
cm := &cmocks.Manager{}
cm.On("Module", mock.Anything, "").Return(module, nil)
return cm
}
componentManagerCannotLoadModule := func() component.Manager {
cm := &cmocks.Manager{}
cm.On("Module", mock.Anything, "").Return(nil, errors.New("can't load module"))
cm.On("Components", mock.Anything, "/").Return(cs, nil)
return cm
}
cannotLoadComponents := func() component.Manager {
module := &cmocks.Module{}
module.On("Components").Return(nil, errors.New("can't load components"))
cm := &cmocks.Manager{}
cm.On("Module", mock.Anything, "").Return(module, nil)
cm.On("Components", mock.Anything, "/").Return(nil, errors.New("can't load components"))
return cm
}
......@@ -74,11 +61,8 @@ func TestComponentList_wide(t *testing.T) {
cs := []component.Component{c1}
module := &cmocks.Module{}
module.On("Components").Return(cs, nil)
cm := &cmocks.Manager{}
cm.On("Module", mock.Anything, "").Return(module, nil)
cm.On("Components", mock.Anything, "/").Return(cs, nil)
return cm
}
......@@ -113,11 +97,6 @@ func TestComponentList_wide(t *testing.T) {
output: "invalid",
isErr: true,
},
{
name: "can't load module",
componentManager: componentManagerCannotLoadModule(),
isErr: true,
},
{
name: "can't load components",
componentManager: cannotLoadComponents(),
......@@ -133,7 +112,7 @@ func TestComponentList_wide(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
withApp(t, func(appMock *amocks.App) {
moduleName := ""
moduleName := "/"
in := map[string]interface{}{
OptionApp: appMock,
......
......@@ -42,6 +42,8 @@ func newComponentPathLocator(a app.App, envName string) (*componentPathLocator,
}, nil
}
// Locate returns component paths targetted by an environment (specified in newComponentPathLocater), or
// all component paths if that environment identifier is the empty string.
func (cpl *componentPathLocator) Locate() ([]string, error) {
if cpl.envName == "" {
return cpl.allNamespaces()
......
......@@ -65,7 +65,7 @@ var (
// Manager is an interface for interacting with components.
type Manager interface {
Components(ns Module) ([]Component, error)
Components(ksApp app.App, module string) ([]Component, error)
Component(ksApp app.App, module, componentName string) (Component, error)
CreateModule(ksApp app.App, name string) error
Module(ksApp app.App, moduleName string) (Module, error)
......@@ -84,8 +84,32 @@ func (dm *defaultManager) Module(ksApp app.App, module string) (Module, error) {
return GetModule(ksApp, module)
}
func (dm *defaultManager) Components(ns Module) ([]Component, error) {
return ns.Components()
// Components returns components for the specified module, or all modules if module == ""
func (dm *defaultManager) Components(ksApp app.App, module string) ([]Component, error) {
if module != "" {
m, err := dm.Module(ksApp, module)
if err != nil {
return nil, err
}
return m.Components()
}
modules, err := dm.Modules(ksApp, "")
if err != nil {
return nil, errors.Wrap(err, "fetching modules")
}
result := make([]Component, 0, len(modules))
for _, m := range modules {
components, err := m.Components()
if err != nil {
return nil, errors.Wrapf(err, "fetching components for module: %s", m.Name())
}
for _, c := range components {
result = append(result, c)
}
}
return result, nil
}
func (dm *defaultManager) Component(ksApp app.App, module, componentName string) (Component, error) {
......
......@@ -16,6 +16,7 @@
package component
import (
"sort"
"testing"
"github.com/ksonnet/ksonnet/pkg/app/mocks"
......@@ -112,3 +113,71 @@ func Test_ResolvePath(t *testing.T) {
}
})
}
func Test_defaultManager_Components(t *testing.T) {
mgr := &defaultManager{}
cases := []struct {
name string
module string
expectedComponents []string
isErr bool
}{
{
name: "module: unknown",
module: "unknown",
expectedComponents: []string{},
isErr: true,
},
{
name: "module: /",
module: "/",
expectedComponents: []string{"deployment"},
isErr: false,
},
{
name: "module: nested",
module: "nested",
expectedComponents: []string{"nested.deployment"},
isErr: false,
},
{
name: "module: nested.deeper",
module: "nested.deeper",
expectedComponents: []string{"nested.deeper.deployment"},
isErr: false,
},
{
name: "all components",
module: "",
expectedComponents: []string{"deployment", "nested.deeper.deployment", "nested.deployment"},
isErr: false,
},
}
test.WithApp(t, "/app", func(a *mocks.App, fs afero.Fs) {
test.StageFile(t, fs, "params-mixed.libsonnet", "/app/components/params.libsonnet")
test.StageFile(t, fs, "deployment.yaml", "/app/components/deployment.yaml")
test.StageFile(t, fs, "params-mixed.libsonnet", "/app/components/nested/params.libsonnet")
test.StageFile(t, fs, "deployment.yaml", "/app/components/nested/deployment.yaml")
test.StageFile(t, fs, "params-mixed.libsonnet", "/app/components/nested/deeper/params.libsonnet")
test.StageFile(t, fs, "deployment.yaml", "/app/components/nested/deeper/deployment.yaml")
for _, tc := range cases {
actual, err := mgr.Components(a, tc.module)
if tc.isErr {
require.Error(t, err, tc.name)
continue
}
require.NoError(t, err, tc.name)
actualNames := make([]string, len(actual))
for i := range actual {
actualNames[i] = actual[i].Name(true)
}
sort.Strings(actualNames)
assert.Equal(t, tc.expectedComponents, actualNames, tc.name)
}
})
}
......@@ -48,13 +48,13 @@ func (_m *Manager) Component(ksApp app.App, module string, componentName string)
return r0, r1
}
// Components provides a mock function with given fields: ns
func (_m *Manager) Components(ns component.Module) ([]component.Component, error) {
ret := _m.Called(ns)
// Components provides a mock function with given fields: ksApp, module
func (_m *Manager) Components(ksApp app.App, module string) ([]component.Component, error) {
ret := _m.Called(ksApp, module)
var r0 []component.Component
if rf, ok := ret.Get(0).(func(component.Module) []component.Component); ok {
r0 = rf(ns)
if rf, ok := ret.Get(0).(func(app.App, string) []component.Component); ok {
r0 = rf(ksApp, module)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]component.Component)
......@@ -62,8 +62,8 @@ func (_m *Manager) Components(ns component.Module) ([]component.Component, error
}
var r1 error
if rf, ok := ret.Get(1).(func(component.Module) error); ok {
r1 = rf(ns)
if rf, ok := ret.Get(1).(func(app.App, string) error); ok {
r1 = rf(ksApp, module)
} else {
r1 = ret.Error(1)
}
......
......@@ -340,6 +340,7 @@ func (m *FilesystemModule) readParams() (string, error) {
}
// ModulesFromEnv returns all modules given an environment.
// If environment == "", all modules (globally) are returned.
func ModulesFromEnv(a app.App, env string) ([]Module, error) {
paths, err := MakePaths(a, env)
if err != nil {
......
......@@ -127,8 +127,8 @@ func (p *Pipeline) Components(filter []string) ([]component.Component, error) {
}
components := make([]component.Component, 0)
for _, ns := range modules {
members, err := p.cm.Components(ns)
for _, m := range modules {
members, err := p.cm.Components(p.app, m.Name())
if err != nil {
return nil, err
}
......
......@@ -68,13 +68,12 @@ func TestPipeline_Components(t *testing.T) {
cpnt := &cmocks.Component{}
components := []component.Component{cpnt}
ns := component.NewModule(p.app, "/")
namespaces := []component.Module{ns}
m.On("Modules", p.app, "default").Return(namespaces, nil)
m.On("Module", p.app, "/").Return(ns, nil)
m.On("NSResolveParams", ns).Return("", nil)
module := component.NewModule(p.app, "/")
modules := []component.Module{module}
m.On("Modules", p.app, "default").Return(modules, nil)
m.On("Module", p.app, "/").Return(module, nil)
a.On("EnvironmentParams", "default").Return("{}", nil)
m.On("Components", ns).Return(components, nil)
m.On("Components", p.app, "/").Return(components, nil)
got, err := p.Components(nil)
require.NoError(t, err)
......@@ -96,13 +95,12 @@ func TestPipeline_Components_filtered(t *testing.T) {
cpnt2 := mockComponent("cpnt2")
components := []component.Component{cpnt1, cpnt2}
ns := component.NewModule(p.app, "/")
namespaces := []component.Module{ns}
m.On("Modules", p.app, "default").Return(namespaces, nil)
m.On("Module", p.app, "/").Return(ns, nil)
m.On("NSResolveParams", ns).Return("", nil)
module := component.NewModule(p.app, "/")
modules := []component.Module{module}
m.On("Modules", p.app, "default").Return(modules, nil)
m.On("Module", p.app, "/").Return(module, nil)
a.On("EnvironmentParams", "default").Return("{}", nil)
m.On("Components", ns).Return(components, nil)
m.On("Components", p.app, "/").Return(components, nil)
got, err := p.Components([]string{"cpnt1"})
require.NoError(t, err)
......@@ -149,7 +147,6 @@ func TestPipeline_Objects(t *testing.T) {
modules := []component.Module{module}
m.On("Modules", p.app, "default").Return(modules, nil)
m.On("Module", p.app, "/").Return(module, nil)
m.On("NSResolveParams", module).Return("", nil)
a.On("EnvironmentParams", "default").Return("{}", nil)
env := &app.EnvironmentConfig{Path: "default"}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment