Groom phase/executors package

All the duplicated methods were removed. Unit tests reorganized.
Test coverage increased.

Change-Id: I0f7bd3eea63c101195ea50c0369e62945d73f297
Signed-off-by: Ruslan Aliev <raliev@mirantis.com>
Closes: #432
This commit is contained in:
Ruslan Aliev 2020-12-10 16:32:06 -06:00
parent 932d92664e
commit 5a6f1096eb
16 changed files with 293 additions and 334 deletions

View File

@ -49,6 +49,7 @@ type BootstrapIsoOptions struct {
Writer io.Writer
}
// VerifyInputs verifies image configuration
func VerifyInputs(cfg *v1alpha1.IsoConfiguration) error {
if cfg.IsoContainer.Volume == "" {
return config.ErrMissingConfig{

View File

@ -37,20 +37,11 @@ type ExecutorRegistry func() map[schema.GroupVersionKind]ifc.ExecutorFactory
func DefaultExecutorRegistry() map[schema.GroupVersionKind]ifc.ExecutorFactory {
execMap := make(map[schema.GroupVersionKind]ifc.ExecutorFactory)
if err := executors.RegisterExecutor(execMap); err != nil {
log.Fatal(ErrExecutorRegistration{ExecutorName: "clusterctl", Err: err})
}
if err := executors.RegisterKubeApplierExecutor(execMap); err != nil {
log.Fatal(ErrExecutorRegistration{ExecutorName: "kubernetes-apply", Err: err})
}
if err := executors.RegisterIsogenExecutor(execMap); err != nil {
log.Fatal(ErrExecutorRegistration{ExecutorName: "isogen", Err: err})
}
if err := executors.RegisterContainerExecutor(execMap); err != nil {
log.Fatal(ErrExecutorRegistration{ExecutorName: "generic-container", Err: err})
}
if err := executors.RegisterEphemeralExecutor(execMap); err != nil {
log.Fatal(ErrExecutorRegistration{ExecutorName: "ephemeral", Err: err})
for _, execName := range []string{executors.Clusterctl, executors.KubernetesApply,
executors.Isogen, executors.GenericContainer, executors.Ephemeral} {
if err := executors.RegisterExecutor(execName, execMap); err != nil {
log.Fatal(ErrExecutorRegistration{ExecutorName: execName, Err: err})
}
}
return execMap
}

View File

@ -17,8 +17,6 @@ package executors
import (
"io"
"k8s.io/apimachinery/pkg/runtime/schema"
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
@ -41,19 +39,8 @@ type ClusterctlExecutor struct {
kubecfg kubeconfig.Interface
}
// RegisterExecutor adds executor to phase executor registry
func RegisterExecutor(registry map[schema.GroupVersionKind]ifc.ExecutorFactory) error {
obj := &airshipv1.Clusterctl{}
gvks, _, err := airshipv1.Scheme.ObjectKinds(obj)
if err != nil {
return err
}
registry[gvks[0]] = NewExecutor
return nil
}
// NewExecutor creates instance of 'clusterctl init' phase executor
func NewExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
// NewClusterctlExecutor creates instance of 'clusterctl init' phase executor
func NewClusterctlExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
options := airshipv1.DefaultClusterctl()
if err := cfg.ExecutorDocument.ToAPIObject(options, airshipv1.Scheme); err != nil {
return nil, err
@ -80,7 +67,7 @@ func (c *ClusterctlExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
case airshipv1.Init:
c.init(opts, evtCh)
default:
c.handleErr(ErrUnknownExecutorAction{Action: string(c.options.Action)}, evtCh)
handleError(evtCh, ErrUnknownExecutorAction{Action: string(c.options.Action)})
}
}
@ -92,23 +79,23 @@ func (c *ClusterctlExecutor) move(opts ifc.RunOptions, evtCh chan events.Event)
ns := c.options.MoveOptions.Namespace
kubeConfigFile, cleanup, err := c.kubecfg.GetFile()
if err != nil {
c.handleErr(err, evtCh)
handleError(evtCh, err)
return
}
defer cleanup()
fromCluster, err := c.clusterMap.ParentCluster(c.clusterName)
if err != nil {
c.handleErr(err, evtCh)
handleError(evtCh, err)
return
}
fromContext, err := c.clusterMap.ClusterKubeconfigContext(fromCluster)
if err != nil {
c.handleErr(err, evtCh)
handleError(evtCh, err)
return
}
toContext, err := c.clusterMap.ClusterKubeconfigContext(c.clusterName)
if err != nil {
c.handleErr(err, evtCh)
handleError(evtCh, err)
return
}
@ -117,7 +104,7 @@ func (c *ClusterctlExecutor) move(opts ifc.RunOptions, evtCh chan events.Event)
if !opts.DryRun {
err = c.Move(kubeConfigFile, fromContext, kubeConfigFile, toContext, ns)
if err != nil {
c.handleErr(err, evtCh)
handleError(evtCh, err)
return
}
}
@ -135,7 +122,7 @@ func (c *ClusterctlExecutor) init(opts ifc.RunOptions, evtCh chan events.Event)
})
kubeConfigFile, cleanup, err := c.kubecfg.GetFile()
if err != nil {
c.handleErr(err, evtCh)
handleError(evtCh, err)
return
}
@ -153,14 +140,14 @@ func (c *ClusterctlExecutor) init(opts ifc.RunOptions, evtCh chan events.Event)
context, err := c.clusterMap.ClusterKubeconfigContext(c.clusterName)
if err != nil {
c.handleErr(err, evtCh)
handleError(evtCh, err)
return
}
// Use cluster name as context in kubeconfig file
err = c.Init(kubeConfigFile, context)
if err != nil {
c.handleErr(err, evtCh)
handleError(evtCh, err)
return
}
evtCh <- events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
@ -169,12 +156,6 @@ func (c *ClusterctlExecutor) init(opts ifc.RunOptions, evtCh chan events.Event)
})
}
func (c *ClusterctlExecutor) handleErr(err error, evtCh chan events.Event) {
evtCh <- events.NewEvent().WithErrorEvent(events.ErrorEvent{
Error: err,
})
}
// Validate executor configuration and documents
func (c *ClusterctlExecutor) Validate() error {
return errors.ErrNotImplemented{}

View File

@ -23,17 +23,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime/schema"
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/document"
airerrors "opendev.org/airship/airshipctl/pkg/errors"
"opendev.org/airship/airshipctl/pkg/events"
"opendev.org/airship/airshipctl/pkg/fs"
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
"opendev.org/airship/airshipctl/pkg/phase"
"opendev.org/airship/airshipctl/pkg/phase/executors"
"opendev.org/airship/airshipctl/pkg/phase/ifc"
testfs "opendev.org/airship/airshipctl/testutil/fs"
@ -59,22 +56,8 @@ providers:
v0.3.3: manifests/function/capi/v0.3.3`
)
func TestRegisterExecutor(t *testing.T) {
registry := make(map[schema.GroupVersionKind]ifc.ExecutorFactory)
expectedGVK := schema.GroupVersionKind{
Group: "airshipit.org",
Version: "v1alpha1",
Kind: "Clusterctl",
}
err := executors.RegisterExecutor(registry)
require.NoError(t, err)
_, found := registry[expectedGVK]
assert.True(t, found)
}
func TestNewExecutor(t *testing.T) {
sampleCfgDoc := executorDoc(t, "init")
sampleCfgDoc := executorDoc(t, fmt.Sprintf(executorConfigTmpl, "init"))
testCases := []struct {
name string
helper ifc.Helper
@ -82,13 +65,13 @@ func TestNewExecutor(t *testing.T) {
}{
{
name: "New Clusterctl Executor",
helper: makeDefaultHelper(t),
helper: makeDefaultHelper(t, "../../clusterctl/client/testdata"),
},
}
for _, test := range testCases {
tt := test
t.Run(tt.name, func(t *testing.T) {
_, actualErr := executors.NewExecutor(ifc.ExecutorConfig{
_, actualErr := executors.NewClusterctlExecutor(ifc.ExecutorConfig{
ExecutorDocument: sampleCfgDoc,
Helper: tt.helper,
})
@ -110,7 +93,7 @@ func TestExecutorRun(t *testing.T) {
}{
{
name: "Error unknown action",
cfgDoc: executorDoc(t, "someAction"),
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmpl, "someAction")),
bundlePath: "testdata/executor_init",
expectedEvt: []events.Event{
wrapError(executors.ErrUnknownExecutorAction{Action: "someAction"}),
@ -119,7 +102,7 @@ func TestExecutorRun(t *testing.T) {
},
{
name: "Error temporary file",
cfgDoc: executorDoc(t, "init"),
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmpl, "init")),
fs: testfs.MockFileSystem{
MockTempFile: func(string, string) (fs.File, error) {
return nil, errTmpFile
@ -136,7 +119,7 @@ func TestExecutorRun(t *testing.T) {
},
{
name: "Regular Run init",
cfgDoc: executorDoc(t, "init"),
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmpl, "init")),
fs: testfs.MockFileSystem{
MockTempFile: func(string, string) (fs.File, error) {
return testfs.TestFile{
@ -167,10 +150,10 @@ func TestExecutorRun(t *testing.T) {
kubeconfig.FromByte([]byte("someKubeConfig")),
kubeconfig.InjectFileSystem(tt.fs),
)
executor, err := executors.NewExecutor(
executor, err := executors.NewClusterctlExecutor(
ifc.ExecutorConfig{
ExecutorDocument: tt.cfgDoc,
Helper: makeDefaultHelper(t),
Helper: makeDefaultHelper(t, "../../clusterctl/client/testdata"),
KubeConfig: kubeCfg,
ClusterMap: tt.clusterMap,
})
@ -197,11 +180,11 @@ func TestExecutorRun(t *testing.T) {
}
func TestExecutorValidate(t *testing.T) {
sampleCfgDoc := executorDoc(t, "init")
executor, err := executors.NewExecutor(
sampleCfgDoc := executorDoc(t, fmt.Sprintf(executorConfigTmpl, "init"))
executor, err := executors.NewClusterctlExecutor(
ifc.ExecutorConfig{
ExecutorDocument: sampleCfgDoc,
Helper: makeDefaultHelper(t),
Helper: makeDefaultHelper(t, "../../clusterctl/client/testdata"),
})
require.NoError(t, err)
expectedErr := airerrors.ErrNotImplemented{}
@ -210,11 +193,11 @@ func TestExecutorValidate(t *testing.T) {
}
func TestExecutorRender(t *testing.T) {
sampleCfgDoc := executorDoc(t, "init")
executor, err := executors.NewExecutor(
sampleCfgDoc := executorDoc(t, fmt.Sprintf(executorConfigTmpl, "init"))
executor, err := executors.NewClusterctlExecutor(
ifc.ExecutorConfig{
ExecutorDocument: sampleCfgDoc,
Helper: makeDefaultHelper(t),
Helper: makeDefaultHelper(t, "../../clusterctl/client/testdata"),
})
require.NoError(t, err)
actualOut := &bytes.Buffer{}
@ -222,29 +205,3 @@ func TestExecutorRender(t *testing.T) {
assert.Len(t, actualOut.Bytes(), 0)
assert.NoError(t, actualErr)
}
func makeDefaultHelper(t *testing.T) ifc.Helper {
t.Helper()
cfg := config.NewConfig()
cfg.Manifests[config.AirshipDefaultManifest].TargetPath = "../../clusterctl/client/testdata"
cfg.Manifests[config.AirshipDefaultManifest].MetadataPath = "metadata.yaml"
cfg.Manifests[config.AirshipDefaultManifest].Repositories[config.DefaultTestPhaseRepo].URLString = ""
cfg.SetLoadedConfigPath(".")
helper, err := phase.NewHelper(cfg)
require.NoError(t, err)
require.NotNil(t, helper)
return helper
}
func executorDoc(t *testing.T, action string) document.Document {
cfg := []byte(fmt.Sprintf(executorConfigTmpl, action))
cfgDoc, err := document.NewDocumentFromBytes(cfg)
require.NoError(t, err)
return cfgDoc
}
func wrapError(err error) events.Event {
return events.NewEvent().WithErrorEvent(events.ErrorEvent{
Error: err,
})
}

71
pkg/phase/executors/common.go Executable file
View File

@ -0,0 +1,71 @@
/*
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 executors
import (
"k8s.io/apimachinery/pkg/runtime/schema"
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/events"
"opendev.org/airship/airshipctl/pkg/phase/ifc"
)
// Constants related to phase executor names
const (
Clusterctl = "clusterctl"
KubernetesApply = "kubernetes-apply"
Isogen = "isogen"
GenericContainer = "generic-container"
Ephemeral = "ephemeral"
)
// RegisterExecutor adds executor to phase executor registry
func RegisterExecutor(executorName string, registry map[schema.GroupVersionKind]ifc.ExecutorFactory) error {
var gvks []schema.GroupVersionKind
var execObj ifc.ExecutorFactory
var err error
switch executorName {
case Clusterctl:
gvks, _, err = airshipv1.Scheme.ObjectKinds(&airshipv1.Clusterctl{})
execObj = NewClusterctlExecutor
case KubernetesApply:
gvks, _, err = airshipv1.Scheme.ObjectKinds(&airshipv1.KubernetesApply{})
execObj = NewKubeApplierExecutor
case Isogen:
gvks, _, err = airshipv1.Scheme.ObjectKinds(airshipv1.DefaultIsoConfiguration())
execObj = NewIsogenExecutor
case GenericContainer:
gvks, _, err = airshipv1.Scheme.ObjectKinds(airshipv1.DefaultGenericContainer())
execObj = NewContainerExecutor
case Ephemeral:
gvks, _, err = airshipv1.Scheme.ObjectKinds(airshipv1.DefaultBootConfiguration())
execObj = NewEphemeralExecutor
default:
return ErrUnknownExecutorName{ExecutorName: executorName}
}
if err != nil {
return err
}
registry[gvks[0]] = execObj
return nil
}
func handleError(ch chan<- events.Event, err error) {
ch <- events.NewEvent().WithErrorEvent(events.ErrorEvent{
Error: err,
})
}

View File

@ -0,0 +1,134 @@
/*
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 executors_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime/schema"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/events"
"opendev.org/airship/airshipctl/pkg/phase"
"opendev.org/airship/airshipctl/pkg/phase/executors"
"opendev.org/airship/airshipctl/pkg/phase/ifc"
)
func TestRegisterExecutor(t *testing.T) {
testCases := []struct {
name string
executorName string
registry map[schema.GroupVersionKind]ifc.ExecutorFactory
expectedGVK schema.GroupVersionKind
expectedErr error
}{
{
name: "register clusterctl executor",
executorName: executors.Clusterctl,
registry: make(map[schema.GroupVersionKind]ifc.ExecutorFactory),
expectedGVK: schema.GroupVersionKind{
Group: "airshipit.org",
Version: "v1alpha1",
Kind: "Clusterctl",
},
},
{
name: "register container executor",
executorName: executors.GenericContainer,
registry: make(map[schema.GroupVersionKind]ifc.ExecutorFactory),
expectedGVK: schema.GroupVersionKind{
Group: "airshipit.org",
Version: "v1alpha1",
Kind: "GenericContainer",
},
},
{
name: "register isogen executor",
executorName: executors.Isogen,
registry: make(map[schema.GroupVersionKind]ifc.ExecutorFactory),
expectedGVK: schema.GroupVersionKind{
Group: "airshipit.org",
Version: "v1alpha1",
Kind: "IsoConfiguration",
},
},
{
name: "register k8s applier executor",
executorName: executors.KubernetesApply,
registry: make(map[schema.GroupVersionKind]ifc.ExecutorFactory),
expectedGVK: schema.GroupVersionKind{
Group: "airshipit.org",
Version: "v1alpha1",
Kind: "KubernetesApply",
},
},
{
name: "register ephemeral executor",
executorName: executors.Ephemeral,
registry: make(map[schema.GroupVersionKind]ifc.ExecutorFactory),
expectedGVK: schema.GroupVersionKind{
Group: "airshipit.org",
Version: "v1alpha1",
Kind: "BootConfiguration",
},
},
}
for _, test := range testCases {
tt := test
t.Run(tt.name, func(t *testing.T) {
err := executors.RegisterExecutor(tt.executorName, tt.registry)
require.NoError(t, err)
_, found := tt.registry[tt.expectedGVK]
assert.True(t, found)
})
}
}
func makeDefaultHelper(t *testing.T, targetPath string) ifc.Helper {
t.Helper()
cfg := config.NewConfig()
cfg.Manifests[config.AirshipDefaultManifest].TargetPath = targetPath
cfg.Manifests[config.AirshipDefaultManifest].MetadataPath = "metadata.yaml"
cfg.Manifests[config.AirshipDefaultManifest].Repositories[config.DefaultTestPhaseRepo].URLString = ""
cfg.SetLoadedConfigPath(".")
helper, err := phase.NewHelper(cfg)
require.NoError(t, err)
require.NotNil(t, helper)
return helper
}
// executorDoc converts string to document object
func executorDoc(t *testing.T, s string) document.Document {
doc, err := document.NewDocumentFromBytes([]byte(s))
require.NoError(t, err)
require.NotNil(t, doc)
return doc
}
func testBundleFactory(path string) document.BundleFactoryFunc {
return func() (document.Bundle, error) {
return document.NewBundleByPath(path)
}
}
func wrapError(err error) events.Event {
return events.NewEvent().WithErrorEvent(events.ErrorEvent{
Error: err,
})
}

View File

@ -26,8 +26,6 @@ import (
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/yaml"
"k8s.io/apimachinery/pkg/runtime/schema"
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/errors"
@ -47,17 +45,6 @@ type ContainerExecutor struct {
targetPath string
}
// RegisterContainerExecutor adds executor to phase executor registry
func RegisterContainerExecutor(registry map[schema.GroupVersionKind]ifc.ExecutorFactory) error {
obj := v1alpha1.DefaultGenericContainer()
gvks, _, err := v1alpha1.Scheme.ObjectKinds(obj)
if err != nil {
return err
}
registry[gvks[0]] = NewContainerExecutor
return nil
}
// NewContainerExecutor creates instance of phase executor
func NewContainerExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
bundle, err := cfg.BundleFactory()

View File

@ -20,15 +20,12 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
"sigs.k8s.io/kustomize/kyaml/runfn"
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/phase"
"opendev.org/airship/airshipctl/pkg/phase/executors"
"opendev.org/airship/airshipctl/pkg/phase/ifc"
yaml_util "opendev.org/airship/airshipctl/pkg/util/yaml"
@ -109,27 +106,13 @@ type: Opaque
`
)
func TestRegisterContainerExecutor(t *testing.T) {
registry := make(map[schema.GroupVersionKind]ifc.ExecutorFactory)
expectedGVK := schema.GroupVersionKind{
Group: "airshipit.org",
Version: "v1alpha1",
Kind: "GenericContainer",
}
err := executors.RegisterContainerExecutor(registry)
require.NoError(t, err)
_, found := registry[expectedGVK]
assert.True(t, found)
}
func TestNewContainerExecutor(t *testing.T) {
execDoc, err := document.NewDocumentFromBytes([]byte(containerExecutorDoc))
require.NoError(t, err)
_, err = executors.NewContainerExecutor(ifc.ExecutorConfig{
ExecutorDocument: execDoc,
BundleFactory: testBundleFactory(singleExecutorBundlePath),
Helper: makeDefaultContainerHelper(t),
Helper: makeDefaultHelper(t, "../../container/testdata"),
})
require.NoError(t, err)
}
@ -236,16 +219,3 @@ func TestPrepareFunctions(t *testing.T) {
assert.Equal(t, transformedFunction, strFuncs)
}
func makeDefaultContainerHelper(t *testing.T) ifc.Helper {
t.Helper()
cfg := config.NewConfig()
cfg.Manifests[config.AirshipDefaultManifest].TargetPath = "../../container/testdata"
cfg.Manifests[config.AirshipDefaultManifest].MetadataPath = "metadata.yaml"
cfg.Manifests[config.AirshipDefaultManifest].Repositories[config.DefaultTestPhaseRepo].URLString = ""
cfg.SetLoadedConfigPath(".")
helper, err := phase.NewHelper(cfg)
require.NoError(t, err)
require.NotNil(t, helper)
return helper
}

View File

@ -19,8 +19,6 @@ import (
"io"
"time"
"k8s.io/apimachinery/pkg/runtime/schema"
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/bootstrap/ephemeral"
"opendev.org/airship/airshipctl/pkg/container"
@ -42,17 +40,6 @@ type EphemeralExecutor struct {
Container container.Container
}
// RegisterEphemeralExecutor adds executor to phase executor registry
func RegisterEphemeralExecutor(registry map[schema.GroupVersionKind]ifc.ExecutorFactory) error {
obj := v1alpha1.DefaultBootConfiguration()
gvks, _, err := v1alpha1.Scheme.ObjectKinds(obj)
if err != nil {
return err
}
registry[gvks[0]] = NewEphemeralExecutor
return nil
}
// NewEphemeralExecutor creates instance of phase executor
func NewEphemeralExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
apiObj := &v1alpha1.BootConfiguration{}
@ -92,7 +79,7 @@ func (c *EphemeralExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
c.BootConf.BootstrapContainer.ContainerRuntime,
c.BootConf.BootstrapContainer.Image)
if err != nil {
handleEphemeralError(evtCh, err)
handleError(evtCh, err)
return
}
c.Container = builder
@ -111,7 +98,7 @@ func (c *EphemeralExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
err := bootstrapOpts.VerifyInputs()
if err != nil {
handleEphemeralError(evtCh, err)
handleError(evtCh, err)
return
}
@ -122,7 +109,7 @@ func (c *EphemeralExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
err = bootstrapOpts.CreateBootstrapContainer()
if err != nil {
handleEphemeralError(evtCh, err)
handleError(evtCh, err)
return
}
@ -133,7 +120,7 @@ func (c *EphemeralExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
err = bootstrapOpts.VerifyArtifacts()
if err != nil {
handleEphemeralError(evtCh, err)
handleError(evtCh, err)
return
}
@ -153,12 +140,3 @@ func (c *EphemeralExecutor) Render(w io.Writer, _ ifc.RenderOptions) error {
log.Print("Ephemeral Executor Render() will be implemented later.")
return nil
}
func handleEphemeralError(ch chan<- events.Event, err error) {
ch <- events.Event{
Type: events.ErrorType,
ErrorEvent: events.ErrorEvent{
Error: err,
},
}
}

View File

@ -24,8 +24,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime/schema"
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/bootstrap/ephemeral"
"opendev.org/airship/airshipctl/pkg/container"
@ -57,21 +55,6 @@ bootstrapContainer:
`
)
// TestRegisterEphemeralExecutor - Unit testing function RegisterEphemeralExecutor()
func TestRegisterEphemeralExecutor(t *testing.T) {
registry := make(map[schema.GroupVersionKind]ifc.ExecutorFactory)
expectedGVK := schema.GroupVersionKind{
Group: "airshipit.org",
Version: "v1alpha1",
Kind: "BootConfiguration",
}
err := executors.RegisterEphemeralExecutor(registry)
require.NoError(t, err)
_, found := registry[expectedGVK]
assert.True(t, found)
}
// TestNewEphemeralExecutor - Unit testing function NewExecutor()
func TestNewEphemeralExecutor(t *testing.T) {
execDoc, err := document.NewDocumentFromBytes([]byte(executorEphemeralDoc))

View File

@ -35,3 +35,13 @@ type ErrIsogenNilBundle struct {
func (e ErrIsogenNilBundle) Error() string {
return "Cannot build iso with empty bundle, no data source is available"
}
// ErrUnknownExecutorName is returned for unknown executor name parameter
// received by RegisterExecutor function
type ErrUnknownExecutorName struct {
ExecutorName string
}
func (e ErrUnknownExecutorName) Error() string {
return fmt.Sprintf("unknown executor name '%s'", e.ExecutorName)
}

View File

@ -21,8 +21,6 @@ import (
"path/filepath"
"strings"
"k8s.io/apimachinery/pkg/runtime/schema"
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/bootstrap/isogen"
"opendev.org/airship/airshipctl/pkg/container"
@ -44,17 +42,6 @@ type IsogenExecutor struct {
Builder container.Container
}
// RegisterIsogenExecutor adds executor to phase executor registry
func RegisterIsogenExecutor(registry map[schema.GroupVersionKind]ifc.ExecutorFactory) error {
obj := v1alpha1.DefaultIsoConfiguration()
gvks, _, err := v1alpha1.Scheme.ObjectKinds(obj)
if err != nil {
return err
}
registry[gvks[0]] = NewIsogenExecutor
return nil
}
// NewIsogenExecutor creates instance of phase executor
func NewIsogenExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
apiObj := &v1alpha1.IsoConfiguration{
@ -161,9 +148,3 @@ func (c *IsogenExecutor) Render(w io.Writer, _ ifc.RenderOptions) error {
_, err := w.Write([]byte{})
return err
}
func handleError(ch chan<- events.Event, err error) {
ch <- events.NewEvent().WithErrorEvent(events.ErrorEvent{
Error: err,
})
}

View File

@ -23,7 +23,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime/schema"
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/container"
@ -53,20 +52,6 @@ container:
executorBundlePath = "../../bootstrap/isogen/testdata/primary/site/test-site/ephemeral/bootstrap"
)
func TestRegisterIsogenExecutor(t *testing.T) {
registry := make(map[schema.GroupVersionKind]ifc.ExecutorFactory)
expectedGVK := schema.GroupVersionKind{
Group: "airshipit.org",
Version: "v1alpha1",
Kind: "IsoConfiguration",
}
err := executors.RegisterIsogenExecutor(registry)
require.NoError(t, err)
_, found := registry[expectedGVK]
assert.True(t, found)
}
func TestNewIsogenExecutor(t *testing.T) {
execDoc, err := document.NewDocumentFromBytes([]byte(isogenExecutorDoc))
require.NoError(t, err)
@ -176,9 +161,3 @@ func TestIsogenExecutorRun(t *testing.T) {
})
}
}
func testBundleFactory(path string) document.BundleFactoryFunc {
return func() (document.Bundle, error) {
return document.NewBundleByPath(path)
}
}

View File

@ -18,7 +18,6 @@ import (
"io"
"time"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/cli-utils/pkg/common"
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
@ -33,73 +32,47 @@ import (
"opendev.org/airship/airshipctl/pkg/phase/ifc"
)
// ExecutorOptions provide a way to configure executor
type ExecutorOptions struct {
BundleName string
ClusterName string
var _ ifc.Executor = &KubeApplierExecutor{}
// KubeApplierExecutor applies resources to kubernetes
type KubeApplierExecutor struct {
ExecutorBundle document.Bundle
ExecutorDocument document.Document
BundleFactory document.BundleFactoryFunc
Kubeconfig kubeconfig.Interface
BundleName string
Helper ifc.Helper
ClusterMap clustermap.ClusterMap
}
var _ ifc.Executor = &Executor{}
// RegisterKubeApplierExecutor adds executor to phase executor registry
func RegisterKubeApplierExecutor(registry map[schema.GroupVersionKind]ifc.ExecutorFactory) error {
obj := &airshipv1.KubernetesApply{}
gvks, _, err := airshipv1.Scheme.ObjectKinds(obj)
if err != nil {
return err
}
registry[gvks[0]] = registerExecutor
return nil
}
// registerExecutor is here so that executor in theory can be used outside phases
func registerExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
return NewKubeApplierExecutor(ExecutorOptions{
ClusterName: cfg.ClusterName,
BundleName: cfg.PhaseName,
Helper: cfg.Helper,
ExecutorDocument: cfg.ExecutorDocument,
BundleFactory: cfg.BundleFactory,
Kubeconfig: cfg.KubeConfig,
ClusterMap: cfg.ClusterMap,
})
}
// Executor applies resources to kubernetes
type Executor struct {
Options ExecutorOptions
ExecutorBundle document.Bundle
apiObject *airshipv1.KubernetesApply
cleanup kubeconfig.Cleanup
apiObject *airshipv1.KubernetesApply
cleanup kubeconfig.Cleanup
clusterMap clustermap.ClusterMap
clusterName string
kubeconfig kubeconfig.Interface
}
// NewKubeApplierExecutor returns instance of executor
func NewKubeApplierExecutor(opts ExecutorOptions) (*Executor, error) {
func NewKubeApplierExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
apiObj := &airshipv1.KubernetesApply{}
err := opts.ExecutorDocument.ToAPIObject(apiObj, airshipv1.Scheme)
err := cfg.ExecutorDocument.ToAPIObject(apiObj, airshipv1.Scheme)
if err != nil {
return nil, err
}
bundle, err := opts.BundleFactory()
bundle, err := cfg.BundleFactory()
if err != nil {
return nil, err
}
return &Executor{
ExecutorBundle: bundle,
Options: opts,
apiObject: apiObj,
return &KubeApplierExecutor{
ExecutorBundle: bundle,
BundleName: cfg.PhaseName,
Helper: cfg.Helper,
ExecutorDocument: cfg.ExecutorDocument,
apiObject: apiObj,
clusterMap: cfg.ClusterMap,
clusterName: cfg.ClusterName,
kubeconfig: cfg.KubeConfig,
}, nil
}
// Run executor, should be performed in separate go routine
func (e *Executor) Run(ch chan events.Event, runOpts ifc.RunOptions) {
func (e *KubeApplierExecutor) Run(ch chan events.Event, runOpts ifc.RunOptions) {
applier, filteredBundle, err := e.prepareApplier(ch)
if err != nil {
handleError(ch, err)
@ -120,20 +93,20 @@ func (e *Executor) Run(ch chan events.Event, runOpts ifc.RunOptions) {
applyOptions := k8sapplier.ApplyOptions{
DryRunStrategy: dryRunStrategy,
Prune: e.apiObject.Config.PruneOptions.Prune,
BundleName: e.Options.BundleName,
BundleName: e.BundleName,
WaitTimeout: timeout,
}
applier.ApplyBundle(filteredBundle, applyOptions)
}
func (e *Executor) prepareApplier(ch chan events.Event) (*k8sapplier.Applier, document.Bundle, error) {
func (e *KubeApplierExecutor) prepareApplier(ch chan events.Event) (*k8sapplier.Applier, document.Bundle, error) {
log.Debug("Getting kubeconfig context name from cluster map")
context, err := e.Options.ClusterMap.ClusterKubeconfigContext(e.Options.ClusterName)
context, err := e.clusterMap.ClusterKubeconfigContext(e.clusterName)
if err != nil {
return nil, nil, err
}
log.Debug("Getting kubeconfig file information from kubeconfig provider")
path, cleanup, err := e.Options.Kubeconfig.GetFile()
path, cleanup, err := e.kubeconfig.GetFile()
if err != nil {
return nil, nil, err
}
@ -151,12 +124,12 @@ func (e *Executor) prepareApplier(ch chan events.Event) (*k8sapplier.Applier, do
}
// Validate document set
func (e *Executor) Validate() error {
func (e *KubeApplierExecutor) Validate() error {
return errors.ErrNotImplemented{}
}
// Render document set
func (e *Executor) Render(w io.Writer, o ifc.RenderOptions) error {
func (e *KubeApplierExecutor) Render(w io.Writer, o ifc.RenderOptions) error {
bundle, err := e.ExecutorBundle.SelectBundle(o.FilterSelector)
if err != nil {
return err

View File

@ -23,13 +23,11 @@ import (
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/events"
"opendev.org/airship/airshipctl/pkg/fs"
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
"opendev.org/airship/airshipctl/pkg/k8s/utils"
"opendev.org/airship/airshipctl/pkg/phase"
"opendev.org/airship/airshipctl/pkg/phase/executors"
"opendev.org/airship/airshipctl/pkg/phase/ifc"
testfs "opendev.org/airship/airshipctl/testutil/fs"
@ -96,7 +94,7 @@ func TestNewKubeApplierExecutor(t *testing.T) {
name: "valid executor",
cfgDoc: ValidExecutorDoc,
kubeconf: testKubeconfig(testValidKubeconfig),
helper: makeKubeApplierDefaultHelper(t),
helper: makeDefaultHelper(t, "../../k8s/applier/testdata"),
bundleFactory: testBundleFactory("../../k8s/applier/testdata/source_bundle"),
},
{
@ -109,7 +107,7 @@ metadata:
labels:
cli-utils.sigs.k8s.io/inventory-id: "some id"`,
expectedErr: "wrong config document",
helper: makeKubeApplierDefaultHelper(t),
helper: makeDefaultHelper(t, "../../k8s/applier/testdata"),
bundleFactory: testBundleFactory("../../k8s/applier/testdata/source_bundle"),
},
@ -118,7 +116,7 @@ metadata:
cfgDoc: ValidExecutorDoc,
expectedErr: "no such file or directory",
kubeconf: testKubeconfig(testValidKubeconfig),
helper: makeKubeApplierDefaultHelper(t),
helper: makeDefaultHelper(t, "../../k8s/applier/testdata"),
bundleFactory: testBundleFactory("does not exist"),
},
}
@ -131,10 +129,10 @@ metadata:
require.NotNil(t, doc)
exec, err := executors.NewKubeApplierExecutor(
executors.ExecutorOptions{
ifc.ExecutorConfig{
ExecutorDocument: doc,
BundleFactory: tt.bundleFactory,
Kubeconfig: tt.kubeconf,
KubeConfig: tt.kubeconf,
Helper: tt.helper,
})
if tt.expectedErr != "" {
@ -167,10 +165,10 @@ func TestKubeApplierExecutorRun(t *testing.T) {
{
name: "cant read kubeconfig error",
containsErr: "no such file or directory",
helper: makeKubeApplierDefaultHelper(t),
helper: makeDefaultHelper(t, "../../k8s/applier/testdata"),
bundleFactory: testBundleFactory("../../k8s/applier/testdata/source_bundle"),
kubeconf: testKubeconfig(`invalid kubeconfig`),
execDoc: toKubernetesApply(t, ValidExecutorDocNamespaced),
execDoc: executorDoc(t, ValidExecutorDocNamespaced),
clusterName: "ephemeral-cluster",
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
Map: map[string]*v1alpha1.Cluster{
@ -181,10 +179,10 @@ func TestKubeApplierExecutorRun(t *testing.T) {
{
name: "error cluster not defined",
containsErr: "cluster is not defined in in cluster map",
helper: makeKubeApplierDefaultHelper(t),
helper: makeDefaultHelper(t, "../../k8s/applier/testdata"),
bundleFactory: testBundleFactory("../../k8s/applier/testdata/source_bundle"),
kubeconf: testKubeconfig(testValidKubeconfig),
execDoc: toKubernetesApply(t, ValidExecutorDocNamespaced),
execDoc: executorDoc(t, ValidExecutorDocNamespaced),
clusterMap: clustermap.NewClusterMap(v1alpha1.DefaultClusterMap()),
},
}
@ -192,11 +190,11 @@ func TestKubeApplierExecutorRun(t *testing.T) {
tt := tt
t.Run(tt.name, func(t *testing.T) {
exec, err := executors.NewKubeApplierExecutor(
executors.ExecutorOptions{
ifc.ExecutorConfig{
ExecutorDocument: tt.execDoc,
Helper: tt.helper,
BundleFactory: tt.bundleFactory,
Kubeconfig: tt.kubeconf,
KubeConfig: tt.kubeconf,
ClusterMap: tt.clusterMap,
ClusterName: tt.clusterName,
})
@ -225,7 +223,7 @@ func TestRender(t *testing.T) {
execDoc, err := document.NewDocumentFromBytes([]byte(ValidExecutorDoc))
require.NoError(t, err)
require.NotNil(t, execDoc)
exec, err := executors.NewKubeApplierExecutor(executors.ExecutorOptions{
exec, err := executors.NewKubeApplierExecutor(ifc.ExecutorConfig{
BundleFactory: testBundleFactory("../../k8s/applier/testdata/source_bundle"),
ExecutorDocument: execDoc,
})
@ -240,42 +238,6 @@ func TestRender(t *testing.T) {
assert.Contains(t, result, "ReplicationController")
}
func makeKubeApplierDefaultHelper(t *testing.T) ifc.Helper {
t.Helper()
conf := &config.Config{
CurrentContext: "default",
Contexts: map[string]*config.Context{
"default": {
Manifest: "default-manifest",
},
},
Manifests: map[string]*config.Manifest{
"default-manifest": {
MetadataPath: "metadata.yaml",
TargetPath: "../../k8s/applier/testdata",
PhaseRepositoryName: config.DefaultTestPhaseRepo,
Repositories: map[string]*config.Repository{
config.DefaultTestPhaseRepo: {
URLString: "",
},
},
},
},
}
helper, err := phase.NewHelper(conf)
require.NoError(t, err)
require.NotNil(t, helper)
return helper
}
// toKubernetesApply converts string to document object
func toKubernetesApply(t *testing.T, s string) document.Document {
doc, err := document.NewDocumentFromBytes([]byte(s))
require.NoError(t, err)
require.NotNil(t, doc)
return doc
}
func testKubeconfig(stringData string) kubeconfig.Interface {
return kubeconfig.NewKubeConfig(
kubeconfig.FromByte([]byte(stringData)),

View File

@ -57,6 +57,7 @@ type ExecutorFactory func(config ExecutorConfig) (Executor, error)
type ExecutorConfig struct {
PhaseName string
ClusterName string
BundleName string
ClusterMap clustermap.ClusterMap
ExecutorDocument document.Document