From b63c9e3888b4766085f255d7f305decd9714826e Mon Sep 17 00:00:00 2001
From: Kostiantyn Kalynovskyi <kkalynovskyi@mirantis.com>
Date: Mon, 14 Sep 2020 15:22:24 -0500
Subject: [PATCH] Cleanup phase package

Change-Id: Ib849da5faa2f69f058bd9df814b55e117ecefb8a
Relates-To: #342
---
 pkg/phase/client.go      |  21 +++
 pkg/phase/client_test.go |  10 ++
 pkg/phase/phase.go       | 258 ------------------------------------
 pkg/phase/phase_test.go  | 276 ---------------------------------------
 4 files changed, 31 insertions(+), 534 deletions(-)
 delete mode 100644 pkg/phase/phase.go
 delete mode 100644 pkg/phase/phase_test.go

diff --git a/pkg/phase/client.go b/pkg/phase/client.go
index 5265e9647..0a93c922a 100644
--- a/pkg/phase/client.go
+++ b/pkg/phase/client.go
@@ -17,14 +17,35 @@ package phase
 import (
 	"path/filepath"
 
+	"k8s.io/apimachinery/pkg/runtime/schema"
+
 	"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
+	clusterctl "opendev.org/airship/airshipctl/pkg/clusterctl/client"
 	"opendev.org/airship/airshipctl/pkg/document"
 	"opendev.org/airship/airshipctl/pkg/events"
+	"opendev.org/airship/airshipctl/pkg/k8s/applier"
 	"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
 	"opendev.org/airship/airshipctl/pkg/k8s/utils"
+	"opendev.org/airship/airshipctl/pkg/log"
 	"opendev.org/airship/airshipctl/pkg/phase/ifc"
 )
 
+// ExecutorRegistry returns map with executor factories
+type ExecutorRegistry func() map[schema.GroupVersionKind]ifc.ExecutorFactory
+
+// DefaultExecutorRegistry returns map with executor factories
+func DefaultExecutorRegistry() map[schema.GroupVersionKind]ifc.ExecutorFactory {
+	execMap := make(map[schema.GroupVersionKind]ifc.ExecutorFactory)
+
+	if err := clusterctl.RegisterExecutor(execMap); err != nil {
+		log.Fatal(ErrExecutorRegistration{ExecutorName: "clusterctl", Err: err})
+	}
+	if err := applier.RegisterExecutor(execMap); err != nil {
+		log.Fatal(ErrExecutorRegistration{ExecutorName: "kubernetes-apply", Err: err})
+	}
+	return execMap
+}
+
 var _ ifc.Phase = &phase{}
 
 // Phase implements phase interface
diff --git a/pkg/phase/client_test.go b/pkg/phase/client_test.go
index 9ea011f18..424fea4e5 100644
--- a/pkg/phase/client_test.go
+++ b/pkg/phase/client_test.go
@@ -147,6 +147,16 @@ func TestClientByAPIObj(t *testing.T) {
 	require.NotNil(t, p)
 }
 
+// assertEqualExecutor allows to compare executor interfaces
+// check if we expect nil, and if so actual interface must be nil also otherwise compare types
+func assertEqualExecutor(t *testing.T, expected, actual ifc.Executor) {
+	if expected == nil {
+		assert.Nil(t, actual)
+		return
+	}
+	assert.IsType(t, expected, actual)
+}
+
 func fakeRegistry() map[schema.GroupVersionKind]ifc.ExecutorFactory {
 	gvk := schema.GroupVersionKind{
 		Group:   "airshipit.org",
diff --git a/pkg/phase/phase.go b/pkg/phase/phase.go
deleted file mode 100644
index c33ce8fe8..000000000
--- a/pkg/phase/phase.go
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- 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 (
-	"path/filepath"
-
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime/schema"
-
-	airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
-	"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
-	clusterctl "opendev.org/airship/airshipctl/pkg/clusterctl/client"
-	"opendev.org/airship/airshipctl/pkg/config"
-	"opendev.org/airship/airshipctl/pkg/document"
-	"opendev.org/airship/airshipctl/pkg/events"
-	"opendev.org/airship/airshipctl/pkg/k8s/applier"
-	"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
-	k8sutils "opendev.org/airship/airshipctl/pkg/k8s/utils"
-	"opendev.org/airship/airshipctl/pkg/log"
-	"opendev.org/airship/airshipctl/pkg/phase/ifc"
-)
-
-// ExecutorRegistry returns map with executor factories
-type ExecutorRegistry func() map[schema.GroupVersionKind]ifc.ExecutorFactory
-
-// DefaultExecutorRegistry returns map with executor factories
-func DefaultExecutorRegistry() map[schema.GroupVersionKind]ifc.ExecutorFactory {
-	execMap := make(map[schema.GroupVersionKind]ifc.ExecutorFactory)
-
-	if err := clusterctl.RegisterExecutor(execMap); err != nil {
-		log.Fatal(ErrExecutorRegistration{ExecutorName: "clusterctl", Err: err})
-	}
-	if err := applier.RegisterExecutor(execMap); err != nil {
-		log.Fatal(ErrExecutorRegistration{ExecutorName: "kubernetes-apply", Err: err})
-	}
-	return execMap
-}
-
-// Cmd object to work with phase api
-type Cmd struct {
-	DryRun bool
-
-	Registry ExecutorRegistry
-	// Will be used to get processor based on executor action
-	Processor events.EventProcessor
-	*config.Config
-}
-
-func (p *Cmd) getBundle() (document.Bundle, error) {
-	tp, err := p.CurrentContextTargetPath()
-	if err != nil {
-		return nil, err
-	}
-	meta, err := p.Config.CurrentContextManifestMetadata()
-	if err != nil {
-		return nil, err
-	}
-	log.Debugf("Building phase bundle from path %s", tp)
-	return document.NewBundleByPath(filepath.Join(tp, meta.PhaseMeta.Path))
-}
-
-func (p *Cmd) getPhaseExecutor(name string) (ifc.Executor, error) {
-	phaseConfig, err := p.GetPhase(name)
-	if err != nil {
-		return nil, err
-	}
-	return p.GetExecutor(phaseConfig)
-}
-
-// GetPhase returns particular phase object identified by name
-func (p *Cmd) GetPhase(name string) (*airshipv1.Phase, error) {
-	bundle, err := p.getBundle()
-	if err != nil {
-		return nil, err
-	}
-	phaseConfig := &airshipv1.Phase{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: name,
-		},
-	}
-	selector, err := document.NewSelector().ByObject(phaseConfig, airshipv1.Scheme)
-	if err != nil {
-		return nil, err
-	}
-	doc, err := bundle.SelectOne(selector)
-	if err != nil {
-		return nil, err
-	}
-
-	if err = doc.ToAPIObject(phaseConfig, airshipv1.Scheme); err != nil {
-		return nil, err
-	}
-	return phaseConfig, nil
-}
-
-// GetClusterMap returns cluster map object
-func (p *Cmd) GetClusterMap() (clustermap.ClusterMap, error) {
-	bundle, err := p.getBundle()
-	if err != nil {
-		return nil, err
-	}
-	clusterMap := &airshipv1.ClusterMap{}
-	selector, err := document.NewSelector().ByObject(clusterMap, airshipv1.Scheme)
-	if err != nil {
-		return nil, err
-	}
-	doc, err := bundle.SelectOne(selector)
-	if err != nil {
-		return nil, err
-	}
-
-	if err = doc.ToAPIObject(clusterMap, airshipv1.Scheme); err != nil {
-		return nil, err
-	}
-	return clustermap.NewClusterMap(clusterMap), nil
-}
-
-// GetExecutor referenced in a phase configuration
-func (p *Cmd) GetExecutor(phase *airshipv1.Phase) (ifc.Executor, error) {
-	bundle, err := p.getBundle()
-	if err != nil {
-		return nil, err
-	}
-	phaseConfig := phase.Config
-	// Searching executor configuration document referenced in
-	// phase configuration
-	refGVK := phaseConfig.ExecutorRef.GroupVersionKind()
-	selector := document.NewSelector().
-		ByGvk(refGVK.Group, refGVK.Version, refGVK.Kind).
-		ByName(phaseConfig.ExecutorRef.Name).
-		ByNamespace(phaseConfig.ExecutorRef.Namespace)
-	executorDoc, err := bundle.SelectOne(selector)
-	if err != nil {
-		return nil, err
-	}
-
-	// Define executor configuration options
-	targetPath, err := p.Config.CurrentContextTargetPath()
-	if err != nil {
-		return nil, err
-	}
-	var executorDocBundle document.Bundle
-	// if entrypoint is defined use it, if not, just pass nil bundle, executors should be ready for that
-	if phaseConfig.DocumentEntryPoint != "" {
-		bundlePath := filepath.Join(targetPath, phaseConfig.DocumentEntryPoint)
-		executorDocBundle, err = document.NewBundleByPath(bundlePath)
-		if err != nil {
-			return nil, err
-		}
-	}
-	if p.Registry == nil {
-		p.Registry = DefaultExecutorRegistry
-	}
-	// Look for executor factory defined in registry
-	executorFactory, found := p.Registry()[refGVK]
-	if !found {
-		return nil, ErrExecutorNotFound{GVK: refGVK}
-	}
-	meta, err := p.Config.CurrentContextManifestMetadata()
-	if err != nil {
-		return nil, err
-	}
-	cMap, err := p.GetClusterMap()
-	if err != nil {
-		return nil, err
-	}
-
-	kubeConfig := kubeconfig.NewBuilder().
-		// TODO add kubeconfig flags path here, when kubeconfig flag is not controlled
-		// by config module during config loading.
-		WithBundle(meta.PhaseMeta.Path).
-		WithClusterMap(cMap).
-		WithClusterName(phase.ClusterName).
-		WithTempRoot(filepath.Dir(p.Config.LoadedConfigPath())).
-		Build()
-	return executorFactory(
-		ifc.ExecutorConfig{
-			ExecutorBundle:   executorDocBundle,
-			PhaseName:        phase.Name,
-			ExecutorDocument: executorDoc,
-			AirshipConfig:    p.Config,
-			KubeConfig:       kubeConfig,
-			ClusterName:      phase.ClusterName,
-			ClusterMap:       cMap,
-		})
-}
-
-// Exec starts executor goroutine and processes the events
-func (p *Cmd) Exec(name string) error {
-	runCh := make(chan events.Event)
-	processor := events.NewDefaultProcessor(k8sutils.Streams())
-	go func() {
-		executor, err := p.getPhaseExecutor(name)
-		if err != nil {
-			handleError(err, runCh)
-			return
-		}
-		executor.Run(runCh, ifc.RunOptions{
-			Debug:  log.DebugEnabled(),
-			DryRun: p.DryRun,
-		})
-	}()
-	return processor.Process(runCh)
-}
-
-// Plan shows available phase names
-func (p *Cmd) Plan() (map[string][]string, error) {
-	bundle, err := p.getBundle()
-	if err != nil {
-		return nil, err
-	}
-	plan := &airshipv1.PhasePlan{}
-	selector, err := document.NewSelector().ByObject(plan, airshipv1.Scheme)
-	if err != nil {
-		return nil, err
-	}
-	doc, err := bundle.SelectOne(selector)
-	if err != nil {
-		return nil, err
-	}
-
-	if err := doc.ToAPIObject(plan, airshipv1.Scheme); err != nil {
-		return nil, err
-	}
-
-	result := make(map[string][]string)
-	for _, phaseGroup := range plan.PhaseGroups {
-		phases := make([]string, len(phaseGroup.Phases))
-		for i, phase := range phaseGroup.Phases {
-			phases[i] = phase.Name
-		}
-		result[phaseGroup.Name] = phases
-	}
-	return result, nil
-}
-
-func handleError(err error, ch chan events.Event) {
-	ch <- events.Event{
-		Type: events.ErrorType,
-		ErrorEvent: events.ErrorEvent{
-			Error: err,
-		},
-	}
-	close(ch)
-}
diff --git a/pkg/phase/phase_test.go b/pkg/phase/phase_test.go
deleted file mode 100644
index 049b5ac24..000000000
--- a/pkg/phase/phase_test.go
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- 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 (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime/schema"
-
-	"sigs.k8s.io/kustomize/api/resid"
-	"sigs.k8s.io/kustomize/api/types"
-
-	airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
-	"opendev.org/airship/airshipctl/pkg/config"
-	"opendev.org/airship/airshipctl/pkg/document"
-	"opendev.org/airship/airshipctl/pkg/k8s/applier"
-	"opendev.org/airship/airshipctl/pkg/phase"
-	"opendev.org/airship/airshipctl/pkg/phase/ifc"
-)
-
-func TestPhasePlan(t *testing.T) {
-	testCases := []struct {
-		name         string
-		settings     func(t *testing.T) *config.Config
-		expectedPlan map[string][]string
-		expectedErr  error
-	}{
-		{
-			name: "No context",
-			settings: func(t *testing.T) *config.Config {
-				s := makeDefaultSettings(t)
-				s.CurrentContext = "badCtx"
-				return s
-			},
-			expectedErr: config.ErrMissingConfig{What: "Context with name 'badCtx'"},
-		},
-		{
-			name:     "Valid Phase Plan",
-			settings: makeDefaultSettings,
-			expectedPlan: map[string][]string{
-				"group1": {
-					"isogen",
-					"remotedirect",
-					"initinfra",
-					"some_phase",
-					"capi_init",
-				},
-			},
-		},
-		{
-			name: "No Phase Plan",
-			settings: func(t *testing.T) *config.Config {
-				s := makeDefaultSettings(t)
-				m, err := s.CurrentContextManifest()
-				require.NoError(t, err)
-				m.SubPath = "no_plan_site"
-				m.MetadataPath = "no_plan_site/metadata.yaml"
-				return s
-			},
-			expectedErr: document.ErrDocNotFound{
-				Selector: document.Selector{
-					Selector: types.Selector{
-						Gvk: resid.Gvk{
-							Group:   "airshipit.org",
-							Version: "v1alpha1",
-							Kind:    "PhasePlan",
-						},
-					},
-				},
-			},
-		},
-	}
-
-	for _, test := range testCases {
-		tt := test
-		t.Run(tt.name, func(t *testing.T) {
-			cmd := phase.Cmd{Config: tt.settings(t)}
-			actualPlan, actualErr := cmd.Plan()
-			assert.Equal(t, tt.expectedErr, actualErr)
-			assert.Equal(t, tt.expectedPlan, actualPlan)
-		})
-	}
-}
-
-func TestGetPhase(t *testing.T) {
-	testCases := []struct {
-		name          string
-		settings      func(t *testing.T) *config.Config
-		phaseName     string
-		expectedPhase *airshipv1.Phase
-		expectedErr   error
-	}{
-		{
-			name: "No context",
-			settings: func(t *testing.T) *config.Config {
-				s := makeDefaultSettings(t)
-				s.CurrentContext = "badCtx"
-				return s
-			},
-			expectedErr: config.ErrMissingConfig{What: "Context with name 'badCtx'"},
-		},
-		{
-			name:      "Get existing phase",
-			settings:  makeDefaultSettings,
-			phaseName: "capi_init",
-			expectedPhase: &airshipv1.Phase{
-				TypeMeta: metav1.TypeMeta{
-					APIVersion: "airshipit.org/v1alpha1",
-					Kind:       "Phase",
-				},
-				ObjectMeta: metav1.ObjectMeta{
-					Name: "capi_init",
-				},
-				Config: airshipv1.PhaseConfig{
-					ExecutorRef: &corev1.ObjectReference{
-						Kind:       "Clusterctl",
-						APIVersion: "airshipit.org/v1alpha1",
-						Name:       "clusterctl-v1",
-					},
-					DocumentEntryPoint: "valid_site/phases",
-				},
-			},
-		},
-		{
-			name:      "Get non-existing phase",
-			settings:  makeDefaultSettings,
-			phaseName: "some_name",
-			expectedErr: document.ErrDocNotFound{
-				Selector: document.Selector{
-					Selector: types.Selector{
-						Gvk: resid.Gvk{
-							Group:   "airshipit.org",
-							Version: "v1alpha1",
-							Kind:    "Phase",
-						},
-						Name: "some_name",
-					},
-				},
-			},
-		},
-	}
-
-	for _, test := range testCases {
-		tt := test
-		t.Run(tt.name, func(t *testing.T) {
-			cmd := phase.Cmd{Config: tt.settings(t)}
-			actualPhase, actualErr := cmd.GetPhase(tt.phaseName)
-			assert.Equal(t, tt.expectedErr, actualErr)
-			assert.Equal(t, tt.expectedPhase, actualPhase)
-		})
-	}
-}
-
-func TestGetExecutor(t *testing.T) {
-	testCases := []struct {
-		name        string
-		settings    func(t *testing.T) *config.Config
-		phase       *airshipv1.Phase
-		expectedExc ifc.Executor
-		expectedErr error
-	}{
-		{
-			name: "No context",
-			settings: func(t *testing.T) *config.Config {
-				s := makeDefaultSettings(t)
-				s.CurrentContext = "badCtx"
-				return s
-			},
-			expectedErr: config.ErrMissingConfig{What: "Context with name 'badCtx'"},
-		},
-		{
-			name:     "Get non-existing executor",
-			settings: makeDefaultSettings,
-			phase: &airshipv1.Phase{
-				Config: airshipv1.PhaseConfig{
-					ExecutorRef: &corev1.ObjectReference{
-						APIVersion: "example.com/v1",
-						Kind:       "SomeKind",
-					},
-				},
-			},
-			expectedErr: document.ErrDocNotFound{
-				Selector: document.Selector{
-					Selector: types.Selector{
-						Gvk: resid.Gvk{
-							Group:   "example.com",
-							Version: "v1",
-							Kind:    "SomeKind",
-						},
-					},
-				},
-			},
-		},
-		{
-			name:     "Get unregistered executor",
-			settings: makeDefaultSettings,
-			phase: &airshipv1.Phase{
-				Config: airshipv1.PhaseConfig{
-					ExecutorRef: &corev1.ObjectReference{
-						APIVersion: "airshipit.org/v1alpha1",
-						Kind:       "SomeExecutor",
-						Name:       "executor-name",
-					},
-					DocumentEntryPoint: "valid_site/phases",
-				},
-			},
-			expectedErr: phase.ErrExecutorNotFound{
-				GVK: schema.GroupVersionKind{
-					Group:   "airshipit.org",
-					Version: "v1alpha1",
-					Kind:    "SomeExecutor",
-				},
-			},
-		},
-		{
-			name:     "Get registered executor",
-			settings: makeDefaultSettings,
-			phase: &airshipv1.Phase{
-				Config: airshipv1.PhaseConfig{
-					ExecutorRef: &corev1.ObjectReference{
-						APIVersion: "airshipit.org/v1alpha1",
-						Kind:       "KubernetesApply",
-						Name:       "kubernetes-apply",
-					},
-					DocumentEntryPoint: "valid_site/phases",
-				},
-			},
-			expectedExc: &applier.Executor{},
-		},
-	}
-
-	for _, test := range testCases {
-		tt := test
-		t.Run(tt.name, func(t *testing.T) {
-			cmd := phase.Cmd{Config: tt.settings(t)}
-			actualExc, actualErr := cmd.GetExecutor(tt.phase)
-			assert.Equal(t, tt.expectedErr, actualErr)
-			assertEqualExecutor(t, tt.expectedExc, actualExc)
-		})
-	}
-}
-
-// assertEqualExecutor allows to compare executor interfaces
-// check if we expect nil, and if so actual interface must be nil also otherwise compare types
-func assertEqualExecutor(t *testing.T, expected, actual ifc.Executor) {
-	if expected == nil {
-		assert.Nil(t, actual)
-		return
-	}
-	assert.IsType(t, expected, actual)
-}
-
-func makeDefaultSettings(t *testing.T) *config.Config {
-	airshipConfigPath := "testdata/airshipconfig.yaml"
-	kubeConfigPath := "testdata/kubeconfig.yaml"
-	testSettings, err := config.CreateFactory(&airshipConfigPath, &kubeConfigPath)()
-	require.NoError(t, err)
-	return testSettings
-}