Introduce plan validate command

This patch implements CLI command to invoke plan validation.
Appropriate functionality was already added to pkg previously.

Change-Id: I038b49c8de0af65f38b62950b27b31d95a6301a9
Signed-off-by: Ruslan Aliev <raliev@mirantis.com>
Relates-To: #19
Relates-To: #497
Closes: #497
This commit is contained in:
Ruslan Aliev 2021-03-23 21:28:09 -05:00
parent e9041a2a22
commit 6076891f31
10 changed files with 226 additions and 1 deletions

View File

@ -37,6 +37,7 @@ func NewPlanCommand(cfgFactory config.Factory) *cobra.Command {
planRootCmd.AddCommand(NewListCommand(cfgFactory))
planRootCmd.AddCommand(NewRunCommand(cfgFactory))
planRootCmd.AddCommand(NewValidateCommand(cfgFactory))
return planRootCmd
}

View File

@ -8,6 +8,7 @@ Available Commands:
help Help about any command
list List plans
run Run plan
validate Validate plan
Flags:
-h, --help help for plan

View File

@ -0,0 +1,7 @@
Run life-cycle phase validation which was defined in document model.
Usage:
validate PLAN_NAME [flags]
Flags:
-h, --help help for validate

48
cmd/plan/validate.go Executable file
View File

@ -0,0 +1,48 @@
/*
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 plan
import (
"github.com/spf13/cobra"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/phase"
)
const (
validateLong = `
Run life-cycle phase validation which was defined in document model.
`
)
// NewValidateCommand creates a command which performs validation of particular phase plan
func NewValidateCommand(cfgFactory config.Factory) *cobra.Command {
r := &phase.PlanValidateCommand{
Factory: cfgFactory,
Options: phase.PlanValidateFlags{},
}
runCmd := &cobra.Command{
Use: "validate PLAN_NAME",
Short: "Validate plan",
Long: validateLong[1:],
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
r.Options.PlanID.Name = args[0]
return r.RunE()
},
}
return runCmd
}

35
cmd/plan/validate_test.go Executable file
View File

@ -0,0 +1,35 @@
/*
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 plan_test
import (
"testing"
"opendev.org/airship/airshipctl/cmd/plan"
"opendev.org/airship/airshipctl/testutil"
)
func TestNewValidateCommand(t *testing.T) {
tests := []*testutil.CmdTest{
{
Name: "plan-validate-with-help",
CmdLine: "--help",
Cmd: plan.NewValidateCommand(nil),
},
}
for _, testcase := range tests {
testutil.RunTest(t, testcase)
}
}

View File

@ -26,4 +26,5 @@ responsible for execution phases in groups
* [airshipctl](airshipctl.md) - A unified entrypoint to various airship components
* [airshipctl plan list](airshipctl_plan_list.md) - List plans
* [airshipctl plan run](airshipctl_plan_run.md) - Run plan
* [airshipctl plan validate](airshipctl_plan_validate.md) - Validate plan

View File

@ -0,0 +1,30 @@
## airshipctl plan validate
Validate plan
### Synopsis
Run life-cycle phase validation which was defined in document model.
```
airshipctl plan validate PLAN_NAME [flags]
```
### Options
```
-h, --help help for validate
```
### Options inherited from parent commands
```
--airshipconf string Path to file for airshipctl configuration. (default "$HOME/.airship/config")
--debug enable verbose output
```
### SEE ALSO
* [airshipctl plan](airshipctl_plan.md) - Manage plans

View File

@ -230,7 +230,7 @@ func (p *plan) Validate() error {
return nil
}
// Run function excutes Run method for each phase
// Run function executes 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})

View File

@ -336,3 +336,35 @@ func (s *StatusCommand) RunE() error {
_, err = ph.Status()
return err
}
// PlanValidateFlags options for plan validate command
type PlanValidateFlags struct {
PlanID ifc.ID
}
// PlanValidateCommand plan validate command
type PlanValidateCommand struct {
Options PlanValidateFlags
Factory config.Factory
}
// RunE runs the plan validate command
func (c *PlanValidateCommand) RunE() error {
cfg, err := c.Factory()
if err != nil {
return err
}
helper, err := NewHelper(cfg)
if err != nil {
return err
}
client := NewClient(helper)
plan, err := client.PlanByID(c.Options.PlanID)
if err != nil {
return err
}
return plan.Validate()
}

View File

@ -27,6 +27,7 @@ import (
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/log"
"opendev.org/airship/airshipctl/pkg/phase"
"opendev.org/airship/airshipctl/pkg/phase/ifc"
)
const (
@ -605,3 +606,72 @@ func TestStatusCommand(t *testing.T) {
})
}
}
func TestPlanValidateCommand(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 plan 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 = defaultCurrentContext
conf.Contexts = map[string]*config.Context{
"context": {
Manifest: "manifest",
},
}
return conf, nil
},
expectedErr: `found no documents`,
},
}
for _, tc := range testCases {
tt := tc
t.Run(tt.name, func(t *testing.T) {
cmd := phase.PlanValidateCommand{
Options: phase.PlanValidateFlags{PlanID: ifc.ID{Name: "invalid"}},
Factory: tt.factory,
}
err := cmd.RunE()
if tt.expectedErr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.expectedErr)
} else {
assert.NoError(t, err)
}
})
}
}