From 3de8b5b2b2620419643a534484244cdece6e41b3 Mon Sep 17 00:00:00 2001 From: Vladislav Kuzmin Date: Fri, 4 Dec 2020 17:51:18 +0400 Subject: [PATCH] Implement kustomize sink in generic container executor Sink function allows to write configurations to an external system. Change-Id: If9c6904239a542ea4c2bef2920965b6d87feb1e6 Relates-To: #202 Relates-To: #369 --- manifests/phases/executors.yaml | 2 +- pkg/api/v1alpha1/genericcontainer_types.go | 6 +- pkg/phase/executors/container.go | 40 ++++++++++--- pkg/phase/executors/container_test.go | 70 ++++++++++++++++++++++ 4 files changed, 107 insertions(+), 11 deletions(-) diff --git a/manifests/phases/executors.yaml b/manifests/phases/executors.yaml index 42fd390f5..7e1a13cec 100644 --- a/manifests/phases/executors.yaml +++ b/manifests/phases/executors.yaml @@ -54,7 +54,7 @@ metadata: name: generic-container labels: airshipit.org/deploy-k8s: "false" -outputToStdout: true +kustomizeSinkOutputDir: "" spec: container: image: quay.io/sample/image:v0.0.1 diff --git a/pkg/api/v1alpha1/genericcontainer_types.go b/pkg/api/v1alpha1/genericcontainer_types.go index d0ef9ef2f..b0c5716cb 100644 --- a/pkg/api/v1alpha1/genericcontainer_types.go +++ b/pkg/api/v1alpha1/genericcontainer_types.go @@ -25,8 +25,10 @@ import ( type GenericContainer struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - // If set to will print output of RunFns to Stdout - PrintOutput bool `json:"printOutput,omitempty"` + // Executor will write output using kustomize sink if this parameter is specified. + // Else it will write output to STDOUT. + // This path relative to current site root. + KustomizeSinkOutputDir string `json:"kustomizeSinkOutputDir,omitempty"` // Settings for for a container Spec runtimeutil.FunctionSpec `json:"spec,omitempty"` // Config for the RunFns function in a custom format diff --git a/pkg/phase/executors/container.go b/pkg/phase/executors/container.go index d24d45822..07219d554 100644 --- a/pkg/phase/executors/container.go +++ b/pkg/phase/executors/container.go @@ -22,6 +22,7 @@ import ( "path/filepath" "sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil" + "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/runfn" kyaml "sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/yaml" @@ -37,12 +38,13 @@ var _ ifc.Executor = &ContainerExecutor{} // ContainerExecutor contains resources for generic container executor type ContainerExecutor struct { - ExecutorBundle document.Bundle - ExecutorDocument document.Document + PhaseEntryPointBasePath string + ExecutorBundle document.Bundle + ExecutorDocument document.Document ContConf *v1alpha1.GenericContainer RunFns runfn.RunFns - targetPath string + TargetPath string } // NewContainerExecutor creates instance of phase executor @@ -61,14 +63,15 @@ func NewContainerExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) { } return &ContainerExecutor{ - ExecutorBundle: bundle, - ExecutorDocument: cfg.ExecutorDocument, + PhaseEntryPointBasePath: cfg.Helper.PhaseEntryPointBasePath(), + ExecutorBundle: bundle, + ExecutorDocument: cfg.ExecutorDocument, ContConf: apiObj, RunFns: runfn.RunFns{ Functions: []*kyaml.RNode{}, }, - targetPath: cfg.Helper.TargetPath(), + TargetPath: cfg.Helper.TargetPath(), }, nil } @@ -102,7 +105,11 @@ func (c *ContainerExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) { c.SetMounts() - if c.ContConf.PrintOutput { + var fnsOutputBuffer bytes.Buffer + + if c.ContConf.KustomizeSinkOutputDir != "" { + c.RunFns.Output = &fnsOutputBuffer + } else { c.RunFns.Output = os.Stdout } @@ -111,6 +118,13 @@ func (c *ContainerExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) { return } + if c.ContConf.KustomizeSinkOutputDir != "" { + if err := c.WriteKustomizeSink(&fnsOutputBuffer); err != nil { + handleError(evtCh, err) + return + } + } + evtCh <- events.NewEvent().WithGenericContainerEvent(events.GenericContainerEvent{ Operation: events.GenericContainerStop, Message: "execution of the generic container finished", @@ -159,11 +173,21 @@ func (c *ContainerExecutor) SetMounts() { } storageMounts := c.ContConf.Spec.Container.StorageMounts for i, mount := range storageMounts { - storageMounts[i].Src = filepath.Join(c.targetPath, mount.Src) + storageMounts[i].Src = filepath.Join(c.TargetPath, mount.Src) } c.RunFns.StorageMounts = storageMounts } +// WriteKustomizeSink writes output to kustomize sink +func (c *ContainerExecutor) WriteKustomizeSink(fnsOutputBuffer *bytes.Buffer) error { + outputDirPath := filepath.Join(c.PhaseEntryPointBasePath, c.ContConf.KustomizeSinkOutputDir) + sinkOutputs := []kio.Writer{&kio.LocalPackageWriter{PackagePath: outputDirPath}} + err := kio.Pipeline{ + Inputs: []kio.Reader{&kio.ByteReader{Reader: fnsOutputBuffer}}, + Outputs: sinkOutputs}.Execute() + return err +} + // Validate executor configuration and documents func (c *ContainerExecutor) Validate() error { return errors.ErrNotImplemented{} diff --git a/pkg/phase/executors/container_test.go b/pkg/phase/executors/container_test.go index cc8e69d03..ce06d7df5 100644 --- a/pkg/phase/executors/container_test.go +++ b/pkg/phase/executors/container_test.go @@ -219,3 +219,73 @@ func TestPrepareFunctions(t *testing.T) { assert.Equal(t, transformedFunction, strFuncs) } + +func TestSetMounts(t *testing.T) { + testCases := []struct { + name string + targetPath string + in []runtimeutil.StorageMount + expectedOut []runtimeutil.StorageMount + }{ + { + name: "Empty TargetPath and mounts", + targetPath: "", + in: nil, + expectedOut: nil, + }, + { + name: "Empty TargetPath with Src and DstPath", + targetPath: "", + in: []runtimeutil.StorageMount{ + { + MountType: "bind", + Src: "src", + DstPath: "dst", + }, + }, + expectedOut: []runtimeutil.StorageMount{ + { + MountType: "bind", + Src: "src", + DstPath: "dst", + }, + }, + }, + { + name: "Not empty TargetPath with Src and DstPath", + targetPath: "target_path", + in: []runtimeutil.StorageMount{ + { + MountType: "bind", + Src: "src", + DstPath: "dst", + }, + }, + expectedOut: []runtimeutil.StorageMount{ + { + MountType: "bind", + Src: "target_path/src", + DstPath: "dst", + }, + }, + }, + } + + for _, test := range testCases { + tt := test + t.Run(tt.name, func(t *testing.T) { + c := executors.ContainerExecutor{ + ContConf: &v1alpha1.GenericContainer{ + Spec: runtimeutil.FunctionSpec{ + Container: runtimeutil.ContainerSpec{ + StorageMounts: tt.in, + }, + }, + }, + TargetPath: tt.targetPath, + } + c.SetMounts() + assert.Equal(t, c.RunFns.StorageMounts, tt.expectedOut) + }) + } +}