Implement plan run command
Change-Id: Ie627ce670cd2b19d6999dc7c7a7a6dc12b25cace Closes: #395
This commit is contained in:
parent
4cba00d98c
commit
178b0eff3e
@ -18,7 +18,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"opendev.org/airship/airshipctl/pkg/config"
|
"opendev.org/airship/airshipctl/pkg/config"
|
||||||
"opendev.org/airship/airshipctl/pkg/errors"
|
"opendev.org/airship/airshipctl/pkg/phase"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -29,14 +29,37 @@ Run life-cycle phase plan which was defined in document model.
|
|||||||
|
|
||||||
// NewRunCommand creates a command which execute a particular phase plan
|
// NewRunCommand creates a command which execute a particular phase plan
|
||||||
func NewRunCommand(cfgFactory config.Factory) *cobra.Command {
|
func NewRunCommand(cfgFactory config.Factory) *cobra.Command {
|
||||||
listCmd := &cobra.Command{
|
r := &phase.PlanRunCommand{
|
||||||
|
Factory: cfgFactory,
|
||||||
|
Options: phase.PlanRunFlags{},
|
||||||
|
}
|
||||||
|
runCmd := &cobra.Command{
|
||||||
Use: "run PLAN_NAME",
|
Use: "run PLAN_NAME",
|
||||||
Short: "Run plan",
|
Short: "Run plan",
|
||||||
Long: runLong[1:],
|
Long: runLong[1:],
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return errors.ErrNotImplemented{What: "airshipctl plan run"}
|
r.Options.PlanID.Name = args[0]
|
||||||
|
return r.RunE()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return listCmd
|
|
||||||
|
flags := runCmd.Flags()
|
||||||
|
flags.BoolVar(
|
||||||
|
&r.Options.DryRun,
|
||||||
|
"dry-run",
|
||||||
|
false,
|
||||||
|
"simulate phase execution")
|
||||||
|
flags.DurationVar(
|
||||||
|
&r.Options.Timeout,
|
||||||
|
"wait-timeout",
|
||||||
|
0,
|
||||||
|
"wait timeout")
|
||||||
|
flags.StringVar(
|
||||||
|
&r.Options.Kubeconfig,
|
||||||
|
"kubeconfig",
|
||||||
|
"",
|
||||||
|
"Path to kubeconfig associated with site being managed")
|
||||||
|
|
||||||
|
return runCmd
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,7 @@ Usage:
|
|||||||
run PLAN_NAME [flags]
|
run PLAN_NAME [flags]
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
|
--dry-run simulate phase execution
|
||||||
-h, --help help for run
|
-h, --help help for run
|
||||||
|
--kubeconfig string Path to kubeconfig associated with site being managed
|
||||||
|
--wait-timeout duration wait timeout
|
||||||
|
@ -14,7 +14,10 @@ airshipctl plan run PLAN_NAME [flags]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
|
--dry-run simulate phase execution
|
||||||
-h, --help help for run
|
-h, --help help for run
|
||||||
|
--kubeconfig string Path to kubeconfig associated with site being managed
|
||||||
|
--wait-timeout duration wait timeout
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
@ -788,8 +788,6 @@ apiVersion: airshipit.org/v1alpha1
|
|||||||
kind: PhasePlan
|
kind: PhasePlan
|
||||||
metadata:
|
metadata:
|
||||||
name: phasePlan
|
name: phasePlan
|
||||||
phaseGroups:
|
|
||||||
- name: group1
|
|
||||||
phases:
|
phases:
|
||||||
- name: clusterctl-init-ephemeral
|
- name: clusterctl-init-ephemeral
|
||||||
- name: controlplane-ephemeral
|
- name: controlplane-ephemeral
|
||||||
|
@ -3,8 +3,6 @@ kind: PhasePlan
|
|||||||
metadata:
|
metadata:
|
||||||
name: phasePlan
|
name: phasePlan
|
||||||
description: "Default phase plan"
|
description: "Default phase plan"
|
||||||
phaseGroups:
|
|
||||||
- name: group1
|
|
||||||
phases:
|
phases:
|
||||||
- name: initinfra-ephemeral
|
- name: initinfra-ephemeral
|
||||||
- name: initinfra-networking-ephemeral
|
- name: initinfra-networking-ephemeral
|
||||||
|
@ -2,8 +2,6 @@ apiVersion: airshipit.org/v1alpha1
|
|||||||
kind: PhasePlan
|
kind: PhasePlan
|
||||||
metadata:
|
metadata:
|
||||||
name: phasePlan
|
name: phasePlan
|
||||||
phaseGroups:
|
|
||||||
- name: group1
|
|
||||||
phases:
|
phases:
|
||||||
- name: clusterctl-init-ephemeral
|
- name: clusterctl-init-ephemeral
|
||||||
- name: controlplane-ephemeral
|
- name: controlplane-ephemeral
|
||||||
|
@ -2,8 +2,6 @@ apiVersion: airshipit.org/v1alpha1
|
|||||||
kind: PhasePlan
|
kind: PhasePlan
|
||||||
metadata:
|
metadata:
|
||||||
name: phasePlan
|
name: phasePlan
|
||||||
phaseGroups:
|
|
||||||
- name: group1
|
|
||||||
phases:
|
phases:
|
||||||
- name: clusterctl-init-ephemeral
|
- name: clusterctl-init-ephemeral
|
||||||
- name: controlplane-ephemeral
|
- name: controlplane-ephemeral
|
||||||
|
@ -2,8 +2,6 @@ apiVersion: airshipit.org/v1alpha1
|
|||||||
kind: PhasePlan
|
kind: PhasePlan
|
||||||
metadata:
|
metadata:
|
||||||
name: phasePlan
|
name: phasePlan
|
||||||
phaseGroups:
|
|
||||||
- name: group1
|
|
||||||
phases:
|
phases:
|
||||||
- name: clusterctl-init-ephemeral
|
- name: clusterctl-init-ephemeral
|
||||||
- name: controlplane-ephemeral
|
- name: controlplane-ephemeral
|
||||||
|
@ -2,8 +2,6 @@ apiVersion: airshipit.org/v1alpha1
|
|||||||
kind: PhasePlan
|
kind: PhasePlan
|
||||||
metadata:
|
metadata:
|
||||||
name: phasePlan
|
name: phasePlan
|
||||||
phaseGroups:
|
|
||||||
- name: group1
|
|
||||||
phases:
|
phases:
|
||||||
- name: clusterctl-init-ephemeral
|
- name: clusterctl-init-ephemeral
|
||||||
- name: controlplane-ephemeral
|
- name: controlplane-ephemeral
|
||||||
|
@ -25,17 +25,10 @@ type PhasePlan struct {
|
|||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
PhaseGroups []PhaseGroup `json:"phaseGroups,omitempty"`
|
Phases []PhaseStep `json:"phases,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PhaseGroup represents set of phases (i.e. steps) executed sequentially.
|
// PhaseStep represents phase (or step) within a phase plan
|
||||||
// Phase groups are executed simultaneously
|
type PhaseStep struct {
|
||||||
type PhaseGroup struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Phases []PhaseGroupStep `json:"phases,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PhaseGroupStep represents phase (or step) within phase group
|
|
||||||
type PhaseGroupStep struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -506,52 +506,15 @@ func (in *PhaseConfig) DeepCopy() *PhaseConfig {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *PhaseGroup) DeepCopyInto(out *PhaseGroup) {
|
|
||||||
*out = *in
|
|
||||||
if in.Phases != nil {
|
|
||||||
in, out := &in.Phases, &out.Phases
|
|
||||||
*out = make([]PhaseGroupStep, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PhaseGroup.
|
|
||||||
func (in *PhaseGroup) DeepCopy() *PhaseGroup {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(PhaseGroup)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *PhaseGroupStep) DeepCopyInto(out *PhaseGroupStep) {
|
|
||||||
*out = *in
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PhaseGroupStep.
|
|
||||||
func (in *PhaseGroupStep) DeepCopy() *PhaseGroupStep {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(PhaseGroupStep)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *PhasePlan) DeepCopyInto(out *PhasePlan) {
|
func (in *PhasePlan) DeepCopyInto(out *PhasePlan) {
|
||||||
*out = *in
|
*out = *in
|
||||||
out.TypeMeta = in.TypeMeta
|
out.TypeMeta = in.TypeMeta
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
if in.PhaseGroups != nil {
|
if in.Phases != nil {
|
||||||
in, out := &in.PhaseGroups, &out.PhaseGroups
|
in, out := &in.Phases, &out.Phases
|
||||||
*out = make([]PhaseGroup, len(*in))
|
*out = make([]PhaseStep, len(*in))
|
||||||
for i := range *in {
|
copy(*out, *in)
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,6 +536,21 @@ func (in *PhasePlan) DeepCopyObject() runtime.Object {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *PhaseStep) DeepCopyInto(out *PhaseStep) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PhaseStep.
|
||||||
|
func (in *PhaseStep) DeepCopy() *PhaseStep {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(PhaseStep)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Provider) DeepCopyInto(out *Provider) {
|
func (in *Provider) DeepCopyInto(out *Provider) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
@ -177,6 +177,34 @@ func (p *phase) Details() (string, error) {
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ ifc.Plan = &plan{}
|
||||||
|
|
||||||
|
type plan struct {
|
||||||
|
helper ifc.Helper
|
||||||
|
apiObj *v1alpha1.PhasePlan
|
||||||
|
phaseClient ifc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate makes sure that phase plan is properly configured
|
||||||
|
// TODO implement this
|
||||||
|
func (p *plan) Validate() error { return nil }
|
||||||
|
|
||||||
|
// Run function excutes Run method for each phase
|
||||||
|
func (p *plan) Run(ro ifc.RunOptions) error {
|
||||||
|
for _, step := range p.apiObj.Phases {
|
||||||
|
phaseRunner, err := p.phaseClient.PhaseByID(ifc.ID{Name: step.Name})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("executing phase: %s\n", step)
|
||||||
|
if err = phaseRunner.Run(ro); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var _ ifc.Client = &client{}
|
var _ ifc.Client = &client{}
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
@ -245,6 +273,18 @@ func (c *client) PhaseByID(id ifc.ID) (ifc.Phase, error) {
|
|||||||
return phase, nil
|
return phase, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *client) PlanByID(id ifc.ID) (ifc.Plan, error) {
|
||||||
|
planObj, err := c.Plan(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &plan{
|
||||||
|
apiObj: planObj,
|
||||||
|
helper: c.Helper,
|
||||||
|
phaseClient: c,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *client) PhaseByAPIObj(phaseObj *v1alpha1.Phase) (ifc.Phase, error) {
|
func (c *client) PhaseByAPIObj(phaseObj *v1alpha1.Phase) (ifc.Phase, error) {
|
||||||
phase := &phase{
|
phase := &phase{
|
||||||
apiObj: phaseObj,
|
apiObj: phaseObj,
|
||||||
|
@ -123,7 +123,7 @@ func TestPhaseRun(t *testing.T) {
|
|||||||
client := phase.NewClient(helper, phase.InjectRegistry(tt.registryFunc))
|
client := phase.NewClient(helper, phase.InjectRegistry(tt.registryFunc))
|
||||||
require.NotNil(t, client)
|
require.NotNil(t, client)
|
||||||
p, err := client.PhaseByID(tt.phaseID)
|
p, err := client.PhaseByID(tt.phaseID)
|
||||||
require.NotNil(t, client)
|
require.NoError(t, err)
|
||||||
err = p.Run(ifc.RunOptions{DryRun: true})
|
err = p.Run(ifc.RunOptions{DryRun: true})
|
||||||
if tt.errContains != "" {
|
if tt.errContains != "" {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
@ -223,7 +223,7 @@ func TestBundleFactoryExecutor(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, helper)
|
require.NotNil(t, helper)
|
||||||
|
|
||||||
fakeRegistry := func() map[schema.GroupVersionKind]ifc.ExecutorFactory {
|
fakeReg := func() map[schema.GroupVersionKind]ifc.ExecutorFactory {
|
||||||
validBundleFactory := schema.GroupVersionKind{
|
validBundleFactory := schema.GroupVersionKind{
|
||||||
Group: "airshipit.org",
|
Group: "airshipit.org",
|
||||||
Version: "v1alpha1",
|
Version: "v1alpha1",
|
||||||
@ -245,7 +245,7 @@ func TestBundleFactoryExecutor(t *testing.T) {
|
|||||||
invalidBundleFactory: bundleCheckFunc,
|
invalidBundleFactory: bundleCheckFunc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c := phase.NewClient(helper, phase.InjectRegistry(fakeRegistry))
|
c := phase.NewClient(helper, phase.InjectRegistry(fakeReg))
|
||||||
p, err := c.PhaseByID(ifc.ID{Name: "capi_init"})
|
p, err := c.PhaseByID(ifc.ID{Name: "capi_init"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, err = p.Executor()
|
_, err = p.Executor()
|
||||||
@ -257,6 +257,50 @@ func TestBundleFactoryExecutor(t *testing.T) {
|
|||||||
assert.Equal(t, phase.ErrDocumentEntrypointNotDefined{PhaseName: "no_entry_point"}, err)
|
assert.Equal(t, phase.ErrDocumentEntrypointNotDefined{PhaseName: "no_entry_point"}, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPlanRun(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
errContains string
|
||||||
|
planID ifc.ID
|
||||||
|
configFunc func(t *testing.T) *config.Config
|
||||||
|
registryFunc phase.ExecutorRegistry
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Success fake executor",
|
||||||
|
configFunc: testConfig,
|
||||||
|
planID: ifc.ID{Name: "init"},
|
||||||
|
registryFunc: fakeRegistry,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Error executor doc doesn't exist",
|
||||||
|
configFunc: testConfig,
|
||||||
|
planID: ifc.ID{Name: "some_plan"},
|
||||||
|
registryFunc: fakeRegistry,
|
||||||
|
errContains: "found no documents",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tt := tc
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
conf := tt.configFunc(t)
|
||||||
|
helper, err := phase.NewHelper(conf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, helper)
|
||||||
|
client := phase.NewClient(helper, phase.InjectRegistry(tt.registryFunc))
|
||||||
|
require.NotNil(t, client)
|
||||||
|
p, err := client.PlanByID(tt.planID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = p.Run(ifc.RunOptions{DryRun: true})
|
||||||
|
if tt.errContains != "" {
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), tt.errContains)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func fakeExecFactory(config ifc.ExecutorConfig) (ifc.Executor, error) {
|
func fakeExecFactory(config ifc.ExecutorConfig) (ifc.Executor, error) {
|
||||||
return fakeExecutor{}, nil
|
return fakeExecutor{}, nil
|
||||||
}
|
}
|
||||||
|
@ -32,15 +32,20 @@ import (
|
|||||||
"opendev.org/airship/airshipctl/pkg/util"
|
"opendev.org/airship/airshipctl/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunFlags options for phase run command
|
// GenericRunFlags generic options for run command
|
||||||
type RunFlags struct {
|
type GenericRunFlags struct {
|
||||||
DryRun bool
|
DryRun bool
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
PhaseID ifc.ID
|
|
||||||
Kubeconfig string
|
Kubeconfig string
|
||||||
Progress bool
|
Progress bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunFlags options for phase run command
|
||||||
|
type RunFlags struct {
|
||||||
|
GenericRunFlags
|
||||||
|
PhaseID ifc.ID
|
||||||
|
}
|
||||||
|
|
||||||
// RunCommand phase run command
|
// RunCommand phase run command
|
||||||
type RunCommand struct {
|
type RunCommand struct {
|
||||||
Options RunFlags
|
Options RunFlags
|
||||||
@ -202,3 +207,37 @@ func (c *PlanListCommand) RunE() error {
|
|||||||
printer.PrintTable(rt, 0)
|
printer.PrintTable(rt, 0)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PlanRunFlags options for phase run command
|
||||||
|
type PlanRunFlags struct {
|
||||||
|
GenericRunFlags
|
||||||
|
PlanID ifc.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlanRunCommand phase run command
|
||||||
|
type PlanRunCommand struct {
|
||||||
|
Options PlanRunFlags
|
||||||
|
Factory config.Factory
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunE executes phase plan
|
||||||
|
func (c *PlanRunCommand) RunE() error {
|
||||||
|
cfg, err := c.Factory()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
helper, err := NewHelper(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
kubeconfigOption := InjectKubeconfigPath(c.Options.Kubeconfig)
|
||||||
|
client := NewClient(helper, kubeconfigOption)
|
||||||
|
|
||||||
|
plan, err := client.PlanByID(c.Options.PlanID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return plan.Run(ifc.RunOptions{DryRun: c.Options.DryRun, Timeout: c.Options.Timeout, Progress: c.Options.Progress})
|
||||||
|
}
|
||||||
|
@ -293,3 +293,77 @@ func TestPlanListCommand(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPlanRunCommand(t *testing.T) {
|
||||||
|
testErr := fmt.Errorf(testFactoryErr)
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
factory config.Factory
|
||||||
|
expectedErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Error config factory",
|
||||||
|
factory: func() (*config.Config, error) {
|
||||||
|
return nil, testErr
|
||||||
|
},
|
||||||
|
expectedErr: testFactoryErr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Error new helper",
|
||||||
|
factory: func() (*config.Config, error) {
|
||||||
|
return &config.Config{
|
||||||
|
CurrentContext: "does not exist",
|
||||||
|
Contexts: make(map[string]*config.Context),
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
expectedErr: "Missing configuration: Context with name 'does not exist'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Error phase by id",
|
||||||
|
factory: func() (*config.Config, error) {
|
||||||
|
conf := config.NewConfig()
|
||||||
|
conf.Manifests = map[string]*config.Manifest{
|
||||||
|
"manifest": {
|
||||||
|
MetadataPath: "metadata.yaml",
|
||||||
|
TargetPath: "testdata",
|
||||||
|
PhaseRepositoryName: config.DefaultTestPhaseRepo,
|
||||||
|
Repositories: map[string]*config.Repository{
|
||||||
|
config.DefaultTestPhaseRepo: {
|
||||||
|
URLString: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
conf.CurrentContext = "context"
|
||||||
|
conf.Contexts = map[string]*config.Context{
|
||||||
|
"context": {
|
||||||
|
Manifest: "manifest",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return conf, nil
|
||||||
|
},
|
||||||
|
expectedErr: `Error events received on channel, errors are:
|
||||||
|
[document filtered by selector [Group="airshipit.org", Version="v1alpha1", Kind="KubeConfig"] found no documents]`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tt := tc
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := phase.PlanRunCommand{
|
||||||
|
Options: phase.PlanRunFlags{
|
||||||
|
GenericRunFlags: phase.GenericRunFlags{
|
||||||
|
DryRun: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Factory: tt.factory,
|
||||||
|
}
|
||||||
|
err := cmd.RunE()
|
||||||
|
if tt.expectedErr != "" {
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Equal(t, tt.expectedErr, err.Error())
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -90,13 +90,18 @@ 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() (*v1alpha1.PhasePlan, error) {
|
func (helper *Helper) Plan(planID ifc.ID) (*v1alpha1.PhasePlan, error) {
|
||||||
bundle, err := document.NewBundleByPath(helper.phaseBundleRoot)
|
bundle, err := document.NewBundleByPath(helper.phaseBundleRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
plan := &v1alpha1.PhasePlan{}
|
plan := &v1alpha1.PhasePlan{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: planID.Name,
|
||||||
|
Namespace: planID.Namespace,
|
||||||
|
},
|
||||||
|
}
|
||||||
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
|
||||||
|
@ -104,6 +104,7 @@ func TestHelperPhase(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHelperPlan(t *testing.T) {
|
func TestHelperPlan(t *testing.T) {
|
||||||
|
testPlanName := "phasePlan"
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
errContains string
|
errContains string
|
||||||
@ -119,12 +120,9 @@ func TestHelperPlan(t *testing.T) {
|
|||||||
APIVersion: "airshipit.org/v1alpha1",
|
APIVersion: "airshipit.org/v1alpha1",
|
||||||
},
|
},
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "phasePlan",
|
Name: testPlanName,
|
||||||
},
|
},
|
||||||
PhaseGroups: []airshipv1.PhaseGroup{
|
Phases: []airshipv1.PhaseStep{
|
||||||
{
|
|
||||||
Name: "group1",
|
|
||||||
Phases: []airshipv1.PhaseGroupStep{
|
|
||||||
{
|
{
|
||||||
Name: "isogen",
|
Name: "isogen",
|
||||||
},
|
},
|
||||||
@ -143,8 +141,6 @@ func TestHelperPlan(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "No Phase Plan",
|
name: "No Phase Plan",
|
||||||
config: func(t *testing.T) *config.Config {
|
config: func(t *testing.T) *config.Config {
|
||||||
@ -172,7 +168,7 @@ func TestHelperPlan(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, helper)
|
require.NotNil(t, helper)
|
||||||
|
|
||||||
actualPlan, actualErr := helper.Plan()
|
actualPlan, actualErr := helper.Plan(ifc.ID{Name: testPlanName})
|
||||||
if tt.errContains != "" {
|
if tt.errContains != "" {
|
||||||
require.Error(t, actualErr)
|
require.Error(t, actualErr)
|
||||||
assert.Contains(t, actualErr.Error(), tt.errContains)
|
assert.Contains(t, actualErr.Error(), tt.errContains)
|
||||||
@ -244,7 +240,7 @@ func TestHelperListPlans(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Success plan list",
|
name: "Success plan list",
|
||||||
expectedLen: 1,
|
expectedLen: 3,
|
||||||
config: testConfig,
|
config: testConfig,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,7 @@ type Helper interface {
|
|||||||
DocEntryPointPrefix() string
|
DocEntryPointPrefix() string
|
||||||
WorkDir() (string, error)
|
WorkDir() (string, error)
|
||||||
Phase(phaseID ID) (*v1alpha1.Phase, error)
|
Phase(phaseID ID) (*v1alpha1.Phase, error)
|
||||||
Plan() (*v1alpha1.PhasePlan, error)
|
Plan(planID ID) (*v1alpha1.PhasePlan, error)
|
||||||
ListPhases() ([]*v1alpha1.Phase, error)
|
ListPhases() ([]*v1alpha1.Phase, error)
|
||||||
ListPlans() ([]*v1alpha1.PhasePlan, error)
|
ListPlans() ([]*v1alpha1.PhasePlan, error)
|
||||||
ClusterMapAPIobj() (*v1alpha1.ClusterMap, error)
|
ClusterMapAPIobj() (*v1alpha1.ClusterMap, error)
|
||||||
|
@ -31,6 +31,12 @@ type Phase interface {
|
|||||||
Render(io.Writer, bool, RenderOptions) error
|
Render(io.Writer, bool, RenderOptions) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Plan provides a way to interact with phase plans
|
||||||
|
type Plan interface {
|
||||||
|
Validate() error
|
||||||
|
Run(RunOptions) error
|
||||||
|
}
|
||||||
|
|
||||||
// ID uniquely identifies the phase
|
// ID uniquely identifies the phase
|
||||||
type ID struct {
|
type ID struct {
|
||||||
Name string
|
Name string
|
||||||
@ -40,6 +46,7 @@ type ID struct {
|
|||||||
// Client is a phase client that can be used by command line or ui packages
|
// Client is a phase client that can be used by command line or ui packages
|
||||||
type Client interface {
|
type Client interface {
|
||||||
PhaseByID(ID) (Phase, error)
|
PhaseByID(ID) (Phase, error)
|
||||||
|
PlanByID(ID) (Plan, error)
|
||||||
PhaseByAPIObj(*v1alpha1.Phase) (Phase, error)
|
PhaseByAPIObj(*v1alpha1.Phase) (Phase, error)
|
||||||
ClusterMap() (clustermap.ClusterMap, error)
|
ClusterMap() (clustermap.ClusterMap, error)
|
||||||
}
|
}
|
||||||
|
2
pkg/phase/testdata/phases/phaseplan.yaml
vendored
2
pkg/phase/testdata/phases/phaseplan.yaml
vendored
@ -3,7 +3,5 @@ kind: PhasePlan
|
|||||||
metadata:
|
metadata:
|
||||||
name: phasePlan
|
name: phasePlan
|
||||||
description: "Default phase plan"
|
description: "Default phase plan"
|
||||||
phaseGroups:
|
|
||||||
- name: group1
|
|
||||||
phases:
|
phases:
|
||||||
- name: phase
|
- name: phase
|
@ -2,11 +2,23 @@ apiVersion: airshipit.org/v1alpha1
|
|||||||
kind: PhasePlan
|
kind: PhasePlan
|
||||||
metadata:
|
metadata:
|
||||||
name: phasePlan
|
name: phasePlan
|
||||||
phaseGroups:
|
|
||||||
- name: group1
|
|
||||||
phases:
|
phases:
|
||||||
- name: isogen
|
- name: isogen
|
||||||
- name: remotedirect
|
- name: remotedirect
|
||||||
- name: initinfra
|
- name: initinfra
|
||||||
- name: some_phase
|
- name: some_phase
|
||||||
- name: capi_init
|
- 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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user