Merge "Add generic container executor"
This commit is contained in:
commit
92ceea5581
@ -32,3 +32,16 @@ container:
|
|||||||
containerRuntime: docker
|
containerRuntime: docker
|
||||||
image: quay.io/airshipit/isogen:latest-ubuntu_focal
|
image: quay.io/airshipit/isogen:latest-ubuntu_focal
|
||||||
volume: /srv/iso:/config
|
volume: /srv/iso:/config
|
||||||
|
---
|
||||||
|
apiVersion: airshipit.org/v1alpha1
|
||||||
|
kind: GenericContainer
|
||||||
|
metadata:
|
||||||
|
name: generic-container
|
||||||
|
labels:
|
||||||
|
airshipit.org/deploy-k8s: "false"
|
||||||
|
outputToStdout: true
|
||||||
|
spec:
|
||||||
|
container:
|
||||||
|
image: quay.io/sample/image:v0.0.1
|
||||||
|
config: |
|
||||||
|
foo: bar
|
||||||
|
64
pkg/api/v1alpha1/genericcontainer_types.go
Normal file
64
pkg/api/v1alpha1/genericcontainer_types.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
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 v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// GenericContainer provides info about generic container
|
||||||
|
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"`
|
||||||
|
// Settings for for a container
|
||||||
|
Spec runtimeutil.FunctionSpec `json:"spec,omitempty"`
|
||||||
|
// Config for the RunFns function in a custom format
|
||||||
|
Config string `json:"config,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultGenericContainer can be used to safely unmarshal GenericContainer object without nil pointers
|
||||||
|
func DefaultGenericContainer() *GenericContainer {
|
||||||
|
return &GenericContainer{
|
||||||
|
Spec: runtimeutil.FunctionSpec{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *GenericContainer) DeepCopyInto(out *GenericContainer) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
|
||||||
|
out.Spec = in.Spec
|
||||||
|
out.Spec.Container = in.Spec.Container
|
||||||
|
out.Spec.Container.Network = in.Spec.Container.Network
|
||||||
|
if in.Spec.Container.StorageMounts != nil {
|
||||||
|
in, out := &in.Spec.Container.StorageMounts, &out.Spec.Container.StorageMounts
|
||||||
|
*out = make([]runtimeutil.StorageMount, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
out.Spec.Starlark = in.Spec.Starlark
|
||||||
|
out.Spec.Exec = in.Spec.Exec
|
||||||
|
if in.Spec.StorageMounts != nil {
|
||||||
|
in, out := &in.Spec.StorageMounts, &out.Spec.StorageMounts
|
||||||
|
*out = make([]runtimeutil.StorageMount, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
}
|
@ -52,6 +52,7 @@ func init() {
|
|||||||
&ReplacementTransformer{},
|
&ReplacementTransformer{},
|
||||||
&Templater{},
|
&Templater{},
|
||||||
&BootConfiguration{},
|
&BootConfiguration{},
|
||||||
|
&GenericContainer{},
|
||||||
)
|
)
|
||||||
_ = AddToScheme(Scheme) //nolint:errcheck
|
_ = AddToScheme(Scheme) //nolint:errcheck
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -288,6 +288,24 @@ func (in *EphemeralCluster) DeepCopy() *EphemeralCluster {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericContainer.
|
||||||
|
func (in *GenericContainer) DeepCopy() *GenericContainer {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(GenericContainer)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *GenericContainer) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ImageConfiguration) DeepCopyInto(out *ImageConfiguration) {
|
func (in *ImageConfiguration) DeepCopyInto(out *ImageConfiguration) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
202
pkg/container/executor.go
Normal file
202
pkg/container/executor.go
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
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 container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/runfn"
|
||||||
|
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"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/events"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/phase/ifc"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// yamlSeparator uses to separate yaml files
|
||||||
|
yamlSeparator = "---\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ ifc.Executor = &Executor{}
|
||||||
|
|
||||||
|
// Executor contains resources for generic container executor
|
||||||
|
type Executor struct {
|
||||||
|
ExecutorBundle document.Bundle
|
||||||
|
ExecutorDocument document.Document
|
||||||
|
|
||||||
|
ContConf *v1alpha1.GenericContainer
|
||||||
|
RunFns runfn.RunFns
|
||||||
|
targetPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterExecutor adds executor to phase executor registry
|
||||||
|
func RegisterExecutor(registry map[schema.GroupVersionKind]ifc.ExecutorFactory) error {
|
||||||
|
obj := v1alpha1.DefaultGenericContainer()
|
||||||
|
gvks, _, err := v1alpha1.Scheme.ObjectKinds(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registry[gvks[0]] = NewExecutor
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExecutor creates instance of phase executor
|
||||||
|
func NewExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
|
||||||
|
bundle, err := cfg.BundleFactory()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
apiObj := &v1alpha1.GenericContainer{
|
||||||
|
Spec: runtimeutil.FunctionSpec{},
|
||||||
|
}
|
||||||
|
err = cfg.ExecutorDocument.ToAPIObject(apiObj, v1alpha1.Scheme)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Executor{
|
||||||
|
ExecutorBundle: bundle,
|
||||||
|
ExecutorDocument: cfg.ExecutorDocument,
|
||||||
|
|
||||||
|
ContConf: apiObj,
|
||||||
|
RunFns: runfn.RunFns{
|
||||||
|
Functions: []*kyaml.RNode{},
|
||||||
|
},
|
||||||
|
targetPath: cfg.Helper.TargetPath(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run generic container as a phase runner
|
||||||
|
func (c *Executor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
|
||||||
|
defer close(evtCh)
|
||||||
|
|
||||||
|
evtCh <- events.NewEvent().WithGenericContainerEvent(events.GenericContainerEvent{
|
||||||
|
Operation: events.GenericContainerStart,
|
||||||
|
Message: "starting generic container",
|
||||||
|
})
|
||||||
|
|
||||||
|
if opts.DryRun {
|
||||||
|
log.Print("generic container will be executed")
|
||||||
|
evtCh <- events.NewEvent().WithGenericContainerEvent(events.GenericContainerEvent{
|
||||||
|
Operation: events.GenericContainerStop,
|
||||||
|
Message: "DryRun execution finished",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetInput(evtCh)
|
||||||
|
c.PrepareFunctions(evtCh)
|
||||||
|
c.SetMounts()
|
||||||
|
|
||||||
|
if c.ContConf.PrintOutput {
|
||||||
|
c.RunFns.Output = os.Stdout
|
||||||
|
}
|
||||||
|
|
||||||
|
err := c.RunFns.Execute()
|
||||||
|
if err != nil {
|
||||||
|
handleError(evtCh, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
evtCh <- events.NewEvent().WithGenericContainerEvent(events.GenericContainerEvent{
|
||||||
|
Operation: events.GenericContainerStop,
|
||||||
|
Message: "execution of the generic container finished",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInput sets input for function
|
||||||
|
func (c *Executor) SetInput(evtCh chan events.Event) {
|
||||||
|
docs, err := c.ExecutorBundle.GetAllDocuments()
|
||||||
|
if err != nil {
|
||||||
|
handleError(evtCh, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
docsBytes := make([]byte, 0)
|
||||||
|
for _, doc := range docs {
|
||||||
|
data, err := doc.AsYAML()
|
||||||
|
if err != nil {
|
||||||
|
handleError(evtCh, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
docsBytes = append(docsBytes, []byte(yamlSeparator)...)
|
||||||
|
docsBytes = append(docsBytes, data...)
|
||||||
|
}
|
||||||
|
c.RunFns.Input = bytes.NewReader(docsBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrepareFunctions prepares data for function
|
||||||
|
func (c *Executor) PrepareFunctions(evtCh chan events.Event) {
|
||||||
|
rnode, err := kyaml.Parse(c.ContConf.Config)
|
||||||
|
if err != nil {
|
||||||
|
handleError(evtCh, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Transform GenericContainer.Spec to annotation,
|
||||||
|
// because we need to specify runFns config in annotation
|
||||||
|
spec, err := yaml.Marshal(c.ContConf.Spec)
|
||||||
|
if err != nil {
|
||||||
|
handleError(evtCh, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
annotation := kyaml.SetAnnotation(runtimeutil.FunctionAnnotationKey, string(spec))
|
||||||
|
_, err = annotation.Filter(rnode)
|
||||||
|
if err != nil {
|
||||||
|
handleError(evtCh, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.RunFns.Functions = append(c.RunFns.Functions, rnode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMounts allows to set relative path for storage mounts to prevent security issues
|
||||||
|
func (c *Executor) SetMounts() {
|
||||||
|
if len(c.ContConf.Spec.Container.StorageMounts) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
storageMounts := c.ContConf.Spec.Container.StorageMounts
|
||||||
|
for i, mount := range storageMounts {
|
||||||
|
storageMounts[i].Src = filepath.Join(c.targetPath, mount.Src)
|
||||||
|
}
|
||||||
|
c.RunFns.StorageMounts = storageMounts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate executor configuration and documents
|
||||||
|
func (c *Executor) Validate() error {
|
||||||
|
return errors.ErrNotImplemented{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render executor documents
|
||||||
|
func (c *Executor) Render(_ io.Writer, _ ifc.RenderOptions) error {
|
||||||
|
return errors.ErrNotImplemented{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleError(ch chan<- events.Event, err error) {
|
||||||
|
ch <- events.NewEvent().WithErrorEvent(events.ErrorEvent{
|
||||||
|
Error: err,
|
||||||
|
})
|
||||||
|
}
|
253
pkg/container/executor_test.go
Normal file
253
pkg/container/executor_test.go
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
/*
|
||||||
|
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 container_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"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/container"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/document"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/events"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/phase"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/phase/ifc"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
executorDoc = `
|
||||||
|
apiVersion: airshipit.org/v1alpha1
|
||||||
|
kind: GenericContainer
|
||||||
|
metadata:
|
||||||
|
name: generic-container
|
||||||
|
labels:
|
||||||
|
airshipit.org/deploy-k8s: "false"
|
||||||
|
spec:
|
||||||
|
container:
|
||||||
|
image: quay.io/test/image:v0.0.1
|
||||||
|
config: |
|
||||||
|
apiVersion: airshipit.org/v1alpha1
|
||||||
|
kind: GenericContainerValues
|
||||||
|
object:
|
||||||
|
executables:
|
||||||
|
- name: test
|
||||||
|
cmdline: /tmp/x/script.sh
|
||||||
|
env:
|
||||||
|
- name: var
|
||||||
|
value: testval
|
||||||
|
volumeMounts:
|
||||||
|
- name: default
|
||||||
|
mountPath: /tmp/x
|
||||||
|
volumes:
|
||||||
|
- name: default
|
||||||
|
secret:
|
||||||
|
name: test-script
|
||||||
|
defaultMode: 0777`
|
||||||
|
//nolint: lll
|
||||||
|
transformedFunction = `apiVersion: airshipit.org/v1alpha1
|
||||||
|
kind: GenericContainerValues
|
||||||
|
object:
|
||||||
|
executables:
|
||||||
|
- name: test
|
||||||
|
cmdline: /tmp/x/script.sh
|
||||||
|
env:
|
||||||
|
- name: var
|
||||||
|
value: testval
|
||||||
|
volumeMounts:
|
||||||
|
- name: default
|
||||||
|
mountPath: /tmp/x
|
||||||
|
volumes:
|
||||||
|
- name: default
|
||||||
|
secret:
|
||||||
|
name: test-script
|
||||||
|
defaultMode: 0777
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: "container:\n image: quay.io/test/image:v0.0.1\n
|
||||||
|
\ network: {}\nexec: {}\nstarlark: {}\n"
|
||||||
|
`
|
||||||
|
singleExecutorBundlePath = "testdata/single"
|
||||||
|
firstDocInput = `---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: test-script
|
||||||
|
stringData:
|
||||||
|
script.sh: |
|
||||||
|
#!/bin/sh
|
||||||
|
echo WORKS! $var >&2
|
||||||
|
type: Opaque`
|
||||||
|
manyExecutorBundlePath = "testdata/many"
|
||||||
|
secondDocInput = `---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-node: "true"
|
||||||
|
name: master-0-bmc-secret
|
||||||
|
type: Opaque
|
||||||
|
`
|
||||||
|
yamlSeparator = "---\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRegisterExecutor(t *testing.T) {
|
||||||
|
registry := make(map[schema.GroupVersionKind]ifc.ExecutorFactory)
|
||||||
|
expectedGVK := schema.GroupVersionKind{
|
||||||
|
Group: "airshipit.org",
|
||||||
|
Version: "v1alpha1",
|
||||||
|
Kind: "GenericContainer",
|
||||||
|
}
|
||||||
|
err := container.RegisterExecutor(registry)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, found := registry[expectedGVK]
|
||||||
|
assert.True(t, found)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewExecutor(t *testing.T) {
|
||||||
|
execDoc, err := document.NewDocumentFromBytes([]byte(executorDoc))
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = container.NewExecutor(ifc.ExecutorConfig{
|
||||||
|
ExecutorDocument: execDoc,
|
||||||
|
BundleFactory: testBundleFactory(singleExecutorBundlePath),
|
||||||
|
Helper: makeDefaultHelper(t),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetInputSingleDocument(t *testing.T) {
|
||||||
|
bundle, err := document.NewBundleByPath(singleExecutorBundlePath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
execDoc, err := document.NewDocumentFromBytes([]byte(executorDoc))
|
||||||
|
require.NoError(t, err)
|
||||||
|
e := &container.Executor{
|
||||||
|
ExecutorBundle: bundle,
|
||||||
|
ExecutorDocument: execDoc,
|
||||||
|
|
||||||
|
ContConf: &v1alpha1.GenericContainer{
|
||||||
|
Spec: runtimeutil.FunctionSpec{},
|
||||||
|
},
|
||||||
|
RunFns: runfn.RunFns{
|
||||||
|
Functions: []*kyaml.RNode{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ch := make(chan events.Event)
|
||||||
|
e.SetInput(ch)
|
||||||
|
assert.Empty(t, ch)
|
||||||
|
|
||||||
|
// need to use kustomize here, because
|
||||||
|
// it changes order of lines in document
|
||||||
|
doc, err := document.NewDocumentFromBytes([]byte(firstDocInput))
|
||||||
|
require.NoError(t, err)
|
||||||
|
docBytes, err := doc.AsYAML()
|
||||||
|
require.NoError(t, err)
|
||||||
|
docBytes = append([]byte(yamlSeparator), docBytes...)
|
||||||
|
|
||||||
|
assert.Equal(t, bytes.NewReader(docBytes), e.RunFns.Input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetInputManyDocuments(t *testing.T) {
|
||||||
|
bundle, err := document.NewBundleByPath(manyExecutorBundlePath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
execDoc, err := document.NewDocumentFromBytes([]byte(executorDoc))
|
||||||
|
require.NoError(t, err)
|
||||||
|
e := &container.Executor{
|
||||||
|
ExecutorBundle: bundle,
|
||||||
|
ExecutorDocument: execDoc,
|
||||||
|
|
||||||
|
ContConf: &v1alpha1.GenericContainer{
|
||||||
|
Spec: runtimeutil.FunctionSpec{},
|
||||||
|
},
|
||||||
|
RunFns: runfn.RunFns{
|
||||||
|
Functions: []*kyaml.RNode{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ch := make(chan events.Event)
|
||||||
|
e.SetInput(ch)
|
||||||
|
assert.Empty(t, ch)
|
||||||
|
|
||||||
|
// need to use kustomize here, because
|
||||||
|
// it changes order of lines in document
|
||||||
|
docSecond, err := document.NewDocumentFromBytes([]byte(secondDocInput))
|
||||||
|
require.NoError(t, err)
|
||||||
|
docSecondBytes, err := docSecond.AsYAML()
|
||||||
|
require.NoError(t, err)
|
||||||
|
docBytes := append([]byte(yamlSeparator), docSecondBytes...)
|
||||||
|
|
||||||
|
docFirst, err := document.NewDocumentFromBytes([]byte(firstDocInput))
|
||||||
|
require.NoError(t, err)
|
||||||
|
docFirstBytes, err := docFirst.AsYAML()
|
||||||
|
require.NoError(t, err)
|
||||||
|
docBytes = append(docBytes, []byte(yamlSeparator)...)
|
||||||
|
docBytes = append(docBytes, docFirstBytes...)
|
||||||
|
|
||||||
|
assert.Equal(t, bytes.NewReader(docBytes), e.RunFns.Input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrepareFunctions(t *testing.T) {
|
||||||
|
bundle, err := document.NewBundleByPath(singleExecutorBundlePath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
execDoc, err := document.NewDocumentFromBytes([]byte(executorDoc))
|
||||||
|
require.NoError(t, err)
|
||||||
|
contConf := &v1alpha1.GenericContainer{
|
||||||
|
Spec: runtimeutil.FunctionSpec{},
|
||||||
|
}
|
||||||
|
err = execDoc.ToAPIObject(contConf, v1alpha1.Scheme)
|
||||||
|
require.NoError(t, err)
|
||||||
|
e := &container.Executor{
|
||||||
|
ExecutorBundle: bundle,
|
||||||
|
ExecutorDocument: execDoc,
|
||||||
|
|
||||||
|
ContConf: contConf,
|
||||||
|
RunFns: runfn.RunFns{
|
||||||
|
Functions: []*kyaml.RNode{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan events.Event)
|
||||||
|
e.PrepareFunctions(ch)
|
||||||
|
assert.Empty(t, ch)
|
||||||
|
strFuncs, err := e.RunFns.Functions[0].String()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, transformedFunction, strFuncs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBundleFactory(path string) document.BundleFactoryFunc {
|
||||||
|
return func() (document.Bundle, error) {
|
||||||
|
return document.NewBundleByPath(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeDefaultHelper(t *testing.T) ifc.Helper {
|
||||||
|
t.Helper()
|
||||||
|
cfg := config.NewConfig()
|
||||||
|
cfg.Manifests[config.AirshipDefaultManifest].TargetPath = "./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
|
||||||
|
}
|
2
pkg/container/testdata/many/kustomization.yaml
vendored
Normal file
2
pkg/container/testdata/many/kustomization.yaml
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
resources:
|
||||||
|
- secret.yaml
|
18
pkg/container/testdata/many/secret.yaml
vendored
Normal file
18
pkg/container/testdata/many/secret.yaml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: test-script
|
||||||
|
stringData:
|
||||||
|
script.sh: |
|
||||||
|
#!/bin/sh
|
||||||
|
echo WORKS! $var >&2
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-node: "true"
|
||||||
|
name: master-0-bmc-secret
|
||||||
|
type: Opaque
|
0
pkg/container/testdata/metadata.yaml
vendored
Normal file
0
pkg/container/testdata/metadata.yaml
vendored
Normal file
2
pkg/container/testdata/single/kustomization.yaml
vendored
Normal file
2
pkg/container/testdata/single/kustomization.yaml
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
resources:
|
||||||
|
- secret.yaml
|
9
pkg/container/testdata/single/secret.yaml
vendored
Normal file
9
pkg/container/testdata/single/secret.yaml
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: test-script
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
script.sh: |
|
||||||
|
#!/bin/sh
|
||||||
|
echo WORKS! $var >&2
|
@ -39,6 +39,8 @@ const (
|
|||||||
IsogenType
|
IsogenType
|
||||||
// BootstrapType event emitted by Bootstrap executor
|
// BootstrapType event emitted by Bootstrap executor
|
||||||
BootstrapType
|
BootstrapType
|
||||||
|
//GenericContainerType event emitted by GenericContainer
|
||||||
|
GenericContainerType
|
||||||
)
|
)
|
||||||
|
|
||||||
// Event holds all possible events that can be produced by airship
|
// Event holds all possible events that can be produced by airship
|
||||||
@ -51,6 +53,7 @@ type Event struct {
|
|||||||
ClusterctlEvent ClusterctlEvent
|
ClusterctlEvent ClusterctlEvent
|
||||||
IsogenEvent IsogenEvent
|
IsogenEvent IsogenEvent
|
||||||
BootstrapEvent BootstrapEvent
|
BootstrapEvent BootstrapEvent
|
||||||
|
GenericContainerEvent GenericContainerEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEvent create new event with timestamp
|
// NewEvent create new event with timestamp
|
||||||
@ -152,3 +155,26 @@ func (e Event) WithBootstrapEvent(concreteEvent BootstrapEvent) Event {
|
|||||||
e.BootstrapEvent = concreteEvent
|
e.BootstrapEvent = concreteEvent
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenericContainerOperation type
|
||||||
|
type GenericContainerOperation int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GenericContainerStart operation
|
||||||
|
GenericContainerStart GenericContainerOperation = iota
|
||||||
|
// GenericContainerStop operation
|
||||||
|
GenericContainerStop
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenericContainerEvent needs to to track events in GenericContainer executor
|
||||||
|
type GenericContainerEvent struct {
|
||||||
|
Operation GenericContainerOperation
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithGenericContainerEvent sets type and actual GenericContainer event
|
||||||
|
func (e Event) WithGenericContainerEvent(concreteEvent GenericContainerEvent) Event {
|
||||||
|
e.Type = GenericContainerType
|
||||||
|
e.GenericContainerEvent = concreteEvent
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
@ -55,7 +55,7 @@ func (p *DefaultProcessor) Process(ch <-chan Event) error {
|
|||||||
case ErrorType:
|
case ErrorType:
|
||||||
log.Printf("Received error on event channel %v", e.ErrorEvent)
|
log.Printf("Received error on event channel %v", e.ErrorEvent)
|
||||||
p.errors = append(p.errors, e.ErrorEvent.Error)
|
p.errors = append(p.errors, e.ErrorEvent.Error)
|
||||||
case ClusterctlType, IsogenType:
|
case ClusterctlType, IsogenType, GenericContainerType:
|
||||||
// TODO each event needs to be interface that allows us to print it for example
|
// TODO each event needs to be interface that allows us to print it for example
|
||||||
// Stringer interface or AsYAML for further processing.
|
// Stringer interface or AsYAML for further processing.
|
||||||
// For now we print the event object as is
|
// For now we print the event object as is
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||||
"opendev.org/airship/airshipctl/pkg/bootstrap/isogen"
|
"opendev.org/airship/airshipctl/pkg/bootstrap/isogen"
|
||||||
clusterctl "opendev.org/airship/airshipctl/pkg/clusterctl/client"
|
clusterctl "opendev.org/airship/airshipctl/pkg/clusterctl/client"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/container"
|
||||||
"opendev.org/airship/airshipctl/pkg/document"
|
"opendev.org/airship/airshipctl/pkg/document"
|
||||||
"opendev.org/airship/airshipctl/pkg/events"
|
"opendev.org/airship/airshipctl/pkg/events"
|
||||||
"opendev.org/airship/airshipctl/pkg/k8s/applier"
|
"opendev.org/airship/airshipctl/pkg/k8s/applier"
|
||||||
@ -48,6 +49,9 @@ func DefaultExecutorRegistry() map[schema.GroupVersionKind]ifc.ExecutorFactory {
|
|||||||
if err := isogen.RegisterExecutor(execMap); err != nil {
|
if err := isogen.RegisterExecutor(execMap); err != nil {
|
||||||
log.Fatal(ErrExecutorRegistration{ExecutorName: "isogen", Err: err})
|
log.Fatal(ErrExecutorRegistration{ExecutorName: "isogen", Err: err})
|
||||||
}
|
}
|
||||||
|
if err := container.RegisterExecutor(execMap); err != nil {
|
||||||
|
log.Fatal(ErrExecutorRegistration{ExecutorName: "generic-container", Err: err})
|
||||||
|
}
|
||||||
return execMap
|
return execMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user