From c46cdebe4860b5b2f475b877090b7cb5f6a6d516 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kalynovskyi Date: Wed, 9 Sep 2020 00:08:03 -0500 Subject: [PATCH] Add phase command objects Commit adds phase command object that allow easy calls from cmd package. Change-Id: I23a9ab3755dbe366ad9b7ce163f75f64f48148cd --- pkg/phase/command.go | 81 +++++++++++++++++++ pkg/phase/command_test.go | 159 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 pkg/phase/command.go create mode 100644 pkg/phase/command_test.go diff --git a/pkg/phase/command.go b/pkg/phase/command.go new file mode 100644 index 000000000..50618cc21 --- /dev/null +++ b/pkg/phase/command.go @@ -0,0 +1,81 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package phase + +import ( + "io" + + "opendev.org/airship/airshipctl/pkg/config" + "opendev.org/airship/airshipctl/pkg/phase/ifc" +) + +// RunFlags options for phase run command +type RunFlags struct { + DryRun bool + PhaseID ifc.ID +} + +// RunCommand phase run command +type RunCommand struct { + Options RunFlags + Factory config.Factory +} + +// RunE runs the phase +func (c *RunCommand) RunE() error { + cfg, err := c.Factory() + if err != nil { + return err + } + + helper, err := NewHelper(cfg) + if err != nil { + return err + } + + client := NewClient(helper) + + phase, err := client.PhaseByID(c.Options.PhaseID) + if err != nil { + return err + } + return phase.Run(ifc.RunOptions{DryRun: c.Options.DryRun}) +} + +// PlanCommand plan command +type PlanCommand struct { + Factory config.Factory + Writer io.Writer +} + +// RunE runs a phase plan command +func (c *PlanCommand) RunE() error { + cfg, err := c.Factory() + if err != nil { + return err + } + + helper, err := NewHelper(cfg) + if err != nil { + return err + } + + plan, err := helper.Plan() + if err != nil { + return err + } + + return PrintPlan(plan, c.Writer) +} diff --git a/pkg/phase/command_test.go b/pkg/phase/command_test.go new file mode 100644 index 000000000..91fa7e4c4 --- /dev/null +++ b/pkg/phase/command_test.go @@ -0,0 +1,159 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package phase_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "opendev.org/airship/airshipctl/pkg/config" + "opendev.org/airship/airshipctl/pkg/phase" +) + +const ( + testFactoryErr = "test config error" + testNewHelperErr = "Missing configuration" + testNoBundlePath = "no such file or directory" +) + +func TestRunCommand(t *testing.T) { + tests := []struct { + name string + errContains string + runFlags phase.RunFlags + factory config.Factory + }{ + { + name: "Error config factory", + factory: func() (*config.Config, error) { + return nil, fmt.Errorf(testFactoryErr) + }, + errContains: testFactoryErr, + }, + { + name: "Error new helper", + factory: func() (*config.Config, error) { + return &config.Config{ + CurrentContext: "does not exist", + Contexts: make(map[string]*config.Context), + }, nil + }, + errContains: testNewHelperErr, + }, + { + name: "Error phase by id", + factory: func() (*config.Config, error) { + conf := config.NewConfig() + conf.Manifests = map[string]*config.Manifest{ + "manifest": { + MetadataPath: "broken_metadata.yaml", + TargetPath: "testdata", + }, + } + conf.CurrentContext = "context" + conf.Contexts = map[string]*config.Context{ + "context": { + Manifest: "manifest", + }, + } + return conf, nil + }, + errContains: testNoBundlePath, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + command := phase.RunCommand{ + Options: tt.runFlags, + Factory: tt.factory, + } + err := command.RunE() + if tt.errContains != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestPlanCommand(t *testing.T) { + tests := []struct { + name string + errContains string + runFlags phase.RunFlags + factory config.Factory + }{ + { + name: "Error config factory", + factory: func() (*config.Config, error) { + return nil, fmt.Errorf(testFactoryErr) + }, + errContains: testFactoryErr, + }, + { + name: "Error new helper", + factory: func() (*config.Config, error) { + return &config.Config{ + CurrentContext: "does not exist", + Contexts: make(map[string]*config.Context), + }, nil + }, + errContains: testNewHelperErr, + }, + { + name: "Error phase by id", + factory: func() (*config.Config, error) { + conf := config.NewConfig() + conf.Manifests = map[string]*config.Manifest{ + "manifest": { + MetadataPath: "broken_metadata.yaml", + TargetPath: "testdata", + }, + } + conf.CurrentContext = "context" + conf.Contexts = map[string]*config.Context{ + "context": { + Manifest: "manifest", + }, + } + return conf, nil + }, + errContains: testNoBundlePath, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + command := phase.PlanCommand{ + Factory: tt.factory, + } + err := command.RunE() + if tt.errContains != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.errContains) + } else { + assert.NoError(t, err) + } + }) + } +}