Reduce the number of unnecessary document.NewBundleByPath() calls

Since document.NewBundleByPath() is pretty resource and time
consuming call (because it invokes kustomize under the hood),
we should avoid unnecessary calls of this function. There are
lots of the same constructor calls of new bundle from phase
bundle root path, mostly in helper, which slows airshipctl
and makes no sense. This patch fixes this problem by storing
phase root bundle variable in helper, which creates only once
during airshipctl execution.

Change-Id: I6b59d440f7ab7a68dc613091dafcc9e82df10eb7
Signed-off-by: Ruslan Aliev <raliev@mirantis.com>
Closes: #488
This commit is contained in:
Ruslan Aliev 2021-03-15 03:25:41 -05:00
parent 3749a8fe50
commit e9041a2a22
27 changed files with 250 additions and 95 deletions

View File

@ -78,22 +78,17 @@ func (cmd *GetKubeconfigCommand) RunE(cfgFactory config.Factory, writer io.Write
return err return err
} }
wd, err := helper.WorkDir()
if err != nil {
return err
}
client, err := client.NewClient(helper.TargetPath(), log.DebugEnabled(), airshipv1.DefaultClusterctl()) client, err := client.NewClient(helper.TargetPath(), log.DebugEnabled(), airshipv1.DefaultClusterctl())
if err != nil { if err != nil {
return err return err
} }
kubeconf := kubeconfig.NewBuilder(). kubeconf := kubeconfig.NewBuilder().
WithBundle(helper.PhaseBundleRoot()). WithBundle(helper.PhaseConfigBundle()).
WithClusterctClient(client). WithClusterctClient(client).
WithClusterMap(cMap). WithClusterMap(cMap).
WithClusterName(cmd.ClusterName). WithClusterName(cmd.ClusterName).
WithTempRoot(wd). WithTempRoot(helper.WorkDir()).
Build() Build()
return kubeconf.Write(writer) return kubeconf.Write(writer)

0
pkg/container/testdata/kustomization.yaml vendored Executable file
View File

View File

@ -23,6 +23,7 @@ import (
"opendev.org/airship/airshipctl/pkg/api/v1alpha1" "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/cluster/clustermap" "opendev.org/airship/airshipctl/pkg/cluster/clustermap"
"opendev.org/airship/airshipctl/pkg/clusterctl/client" "opendev.org/airship/airshipctl/pkg/clusterctl/client"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/fs" "opendev.org/airship/airshipctl/pkg/fs"
"opendev.org/airship/airshipctl/pkg/log" "opendev.org/airship/airshipctl/pkg/log"
) )
@ -40,19 +41,19 @@ func NewBuilder() *Builder {
// Builder is an object that allows to build a kubeconfig based on various provided sources // Builder is an object that allows to build a kubeconfig based on various provided sources
// such as path to kubeconfig, path to bundle that should contain kubeconfig and parent cluster // such as path to kubeconfig, path to bundle that should contain kubeconfig and parent cluster
type Builder struct { type Builder struct {
bundlePath string
clusterName string clusterName string
root string root string
bundle document.Bundle
clusterMap clustermap.ClusterMap clusterMap clustermap.ClusterMap
clusterctlClient client.Interface clusterctlClient client.Interface
fs fs.FileSystem fs fs.FileSystem
siteKubeconf *api.Config siteKubeconf *api.Config
} }
// WithBundle allows to set path to bundle that should contain kubeconfig api object // WithBundle allows to set document.Bundle object that should contain kubeconfig api object
func (b *Builder) WithBundle(bundlePath string) *Builder { func (b *Builder) WithBundle(bundle document.Bundle) *Builder {
b.bundlePath = bundlePath b.bundle = bundle
return b return b
} }
@ -181,7 +182,7 @@ func (b *Builder) trySource(clusterID, dstContext string, source v1alpha1.Kubeco
getter = FromFile(source.FileSystem.Path, b.fs) getter = FromFile(source.FileSystem.Path, b.fs)
sourceContext = source.FileSystem.Context sourceContext = source.FileSystem.Context
case v1alpha1.KubeconfigSourceTypeBundle: case v1alpha1.KubeconfigSourceTypeBundle:
getter = FromBundle(b.bundlePath) getter = FromBundle(b.bundle)
sourceContext = source.Bundle.Context sourceContext = source.Bundle.Context
case v1alpha1.KubeconfigSourceTypeClusterAPI: case v1alpha1.KubeconfigSourceTypeClusterAPI:
getter = b.fromClusterAPI(clusterID, source.ClusterAPI) getter = b.fromClusterAPI(clusterID, source.ClusterAPI)

View File

@ -27,6 +27,7 @@ import (
"opendev.org/airship/airshipctl/pkg/api/v1alpha1" "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/cluster/clustermap" "opendev.org/airship/airshipctl/pkg/cluster/clustermap"
"opendev.org/airship/airshipctl/pkg/clusterctl/client" "opendev.org/airship/airshipctl/pkg/clusterctl/client"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/fs" "opendev.org/airship/airshipctl/pkg/fs"
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig" "opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
"opendev.org/airship/airshipctl/testutil/clusterctl" "opendev.org/airship/airshipctl/testutil/clusterctl"
@ -85,8 +86,9 @@ func TestBuilderClusterctl(t *testing.T) {
parentUser := "parent_admin" parentUser := "parent_admin"
parentParentUser := "parent_parent_admin" parentParentUser := "parent_parent_admin"
childUser := "child_user" childUser := "child_user"
testBundlePath := "testdata" testBundle, err := document.NewBundleByPath("testdata")
kubeconfigPath := filepath.Join(testBundlePath, "kubeconfig-12341234") require.NoError(t, err)
kubeconfigPath := filepath.Join("testdata", "kubeconfig-12341234")
tests := []struct { tests := []struct {
name string name string
@ -259,7 +261,7 @@ func TestBuilderClusterctl(t *testing.T) {
kube := kubeconfig.NewBuilder(). kube := kubeconfig.NewBuilder().
WithClusterMap(tt.clusterMap). WithClusterMap(tt.clusterMap).
WithClusterName(tt.requestedClusterName). WithClusterName(tt.requestedClusterName).
WithBundle(testBundlePath). WithBundle(testBundle).
WithTempRoot(tt.tempRoot). WithTempRoot(tt.tempRoot).
WithClusterctClient(tt.clusterctlClient). WithClusterctClient(tt.clusterctlClient).
WithFilesytem(tt.fs). WithFilesytem(tt.fs).

View File

@ -122,20 +122,15 @@ func FromFile(path string, fSys fs.FileSystem) KubeSourceFunc {
} }
// FromBundle returns KubeSource type, uses path to document bundle to find kubeconfig // FromBundle returns KubeSource type, uses path to document bundle to find kubeconfig
func FromBundle(root string) KubeSourceFunc { func FromBundle(bundle document.Bundle) KubeSourceFunc {
return func() ([]byte, error) { return func() ([]byte, error) {
docBundle, err := document.NewBundleByPath(root)
if err != nil {
return nil, err
}
config := &v1alpha1.KubeConfig{} config := &v1alpha1.KubeConfig{}
selector, err := document.NewSelector().ByObject(config, v1alpha1.Scheme) selector, err := document.NewSelector().ByObject(config, v1alpha1.Scheme)
if err != nil { if err != nil {
return nil, err return nil, err
} }
doc, err := docBundle.SelectOne(selector) doc, err := bundle.SelectOne(selector)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -29,6 +29,7 @@ import (
"opendev.org/airship/airshipctl/pkg/api/v1alpha1" "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/clusterctl/client" "opendev.org/airship/airshipctl/pkg/clusterctl/client"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/fs" "opendev.org/airship/airshipctl/pkg/fs"
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig" "opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
testfs "opendev.org/airship/airshipctl/testutil/fs" testfs "opendev.org/airship/airshipctl/testutil/fs"
@ -184,14 +185,14 @@ func TestFromBundle(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
kubeconf, err := kubeconfig.FromBundle(tt.rootPath)() bundle, err := document.NewBundleByPath(tt.rootPath)
if tt.shouldFail { if tt.shouldFail {
require.Error(t, err) require.Error(t, err)
assert.Nil(t, kubeconf) return
} else {
require.NoError(t, err)
assert.Contains(t, string(kubeconf), tt.expectedContains)
} }
kubeconf, err := kubeconfig.FromBundle(bundle)()
require.NoError(t, err)
assert.Contains(t, string(kubeconf), tt.expectedContains)
}) })
} }
} }

View File

@ -87,11 +87,6 @@ func (p *phase) Executor() (ifc.Executor, error) {
return nil, err return nil, err
} }
wd, err := p.helper.WorkDir()
if err != nil {
return nil, err
}
cctlClient, err := cctlclient.NewClient( cctlClient, err := cctlclient.NewClient(
p.helper.PhaseBundleRoot(), p.helper.PhaseBundleRoot(),
log.DebugEnabled(), log.DebugEnabled(),
@ -101,9 +96,9 @@ func (p *phase) Executor() (ifc.Executor, error) {
} }
kubeconf := kubeconfig.NewBuilder(). kubeconf := kubeconfig.NewBuilder().
WithBundle(p.helper.PhaseBundleRoot()). WithBundle(p.helper.PhaseConfigBundle()).
WithClusterMap(cMap). WithClusterMap(cMap).
WithTempRoot(wd). WithTempRoot(p.helper.WorkDir()).
WithClusterctClient(cctlClient). WithClusterctClient(cctlClient).
Build() Build()

View File

@ -179,18 +179,14 @@ func (c *ContainerExecutor) Render(_ io.Writer, _ ifc.RenderOptions) error {
func (c *ContainerExecutor) setConfig() error { func (c *ContainerExecutor) setConfig() error {
if c.Container.ConfigRef != nil { if c.Container.ConfigRef != nil {
log.Printf("Config reference is specified, looking for the object in config ref: '%v'", c.Container.ConfigRef) log.Debugf("Config reference is specified, looking for the object in config ref: '%v'", c.Container.ConfigRef)
log.Printf("using bundle root %s", c.Helper.PhaseBundleRoot()) log.Debugf("using bundle root %s", c.Helper.PhaseBundleRoot())
bundle, err := document.NewBundleByPath(c.Helper.PhaseBundleRoot())
if err != nil {
return err
}
gvk := c.Container.ConfigRef.GroupVersionKind() gvk := c.Container.ConfigRef.GroupVersionKind()
selector := document.NewSelector(). selector := document.NewSelector().
ByName(c.Container.ConfigRef.Name). ByName(c.Container.ConfigRef.Name).
ByNamespace(c.Container.ConfigRef.Namespace). ByNamespace(c.Container.ConfigRef.Namespace).
ByGvk(gvk.Group, gvk.Version, gvk.Kind) ByGvk(gvk.Group, gvk.Version, gvk.Kind)
doc, err := bundle.SelectOne(selector) doc, err := c.Helper.PhaseConfigBundle().SelectOne(selector)
if err != nil { if err != nil {
return err return err
} }

View File

@ -38,17 +38,16 @@ type Helper struct {
targetPath string targetPath string
phaseRepoDir string phaseRepoDir string
phaseEntryPointBasePath string phaseEntryPointBasePath string
workDir string
inventory inventoryifc.Inventory inventory inventoryifc.Inventory
metadata *config.Metadata metadata *config.Metadata
config *config.Config phaseConfigBundle document.Bundle
} }
// NewHelper constructs metadata interface based on config // NewHelper constructs metadata interface based on config
func NewHelper(cfg *config.Config) (ifc.Helper, error) { func NewHelper(cfg *config.Config) (ifc.Helper, error) {
helper := &Helper{ helper := &Helper{}
config: cfg,
}
var err error var err error
helper.targetPath, err = cfg.CurrentContextTargetPath() helper.targetPath, err = cfg.CurrentContextTargetPath()
@ -63,20 +62,23 @@ func NewHelper(cfg *config.Config) (ifc.Helper, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
helper.workDir, err = cfg.WorkDir()
if err != nil {
return nil, err
}
helper.phaseBundleRoot = filepath.Join(helper.targetPath, helper.phaseRepoDir, helper.metadata.PhaseMeta.Path) helper.phaseBundleRoot = filepath.Join(helper.targetPath, helper.phaseRepoDir, helper.metadata.PhaseMeta.Path)
helper.inventoryRoot = filepath.Join(helper.targetPath, helper.phaseRepoDir, helper.metadata.Inventory.Path) helper.inventoryRoot = filepath.Join(helper.targetPath, helper.phaseRepoDir, helper.metadata.Inventory.Path)
helper.phaseEntryPointBasePath = filepath.Join(helper.targetPath, helper.phaseRepoDir, helper.phaseEntryPointBasePath = filepath.Join(helper.targetPath, helper.phaseRepoDir,
helper.metadata.PhaseMeta.DocEntryPointPrefix) helper.metadata.PhaseMeta.DocEntryPointPrefix)
helper.inventory = inventory.NewInventory(func() (*config.Config, error) { return cfg, nil }) helper.inventory = inventory.NewInventory(func() (*config.Config, error) { return cfg, nil })
if helper.phaseConfigBundle, err = document.NewBundleByPath(helper.phaseBundleRoot); err != nil {
return nil, err
}
return helper, nil return helper, nil
} }
// Phase returns a phase APIObject based on phase selector // Phase returns a phase APIObject based on phase selector
func (helper *Helper) Phase(phaseID ifc.ID) (*v1alpha1.Phase, error) { func (helper *Helper) Phase(phaseID ifc.ID) (*v1alpha1.Phase, error) {
bundle, err := document.NewBundleByPath(helper.phaseBundleRoot)
if err != nil {
return nil, err
}
phase := &v1alpha1.Phase{ phase := &v1alpha1.Phase{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: v1.ObjectMeta{
Name: phaseID.Name, Name: phaseID.Name,
@ -87,7 +89,7 @@ func (helper *Helper) Phase(phaseID ifc.ID) (*v1alpha1.Phase, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
doc, err := bundle.SelectOne(selector) doc, err := helper.phaseConfigBundle.SelectOne(selector)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -101,11 +103,6 @@ func (helper *Helper) Phase(phaseID ifc.ID) (*v1alpha1.Phase, error) {
// Plan returns plan associated with a manifest // Plan returns plan associated with a manifest
func (helper *Helper) Plan(planID ifc.ID) (*v1alpha1.PhasePlan, error) { func (helper *Helper) Plan(planID ifc.ID) (*v1alpha1.PhasePlan, error) {
bundle, err := document.NewBundleByPath(helper.phaseBundleRoot)
if err != nil {
return nil, err
}
plan := &v1alpha1.PhasePlan{ plan := &v1alpha1.PhasePlan{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: v1.ObjectMeta{
Name: planID.Name, Name: planID.Name,
@ -117,7 +114,7 @@ func (helper *Helper) Plan(planID ifc.ID) (*v1alpha1.PhasePlan, error) {
return nil, err return nil, err
} }
doc, err := bundle.SelectOne(selector) doc, err := helper.phaseConfigBundle.SelectOne(selector)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -130,18 +127,13 @@ func (helper *Helper) Plan(planID ifc.ID) (*v1alpha1.PhasePlan, error) {
// ListPhases returns all phases associated with manifest // ListPhases returns all phases associated with manifest
func (helper *Helper) ListPhases(o ifc.ListPhaseOptions) ([]*v1alpha1.Phase, error) { func (helper *Helper) ListPhases(o ifc.ListPhaseOptions) ([]*v1alpha1.Phase, error) {
bundle, err := document.NewBundleByPath(helper.phaseBundleRoot)
if err != nil {
return nil, err
}
phase := &v1alpha1.Phase{} phase := &v1alpha1.Phase{}
selector, err := document.NewSelector().ByObject(phase, v1alpha1.Scheme) selector, err := document.NewSelector().ByObject(phase, v1alpha1.Scheme)
if err != nil { if err != nil {
return nil, err return nil, err
} }
bundle, err = bundle.SelectBundle(selector) bundle, err := helper.phaseConfigBundle.SelectBundle(selector)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -210,18 +202,13 @@ func (helper *Helper) getDocsByPhasePlan(planID ifc.ID, bundle document.Bundle)
// ListPlans returns all phases associated with manifest // ListPlans returns all phases associated with manifest
func (helper *Helper) ListPlans() ([]*v1alpha1.PhasePlan, error) { func (helper *Helper) ListPlans() ([]*v1alpha1.PhasePlan, error) {
bundle, err := document.NewBundleByPath(helper.phaseBundleRoot)
if err != nil {
return nil, err
}
plan := &v1alpha1.PhasePlan{} plan := &v1alpha1.PhasePlan{}
selector, err := document.NewSelector().ByObject(plan, v1alpha1.Scheme) selector, err := document.NewSelector().ByObject(plan, v1alpha1.Scheme)
if err != nil { if err != nil {
return nil, err return nil, err
} }
docs, err := bundle.Select(selector) docs, err := helper.phaseConfigBundle.Select(selector)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -239,18 +226,13 @@ func (helper *Helper) ListPlans() ([]*v1alpha1.PhasePlan, error) {
// ClusterMapAPIobj associated with the the manifest // ClusterMapAPIobj associated with the the manifest
func (helper *Helper) ClusterMapAPIobj() (*v1alpha1.ClusterMap, error) { func (helper *Helper) ClusterMapAPIobj() (*v1alpha1.ClusterMap, error) {
bundle, err := document.NewBundleByPath(helper.phaseBundleRoot)
if err != nil {
return nil, err
}
cMap := v1alpha1.DefaultClusterMap() cMap := v1alpha1.DefaultClusterMap()
selector, err := document.NewSelector().ByObject(cMap, v1alpha1.Scheme) selector, err := document.NewSelector().ByObject(cMap, v1alpha1.Scheme)
if err != nil { if err != nil {
return nil, err return nil, err
} }
doc, err := bundle.SelectOne(selector) doc, err := helper.phaseConfigBundle.SelectOne(selector)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -272,10 +254,6 @@ func (helper *Helper) ClusterMap() (clustermap.ClusterMap, error) {
// ExecutorDoc returns executor document associated with phase // ExecutorDoc returns executor document associated with phase
func (helper *Helper) ExecutorDoc(phaseID ifc.ID) (document.Document, error) { func (helper *Helper) ExecutorDoc(phaseID ifc.ID) (document.Document, error) {
bundle, err := document.NewBundleByPath(helper.phaseBundleRoot)
if err != nil {
return nil, err
}
phaseObj, err := helper.Phase(phaseID) phaseObj, err := helper.Phase(phaseID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -293,7 +271,7 @@ func (helper *Helper) ExecutorDoc(phaseID ifc.ID) (document.Document, error) {
ByGvk(refGVK.Group, refGVK.Version, refGVK.Kind). ByGvk(refGVK.Group, refGVK.Version, refGVK.Kind).
ByName(phaseConfig.ExecutorRef.Name). ByName(phaseConfig.ExecutorRef.Name).
ByNamespace(phaseConfig.ExecutorRef.Namespace) ByNamespace(phaseConfig.ExecutorRef.Namespace)
return bundle.SelectOne(selector) return helper.phaseConfigBundle.SelectOne(selector)
} }
// TargetPath returns manifest root // TargetPath returns manifest root
@ -324,12 +302,17 @@ func (helper *Helper) PhaseEntryPointBasePath() string {
return helper.phaseEntryPointBasePath return helper.phaseEntryPointBasePath
} }
// WorkDir return working directory for aisrhipctl, creates it, if doesn't exist // WorkDir return working directory for airshipctl
func (helper *Helper) WorkDir() (string, error) { func (helper *Helper) WorkDir() string {
return helper.config.WorkDir() return helper.workDir
} }
// Inventory return inventory interface // Inventory return inventory interface
func (helper *Helper) Inventory() (inventoryifc.Inventory, error) { func (helper *Helper) Inventory() (inventoryifc.Inventory, error) {
return helper.inventory, nil return helper.inventory, nil
} }
// PhaseConfigBundle returns bundle based on phaseBundleRoot
func (helper *Helper) PhaseConfigBundle() document.Bundle {
return helper.phaseConfigBundle
}

View File

@ -41,6 +41,7 @@ func TestHelperPhase(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
errContains string errContains string
helperErr bool
phaseID ifc.ID phaseID ifc.ID
config func(t *testing.T) *config.Config config func(t *testing.T) *config.Config
@ -82,6 +83,7 @@ func TestHelperPhase(t *testing.T) {
return conf return conf
}, },
errContains: "no such file or directory", errContains: "no such file or directory",
helperErr: true,
}, },
} }
@ -89,9 +91,11 @@ func TestHelperPhase(t *testing.T) {
tt := test tt := test
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
helper, err := phase.NewHelper(tt.config(t)) helper, err := phase.NewHelper(tt.config(t))
require.NoError(t, err) if tt.helperErr {
require.Error(t, err)
return
}
require.NotNil(t, helper) require.NotNil(t, helper)
actualPhase, actualErr := helper.Phase(tt.phaseID) actualPhase, actualErr := helper.Phase(tt.phaseID)
if tt.errContains != "" { if tt.errContains != "" {
require.Error(t, actualErr) require.Error(t, actualErr)
@ -111,6 +115,7 @@ func TestHelperPlan(t *testing.T) {
errContains string errContains string
expectedPlan *v1alpha1.PhasePlan expectedPlan *v1alpha1.PhasePlan
config func(t *testing.T) *config.Config config func(t *testing.T) *config.Config
bundleErr bool
}{ }{
{ {
name: "Valid Phase Plan", name: "Valid Phase Plan",
@ -156,6 +161,7 @@ func TestHelperPlan(t *testing.T) {
return conf return conf
}, },
errContains: "no such file or directory", errContains: "no such file or directory",
bundleErr: true,
}, },
} }
@ -163,6 +169,10 @@ func TestHelperPlan(t *testing.T) {
tt := test tt := test
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
helper, err := phase.NewHelper(tt.config(t)) helper, err := phase.NewHelper(tt.config(t))
if tt.bundleErr {
require.Error(t, err)
return
}
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, helper) require.NotNil(t, helper)
@ -185,6 +195,7 @@ func TestHelperListPhases(t *testing.T) {
phaseLen int phaseLen int
config func(t *testing.T) *config.Config config func(t *testing.T) *config.Config
options ifc.ListPhaseOptions options ifc.ListPhaseOptions
bundleErr bool
}{ }{
{ {
name: "Success phase list", name: "Success phase list",
@ -199,6 +210,7 @@ func TestHelperListPhases(t *testing.T) {
return conf return conf
}, },
errContains: "no such file or directory", errContains: "no such file or directory",
bundleErr: true,
}, },
{ {
name: "Success 0 phases", name: "Success 0 phases",
@ -234,6 +246,10 @@ func TestHelperListPhases(t *testing.T) {
tt := test tt := test
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
helper, err := phase.NewHelper(tt.config(t)) helper, err := phase.NewHelper(tt.config(t))
if tt.bundleErr {
require.Error(t, err)
return
}
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, helper) require.NotNil(t, helper)
@ -253,6 +269,7 @@ func TestHelperListPlans(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
errContains string errContains string
bundleErr bool
expectedLen int expectedLen int
config func(t *testing.T) *config.Config config func(t *testing.T) *config.Config
}{ }{
@ -269,6 +286,7 @@ func TestHelperListPlans(t *testing.T) {
return conf return conf
}, },
errContains: "no such file or directory", errContains: "no such file or directory",
bundleErr: true,
}, },
{ {
name: "Success 0 plans", name: "Success 0 plans",
@ -285,6 +303,10 @@ func TestHelperListPlans(t *testing.T) {
tt := test tt := test
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
helper, err := phase.NewHelper(tt.config(t)) helper, err := phase.NewHelper(tt.config(t))
if tt.bundleErr {
require.Error(t, err)
return
}
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, helper) require.NotNil(t, helper)
@ -304,6 +326,7 @@ func TestHelperClusterMapAPI(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
errContains string errContains string
helperErr bool
expectedCMap *v1alpha1.ClusterMap expectedCMap *v1alpha1.ClusterMap
config func(t *testing.T) *config.Config config func(t *testing.T) *config.Config
}{ }{
@ -345,6 +368,7 @@ func TestHelperClusterMapAPI(t *testing.T) {
return conf return conf
}, },
errContains: "no such file or directory", errContains: "no such file or directory",
helperErr: true,
}, },
{ {
name: "Error no cluster map", name: "Error no cluster map",
@ -361,6 +385,10 @@ func TestHelperClusterMapAPI(t *testing.T) {
tt := test tt := test
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
helper, err := phase.NewHelper(tt.config(t)) helper, err := phase.NewHelper(tt.config(t))
if tt.helperErr {
require.Error(t, err)
return
}
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, helper) require.NotNil(t, helper)
@ -421,6 +449,7 @@ func TestHelperExecutorDoc(t *testing.T) {
name string name string
errContains string errContains string
expectedExecutor string expectedExecutor string
helperErr bool
phaseID ifc.ID phaseID ifc.ID
config func(t *testing.T) *config.Config config func(t *testing.T) *config.Config
@ -445,6 +474,7 @@ func TestHelperExecutorDoc(t *testing.T) {
return conf return conf
}, },
errContains: "no such file or directory", errContains: "no such file or directory",
helperErr: true,
}, },
{ {
name: "Error get phase without executor", name: "Error get phase without executor",
@ -460,6 +490,10 @@ func TestHelperExecutorDoc(t *testing.T) {
tt := test tt := test
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
helper, err := phase.NewHelper(tt.config(t)) helper, err := phase.NewHelper(tt.config(t))
if tt.helperErr {
require.Error(t, err)
return
}
require.NoError(t, err) require.NoError(t, err)
actualDoc, actualErr := helper.ExecutorDoc(tt.phaseID) actualDoc, actualErr := helper.ExecutorDoc(tt.phaseID)
@ -533,8 +567,7 @@ func TestHelperWorkdir(t *testing.T) {
helper, err := phase.NewHelper(testConfig(t)) helper, err := phase.NewHelper(testConfig(t))
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, helper) require.NotNil(t, helper)
workDir, err := helper.WorkDir() workDir := helper.WorkDir()
assert.NoError(t, err)
assert.Greater(t, len(workDir), 0) assert.Greater(t, len(workDir), 0)
} }

View File

@ -26,7 +26,7 @@ type Helper interface {
TargetPath() string TargetPath() string
PhaseRepoDir() string PhaseRepoDir() string
DocEntryPointPrefix() string DocEntryPointPrefix() string
WorkDir() (string, error) WorkDir() string
Phase(phaseID ID) (*v1alpha1.Phase, error) Phase(phaseID ID) (*v1alpha1.Phase, error)
Plan(planID ID) (*v1alpha1.PhasePlan, error) Plan(planID ID) (*v1alpha1.PhasePlan, error)
ListPhases(o ListPhaseOptions) ([]*v1alpha1.Phase, error) ListPhases(o ListPhaseOptions) ([]*v1alpha1.Phase, error)
@ -37,4 +37,5 @@ type Helper interface {
PhaseBundleRoot() string PhaseBundleRoot() string
Inventory() (ifc.Inventory, error) Inventory() (ifc.Inventory, error)
PhaseEntryPointBasePath() string PhaseEntryPointBasePath() string
PhaseConfigBundle() document.Bundle
} }

View File

@ -104,12 +104,7 @@ func (fo *RenderCommand) RunE(cfgFactory config.Factory, out io.Writer) error {
} }
func renderConfigBundle(out io.Writer, h ifc.Helper, sel document.Selector) error { func renderConfigBundle(out io.Writer, h ifc.Helper, sel document.Selector) error {
bundle, err := document.NewBundleByPath(h.PhaseBundleRoot()) bundle, err := h.PhaseConfigBundle().SelectBundle(sel)
if err != nil {
return err
}
bundle, err = bundle.SelectBundle(sel)
if err != nil { if err != nil {
return err return err
} }

View File

@ -0,0 +1,4 @@
inventory:
path: "manifests/site/inventory"
phase:
path: "valid_site/phases"

View File

@ -0,0 +1,10 @@
apiVersion: airshipit.org/v1alpha1
kind: Phase
metadata:
name: capi_init
config:
executorRef:
apiVersion: airshipit.org/v1alpha1
kind: Clusterctl
name: clusterctl-v1
documentEntryPoint: valid_site/phases

View File

@ -0,0 +1,12 @@
apiVersion: airshipit.org/v1alpha1
kind: ClusterMap
metadata:
name: clusterctl-v1
map:
target:
parent: ephemeral
kubeconfigSources:
- type: bundle
ephemeral:
kubeconfigSources:
- type: bundle

View File

@ -0,0 +1,12 @@
apiVersion: airshipit.org/v1alpha1
kind: Clusterctl
metadata:
name: clusterctl-v1
action: init
init-options:
core-provider: "cluster-api:v0.3.3"
providers:
- name: "cluster-api"
type: "CoreProvider"
versions:
v0.3.3: manifests/function/capi/v0.3.3

View File

@ -0,0 +1,10 @@
apiVersion: airshipit.org/v1alpha1
kind: Phase
metadata:
name: kube_apply
config:
executorRef:
apiVersion: airshipit.org/v1alpha1
kind: KubernetesApply
name: kubernetes-apply
documentEntryPoint: valid_site/phases

View File

@ -0,0 +1,12 @@
---
apiVersion: airshipit.org/v1alpha1
kind: KubernetesApply
metadata:
labels:
airshipit.org/deploy-k8s: "false"
name: kubernetes-apply
config:
waitOptions:
timeout: 600
pruneOptions:
prune: false

View File

@ -0,0 +1,11 @@
resources:
- phaseplan.yaml
- some_phase.yaml
- some_exc.yaml
- capi_init.yaml
- clusterctl.yaml
- kubernetes_apply.yaml
- cluster_map.yaml
- phase_no_docentrypoint.yaml
- no_executor_phase.yaml
- kubeapply_phase.yaml

View File

@ -0,0 +1,7 @@
apiVersion: airshipit.org/v1alpha1
kind: Phase
metadata:
name: no_executor_phase
config:
documentEntryPoint: valid_site/phases

View File

@ -0,0 +1,9 @@
apiVersion: airshipit.org/v1alpha1
kind: Phase
metadata:
name: no_entry_point
config:
executorRef:
apiVersion: airshipit.org/v1alpha1
kind: SomeExecutor
name: executor-name

View File

@ -0,0 +1,37 @@
apiVersion: airshipit.org/v1alpha1
kind: PhasePlan
metadata:
name: phasePlan
phases:
- name: remotedirect
- name: initinfra
- name: some_phase
- name: capi_init
---
apiVersion: airshipit.org/v1alpha1
kind: PhasePlan
metadata:
name: init
phases:
- name: capi_init
---
apiVersion: airshipit.org/v1alpha1
kind: PhasePlan
metadata:
name: some_plan
phases:
- name: some_phase
---
apiVersion: airshipit.org/v1alpha1
kind: PhasePlan
metadata:
name: plan_invalid_phase
phases:
- name: no_entry_point
---
apiVersion: airshipit.org/v1alpha1
kind: PhasePlan
metadata:
name: phase_not_exist
phases:
- name: non_existent_name

View File

@ -0,0 +1,13 @@
apiVersion: airshipit.org/v1alpha1
kind: SomeExecutor
metadata:
labels:
airshipit.org/deploy-k8s: "false"
name: executor-name
init-options:
core-provider: "cluster-api:v0.3.3"
providers:
- name: "cluster-api"
type: "CoreProvider"
versions:
v0.3.3: manifests/function/capi/v0.3.3

View File

@ -0,0 +1,10 @@
apiVersion: airshipit.org/v1alpha1
kind: Phase
metadata:
name: some_phase
clusterName: some_cluster
config:
executorRef:
apiVersion: airshipit.org/v1alpha1
kind: Does not exist
name: executor-name

View File

@ -0,0 +1,3 @@
phase:
path: "valid_site_with_doc_prefix/phases"
docEntryPointPrefix: "valid_site_with_doc_prefix/phases"

View File

@ -0,0 +1,2 @@
resources:
- sample.yaml

View File

@ -0,0 +1,6 @@
apiVersion: airshipit.org/v1alpha1
kind: Phase
metadata:
name: sample
config:
documentEntryPoint: entrypoint