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
This commit is contained in:
Vladislav Kuzmin 2020-12-04 17:51:18 +04:00
parent 80a8b53c42
commit 3de8b5b2b2
4 changed files with 107 additions and 11 deletions

View File

@ -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

View File

@ -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

View File

@ -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{}

View File

@ -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)
})
}
}