From a6363f1c1e65bbeb43c0abf21e2be15d6cc019a1 Mon Sep 17 00:00:00 2001
From: Ruslan Aliev <raliev@mirantis.com>
Date: Wed, 7 Oct 2020 22:58:00 -0500
Subject: [PATCH] Run image build command like a phase

We perform almost the same activities prior calling createBootstrapIso
function like we do that in phase executor, also image build command
now is techically outdated since it doesn't work because it fails with
"phase document was not found" error. This patch removes unnecessary
functions like GenerateBootstrapIso and runs image build command in
a phase way. Test coverage increases slightly (by 0.2%) by removing
partially duplicating code which was not tested properly. Minor bug
fixes included.

Change-Id: I545004cea721164838b296ae647a7651cde248ff
Signed-off-by: Ruslan Aliev <raliev@mirantis.com>
---
 cmd/image/build.go                            | 10 ++-
 pkg/bootstrap/isogen/command.go               | 66 +------------------
 pkg/bootstrap/isogen/command_test.go          | 66 -------------------
 pkg/bootstrap/isogen/executor.go              |  4 +-
 pkg/bootstrap/isogen/testdata/config/config   | 34 ----------
 .../isogen/testdata/config/kubeconfig         | 17 -----
 .../bootstrap/image_configuration.yaml        | 12 ----
 .../ephemeral/bootstrap/kustomization.yaml    |  2 -
 .../bootstrap/image_configuration.yaml        | 10 ---
 .../ephemeral/bootstrap/kustomization.yaml    |  2 -
 .../bootstrap/image_configuration.yaml        | 11 ----
 .../ephemeral/bootstrap/kustomization.yaml    |  2 -
 pkg/phase/command.go                          |  3 +-
 pkg/phase/ifc/executor.go                     |  3 +-
 testdata/k8s/kubeconfig.yaml                  | 17 -----
 15 files changed, 14 insertions(+), 245 deletions(-)
 delete mode 100644 pkg/bootstrap/isogen/testdata/config/config
 delete mode 100644 pkg/bootstrap/isogen/testdata/config/kubeconfig
 delete mode 100644 pkg/bootstrap/isogen/testdata/missingkinddoc/site/test-site/ephemeral/bootstrap/image_configuration.yaml
 delete mode 100644 pkg/bootstrap/isogen/testdata/missingkinddoc/site/test-site/ephemeral/bootstrap/kustomization.yaml
 delete mode 100644 pkg/bootstrap/isogen/testdata/missingmetadoc/site/test-site/ephemeral/bootstrap/image_configuration.yaml
 delete mode 100644 pkg/bootstrap/isogen/testdata/missingmetadoc/site/test-site/ephemeral/bootstrap/kustomization.yaml
 delete mode 100644 pkg/bootstrap/isogen/testdata/missingvoldoc/site/test-site/ephemeral/bootstrap/image_configuration.yaml
 delete mode 100644 pkg/bootstrap/isogen/testdata/missingvoldoc/site/test-site/ephemeral/bootstrap/kustomization.yaml
 delete mode 100644 testdata/k8s/kubeconfig.yaml

diff --git a/cmd/image/build.go b/cmd/image/build.go
index f6ecd6856..dff470d18 100644
--- a/cmd/image/build.go
+++ b/cmd/image/build.go
@@ -17,19 +17,23 @@ package image
 import (
 	"github.com/spf13/cobra"
 
-	"opendev.org/airship/airshipctl/pkg/bootstrap/isogen"
 	"opendev.org/airship/airshipctl/pkg/config"
+	"opendev.org/airship/airshipctl/pkg/phase"
 )
 
 // NewImageBuildCommand creates a new command with the capability to build an ISO image.
 func NewImageBuildCommand(cfgFactory config.Factory) *cobra.Command {
 	var progress bool
-
 	cmd := &cobra.Command{
 		Use:   "build",
 		Short: "Build ISO image",
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return isogen.GenerateBootstrapIso(cfgFactory, progress)
+			p := &phase.RunCommand{
+				Factory: cfgFactory,
+				Options: phase.RunFlags{Progress: progress},
+			}
+			p.Options.PhaseID.Name = config.BootstrapPhase
+			return p.RunE()
 		},
 	}
 
diff --git a/pkg/bootstrap/isogen/command.go b/pkg/bootstrap/isogen/command.go
index 3fb341bbf..bca644956 100644
--- a/pkg/bootstrap/isogen/command.go
+++ b/pkg/bootstrap/isogen/command.go
@@ -16,7 +16,6 @@ package isogen
 
 import (
 	"bufio"
-	"context"
 	"fmt"
 	"io"
 	"os"
@@ -65,69 +64,6 @@ type BootstrapIsoOptions struct {
 	writer   io.Writer
 }
 
-// GenerateBootstrapIso will generate data for cloud init and start ISO builder container
-// TODO (vkuzmin): Remove this public function and move another functions
-// to the executor module when the phases will be ready
-func GenerateBootstrapIso(cfgFactory config.Factory, progress bool) error {
-	ctx := context.Background()
-
-	globalConf, err := cfgFactory()
-	if err != nil {
-		return err
-	}
-
-	root, err := globalConf.CurrentContextEntryPoint(config.BootstrapPhase)
-	if err != nil {
-		return err
-	}
-	docBundle, err := document.NewBundleByPath(root)
-	if err != nil {
-		return err
-	}
-
-	imageConfiguration := &v1alpha1.ImageConfiguration{}
-	selector, err := document.NewSelector().ByObject(imageConfiguration, v1alpha1.Scheme)
-	if err != nil {
-		return err
-	}
-	doc, err := docBundle.SelectOne(selector)
-	if err != nil {
-		return err
-	}
-
-	err = doc.ToAPIObject(imageConfiguration, v1alpha1.Scheme)
-	if err != nil {
-		return err
-	}
-	if err = verifyInputs(imageConfiguration); err != nil {
-		return err
-	}
-
-	log.Print("Creating ISO builder container")
-	builder, err := container.NewContainer(
-		&ctx, imageConfiguration.Container.ContainerRuntime,
-		imageConfiguration.Container.Image)
-	if err != nil {
-		return err
-	}
-
-	bootstrapIsoOptions := BootstrapIsoOptions{
-		docBundle: docBundle,
-		builder:   builder,
-		doc:       doc,
-		cfg:       imageConfiguration,
-		debug:     log.DebugEnabled(),
-		progress:  progress,
-		writer:    log.Writer(),
-	}
-	err = bootstrapIsoOptions.createBootstrapIso()
-	if err != nil {
-		return err
-	}
-	log.Print("Checking artifacts")
-	return verifyArtifacts(imageConfiguration)
-}
-
 func verifyInputs(cfg *v1alpha1.ImageConfiguration) error {
 	if cfg.Container.Volume == "" {
 		return config.ErrMissingConfig{
@@ -227,7 +163,7 @@ func (opts BootstrapIsoOptions) createBootstrapIso() error {
 			case opts.debug:
 				log.Print("start reading container logs")
 				// either container log output or progress bar will be shown
-				if _, err = io.Copy(log.Writer(), cLogs); err != nil {
+				if _, err = io.Copy(opts.writer, cLogs); err != nil {
 					log.Debugf("failed to write container logs to log output %s", err)
 				}
 				log.Print("got EOF from container logs")
diff --git a/pkg/bootstrap/isogen/command_test.go b/pkg/bootstrap/isogen/command_test.go
index a3ff53bb5..5b7a9c677 100644
--- a/pkg/bootstrap/isogen/command_test.go
+++ b/pkg/bootstrap/isogen/command_test.go
@@ -254,72 +254,6 @@ func TestVerifyInputs(t *testing.T) {
 	}
 }
 
-func TestGenerateBootstrapIso(t *testing.T) {
-	airshipConfigPath := "testdata/config/config"
-
-	t.Run("ContextEntryPointError", func(t *testing.T) {
-		cfg, err := config.CreateFactory(&airshipConfigPath)()
-		require.NoError(t, err)
-		cfg.Manifests["default"].Repositories = make(map[string]*config.Repository)
-		settings := func() (*config.Config, error) {
-			return cfg, nil
-		}
-		expectedErr := config.ErrMissingPhaseRepo{}
-		actualErr := GenerateBootstrapIso(settings, false)
-		assert.Equal(t, expectedErr, actualErr)
-	})
-
-	t.Run("NewBundleByPathError", func(t *testing.T) {
-		cfg, err := config.CreateFactory(&airshipConfigPath)()
-		require.NoError(t, err)
-		cfg.Manifests["default"].TargetPath = "/nonexistent"
-		settings := func() (*config.Config, error) {
-			return cfg, nil
-		}
-		expectedErr := config.ErrMissingPhaseDocument{PhaseName: "bootstrap"}
-		actualErr := GenerateBootstrapIso(settings, false)
-		assert.Equal(t, expectedErr, actualErr)
-	})
-
-	t.Run("SelectOneError", func(t *testing.T) {
-		cfg, err := config.CreateFactory(&airshipConfigPath)()
-		require.NoError(t, err)
-		cfg.Manifests["default"].SubPath = "missingkinddoc/site/test-site"
-		settings := func() (*config.Config, error) {
-			return cfg, nil
-		}
-		expectedErr := document.ErrDocNotFound{
-			Selector: document.NewSelector().ByGvk("airshipit.org", "v1alpha1", "ImageConfiguration")}
-		actualErr := GenerateBootstrapIso(settings, false)
-		assert.Equal(t, expectedErr, actualErr)
-	})
-
-	t.Run("ToObjectError", func(t *testing.T) {
-		cfg, err := config.CreateFactory(&airshipConfigPath)()
-		require.NoError(t, err)
-		cfg.Manifests["default"].SubPath = "missingmetadoc/site/test-site"
-		settings := func() (*config.Config, error) {
-			return cfg, nil
-		}
-		expectedErrMessage := "missing metadata.name in object"
-		actualErr := GenerateBootstrapIso(settings, false)
-		require.NotNil(t, actualErr)
-		assert.Contains(t, actualErr.Error(), expectedErrMessage)
-	})
-
-	t.Run("verifyInputsError", func(t *testing.T) {
-		cfg, err := config.CreateFactory(&airshipConfigPath)()
-		require.NoError(t, err)
-		cfg.Manifests["default"].SubPath = "missingvoldoc/site/test-site"
-		settings := func() (*config.Config, error) {
-			return cfg, nil
-		}
-		expectedErr := config.ErrMissingConfig{What: "Must specify volume bind for ISO builder container"}
-		actualErr := GenerateBootstrapIso(settings, false)
-		assert.Equal(t, expectedErr, actualErr)
-	})
-}
-
 func TestShowProgress(t *testing.T) {
 	tests := []struct {
 		name   string
diff --git a/pkg/bootstrap/isogen/executor.go b/pkg/bootstrap/isogen/executor.go
index ead0e9053..9427bd879 100644
--- a/pkg/bootstrap/isogen/executor.go
+++ b/pkg/bootstrap/isogen/executor.go
@@ -121,8 +121,8 @@ func (c *Executor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
 		doc:       c.ExecutorDocument,
 		cfg:       c.imgConf,
 		debug:     log.DebugEnabled(),
-		progress:  false,
-		writer:    nil,
+		progress:  opts.Progress,
+		writer:    log.Writer(),
 	}
 
 	err := bootstrapOpts.createBootstrapIso()
diff --git a/pkg/bootstrap/isogen/testdata/config/config b/pkg/bootstrap/isogen/testdata/config/config
deleted file mode 100644
index 2046db657..000000000
--- a/pkg/bootstrap/isogen/testdata/config/config
+++ /dev/null
@@ -1,34 +0,0 @@
-apiVersion: airshipit.org/v1alpha1
-clusters:
-  default:
-    clusterType:
-      ephemeral:
-        clusterKubeconf: default_ephemeral
-        managementConfiguration: default
-contexts:
-  default:
-    contextKubeconf: default_ephemeral
-    manifest: default
-currentContext: default
-kind: Config
-managementConfiguration:
-  default:
-    insecure: true
-    systemActionRetries: 30
-    systemRebootDelay: 30
-    type: redfish
-manifests:
-  default:
-    phaseRepositoryName: primary
-    repositories:
-      primary:
-        checkout:
-          branch: master
-          commitHash: ""
-          force: false
-          tag: ""
-        url: https://opendev.org/airship/treasuremap
-    subPath: primary/site/test-site
-    targetPath: testdata
-users:
-  admin: {}
diff --git a/pkg/bootstrap/isogen/testdata/config/kubeconfig b/pkg/bootstrap/isogen/testdata/config/kubeconfig
deleted file mode 100644
index 0fc12972f..000000000
--- a/pkg/bootstrap/isogen/testdata/config/kubeconfig
+++ /dev/null
@@ -1,17 +0,0 @@
-apiVersion: v1
-clusters:
-- cluster:
-    server: https://172.17.0.1:6443
-  name: default_ephemeral
-contexts:
-- context:
-    cluster: default_ephemeral
-    user: admin
-  name: default
-current-context: default
-kind: Config
-preferences: {}
-users:
-- name: admin
-  user:
-    username: airship-admin
diff --git a/pkg/bootstrap/isogen/testdata/missingkinddoc/site/test-site/ephemeral/bootstrap/image_configuration.yaml b/pkg/bootstrap/isogen/testdata/missingkinddoc/site/test-site/ephemeral/bootstrap/image_configuration.yaml
deleted file mode 100644
index aecfb71f0..000000000
--- a/pkg/bootstrap/isogen/testdata/missingkinddoc/site/test-site/ephemeral/bootstrap/image_configuration.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: airshipit.org/v1alpha1
-kind: FakeImageConfiguration
-metadata:
-  name: default
-builder:
-  networkConfigFileName: network-config
-  outputMetadataFileName: output-metadata.yaml
-  userDataFileName: user-data
-container:
-  containerRuntime: docker
-  image: quay.io/airshipit/isogen:latest-debian_stable
-  volume: /srv/iso:/config
diff --git a/pkg/bootstrap/isogen/testdata/missingkinddoc/site/test-site/ephemeral/bootstrap/kustomization.yaml b/pkg/bootstrap/isogen/testdata/missingkinddoc/site/test-site/ephemeral/bootstrap/kustomization.yaml
deleted file mode 100644
index 86ef7db29..000000000
--- a/pkg/bootstrap/isogen/testdata/missingkinddoc/site/test-site/ephemeral/bootstrap/kustomization.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-resources:
-  - image_configuration.yaml
diff --git a/pkg/bootstrap/isogen/testdata/missingmetadoc/site/test-site/ephemeral/bootstrap/image_configuration.yaml b/pkg/bootstrap/isogen/testdata/missingmetadoc/site/test-site/ephemeral/bootstrap/image_configuration.yaml
deleted file mode 100644
index 91e86a591..000000000
--- a/pkg/bootstrap/isogen/testdata/missingmetadoc/site/test-site/ephemeral/bootstrap/image_configuration.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-apiVersion: airshipit.org/v1alpha1
-kind: ImageConfiguration
-builder:
-  networkConfigFileName: network-config
-  outputMetadataFileName: output-metadata.yaml
-  userDataFileName: user-data
-container:
-  containerRuntime: docker
-  image: quay.io/airshipit/isogen:latest-debian_stable
-  volume: /srv/iso:/config
diff --git a/pkg/bootstrap/isogen/testdata/missingmetadoc/site/test-site/ephemeral/bootstrap/kustomization.yaml b/pkg/bootstrap/isogen/testdata/missingmetadoc/site/test-site/ephemeral/bootstrap/kustomization.yaml
deleted file mode 100644
index 86ef7db29..000000000
--- a/pkg/bootstrap/isogen/testdata/missingmetadoc/site/test-site/ephemeral/bootstrap/kustomization.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-resources:
-  - image_configuration.yaml
diff --git a/pkg/bootstrap/isogen/testdata/missingvoldoc/site/test-site/ephemeral/bootstrap/image_configuration.yaml b/pkg/bootstrap/isogen/testdata/missingvoldoc/site/test-site/ephemeral/bootstrap/image_configuration.yaml
deleted file mode 100644
index cf65e1c06..000000000
--- a/pkg/bootstrap/isogen/testdata/missingvoldoc/site/test-site/ephemeral/bootstrap/image_configuration.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-apiVersion: airshipit.org/v1alpha1
-kind: ImageConfiguration
-metadata:
-  name: default
-builder:
-  networkConfigFileName: network-config
-  outputMetadataFileName: output-metadata.yaml
-  userDataFileName: user-data
-container:
-  containerRuntime: docker
-  image: quay.io/airshipit/isogen:latest-debian_stable
diff --git a/pkg/bootstrap/isogen/testdata/missingvoldoc/site/test-site/ephemeral/bootstrap/kustomization.yaml b/pkg/bootstrap/isogen/testdata/missingvoldoc/site/test-site/ephemeral/bootstrap/kustomization.yaml
deleted file mode 100644
index 86ef7db29..000000000
--- a/pkg/bootstrap/isogen/testdata/missingvoldoc/site/test-site/ephemeral/bootstrap/kustomization.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-resources:
-  - image_configuration.yaml
diff --git a/pkg/phase/command.go b/pkg/phase/command.go
index 17f9236a8..a2c915cdd 100644
--- a/pkg/phase/command.go
+++ b/pkg/phase/command.go
@@ -28,6 +28,7 @@ type RunFlags struct {
 	Timeout    time.Duration
 	PhaseID    ifc.ID
 	Kubeconfig string
+	Progress   bool
 }
 
 // RunCommand phase run command
@@ -55,7 +56,7 @@ func (c *RunCommand) RunE() error {
 	if err != nil {
 		return err
 	}
-	return phase.Run(ifc.RunOptions{DryRun: c.Options.DryRun, Timeout: c.Options.Timeout})
+	return phase.Run(ifc.RunOptions{DryRun: c.Options.DryRun, Timeout: c.Options.Timeout, Progress: c.Options.Progress})
 }
 
 // PlanCommand plan command
diff --git a/pkg/phase/ifc/executor.go b/pkg/phase/ifc/executor.go
index a1f6f8fde..c7da6a448 100644
--- a/pkg/phase/ifc/executor.go
+++ b/pkg/phase/ifc/executor.go
@@ -34,7 +34,8 @@ type Executor interface {
 
 // RunOptions holds options for run method
 type RunOptions struct {
-	DryRun bool
+	DryRun   bool
+	Progress bool
 
 	Timeout time.Duration
 }
diff --git a/testdata/k8s/kubeconfig.yaml b/testdata/k8s/kubeconfig.yaml
deleted file mode 100644
index 43c5d1c72..000000000
--- a/testdata/k8s/kubeconfig.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-apiVersion: v1
-clusters:
-- cluster:
-    server: https://172.17.0.1:6443
-  name: default_target
-contexts:
-- context:
-    cluster: default_target
-    user: admin
-  name: default
-current-context: ""
-kind: Config
-preferences: {}
-users:
-- name: admin
-  user:
-    username: airship-admin