Add new selector by ObjectReference

This selector will allow us to improve internal logic in places
where we create selectors by ObjectReference objects and reuse
the code.

Change-Id: I4e4808bfdffc4446e9df255e2ed0b7b8f47d135c
Signed-off-by: Ruslan Aliev <raliev@mirantis.com>
This commit is contained in:
Ruslan Aliev 2021-04-05 00:19:19 -05:00
parent a377007ad0
commit bd287ce369
8 changed files with 71 additions and 42 deletions

View File

@ -18,9 +18,9 @@ import (
"fmt" "fmt"
"strings" "strings"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/resid" "sigs.k8s.io/kustomize/kyaml/resid"
) )
@ -104,6 +104,15 @@ func (s Selector) ByObject(obj runtime.Object, scheme *runtime.Scheme) (Selector
return result, nil return result, nil
} }
// ByObjectReference select by ObjectReference
func (s Selector) ByObjectReference(objRef *corev1.ObjectReference) Selector {
refGVK := objRef.GroupVersionKind()
return NewSelector().
ByGvk(refGVK.Group, refGVK.Version, refGVK.Kind).
ByName(objRef.Name).
ByNamespace(objRef.Namespace)
}
// String is a convenience function which dumps all relevant information about a Selector in the following format: // String is a convenience function which dumps all relevant information about a Selector in the following format:
// [Key1=Value1, Key2=Value2, ...] // [Key1=Value1, Key2=Value2, ...]
func (s Selector) String() string { func (s Selector) String() string {

View File

@ -208,3 +208,41 @@ func TestSelectorToObject(t *testing.T) {
}) })
} }
} }
func TestSelectorByObjRef(t *testing.T) {
tests := []struct {
name string
objRef *k8sv1.ObjectReference
expectedSel document.Selector
}{
{
name: "Selector with GVK, name and namespace",
objRef: &k8sv1.ObjectReference{
Kind: "TestKind",
Name: "TestName",
APIVersion: "api.version/v1",
Namespace: "TestNamespace",
},
expectedSel: document.Selector{
Selector: types.Selector{
ResId: resid.ResId{
Gvk: resid.Gvk{
Group: "api.version",
Version: "v1",
Kind: "TestKind",
},
Name: "TestName",
Namespace: "TestNamespace",
},
},
},
},
}
for _, test := range tests {
tt := test
t.Run(tt.name, func(t *testing.T) {
actualSel := document.NewSelector().ByObjectReference(tt.objRef)
assert.Equal(t, tt.expectedSel, actualSel)
})
}
}

View File

@ -63,24 +63,10 @@ type phase struct {
processor events.EventProcessor processor events.EventProcessor
} }
func (p *phase) defaultBundleFactory() document.BundleFactoryFunc {
return document.BundleFactoryFromDocRoot(p.DocumentRoot)
}
func (p *phase) defaultDocFactory() document.DocFactoryFunc {
return func() (document.Document, error) {
return p.helper.ExecutorDoc(ifc.ID{Name: p.apiObj.Name, Namespace: p.apiObj.Namespace})
}
}
// Executor returns executor interface associated with the phase // Executor returns executor interface associated with the phase
func (p *phase) Executor() (ifc.Executor, error) { func (p *phase) Executor() (ifc.Executor, error) {
return p.executor(p.defaultDocFactory(), p.defaultBundleFactory()) executorDoc, err := p.helper.PhaseConfigBundle().SelectOne(
} document.NewSelector().ByObjectReference(p.apiObj.Config.ExecutorRef))
func (p *phase) executor(docFactory document.DocFactoryFunc,
bundleFactory document.BundleFactoryFunc) (ifc.Executor, error) {
executorDoc, err := docFactory()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -112,7 +98,7 @@ func (p *phase) executor(docFactory document.DocFactoryFunc,
return executorFactory( return executorFactory(
ifc.ExecutorConfig{ ifc.ExecutorConfig{
ClusterMap: cMap, ClusterMap: cMap,
BundleFactory: bundleFactory, BundleFactory: document.BundleFactoryFromDocRoot(p.DocumentRoot),
PhaseName: p.apiObj.Name, PhaseName: p.apiObj.Name,
KubeConfig: kubeconf, KubeConfig: kubeconf,
ExecutorDocument: executorDoc, ExecutorDocument: executorDoc,

View File

@ -233,13 +233,6 @@ func TestPhaseValidate(t *testing.T) {
registryFunc: fakeRegistry, registryFunc: fakeRegistry,
errContains: "executor identified by 'airshipit.org/v1alpha1, Kind=SomeExecutor' is not found", errContains: "executor identified by 'airshipit.org/v1alpha1, Kind=SomeExecutor' is not found",
}, },
{
name: "Error no executor",
configFunc: testConfig,
phaseID: ifc.ID{Name: "no_executor_phase"},
registryFunc: fakeRegistry,
errContains: "Phase name 'no_executor_phase', namespace '' must have executorRef field defined in config",
},
{ {
name: "Error executor validate", name: "Error executor validate",
configFunc: testConfig, configFunc: testConfig,

View File

@ -182,12 +182,7 @@ func (c *ContainerExecutor) Render(w io.Writer, o ifc.RenderOptions) error {
func (c *ContainerExecutor) setConfig() error { func (c *ContainerExecutor) setConfig() error {
if c.Container.ConfigRef != nil { if c.Container.ConfigRef != nil {
log.Debugf("Config reference is specified, looking for the object in config ref: '%v'", c.Container.ConfigRef) log.Debugf("Config reference is specified, looking for the object in config ref: '%v'", c.Container.ConfigRef)
gvk := c.Container.ConfigRef.GroupVersionKind() doc, err := c.Options.PhaseConfigBundle.SelectOne(document.NewSelector().ByObjectReference(c.Container.ConfigRef))
selector := document.NewSelector().
ByName(c.Container.ConfigRef.Name).
ByNamespace(c.Container.ConfigRef.Namespace).
ByGvk(gvk.Group, gvk.Version, gvk.Kind)
doc, err := c.Options.PhaseConfigBundle.SelectOne(selector)
if err != nil { if err != nil {
return err return err
} }

View File

@ -110,6 +110,13 @@ func (helper *Helper) Phase(phaseID ifc.ID) (*v1alpha1.Phase, error) {
if err = doc.ToAPIObject(phase, v1alpha1.Scheme); err != nil { if err = doc.ToAPIObject(phase, v1alpha1.Scheme); err != nil {
return nil, err return nil, err
} }
// Phase must contain an executor
if phase.Config.ExecutorRef == nil {
return nil, errors.ErrExecutorRefNotDefined{
PhaseName: phase.Name,
PhaseNamespace: phase.Namespace,
}
}
return phase, nil return phase, nil
} }
@ -271,19 +278,9 @@ func (helper *Helper) ExecutorDoc(phaseID ifc.ID) (document.Document, error) {
return nil, err return nil, err
} }
phaseConfig := phaseObj.Config
if phaseConfig.ExecutorRef == nil {
return nil, errors.ErrExecutorRefNotDefined{PhaseName: phaseID.Name, PhaseNamespace: phaseID.Namespace}
}
// Searching executor configuration document referenced in // Searching executor configuration document referenced in
// phase configuration // phase configuration
refGVK := phaseConfig.ExecutorRef.GroupVersionKind() return helper.phaseConfigBundle.SelectOne(document.NewSelector().ByObjectReference(phaseObj.Config.ExecutorRef))
selector := document.NewSelector().
ByGvk(refGVK.Group, refGVK.Version, refGVK.Kind).
ByName(phaseConfig.ExecutorRef.Name).
ByNamespace(phaseConfig.ExecutorRef.Namespace)
return helper.phaseConfigBundle.SelectOne(selector)
} }
// TargetPath returns manifest root // TargetPath returns manifest root

View File

@ -75,6 +75,12 @@ func TestHelperPhase(t *testing.T) {
phaseID: ifc.ID{Name: "some_name"}, phaseID: ifc.ID{Name: "some_name"},
errContains: "found no documents", errContains: "found no documents",
}, },
{
name: "Error no executor",
config: testConfig,
phaseID: ifc.ID{Name: "no_executor_phase"},
errContains: "Phase name 'no_executor_phase', namespace '' must have executorRef field defined in config",
},
{ {
name: "Error bundle path doesn't exist", name: "Error bundle path doesn't exist",
config: func(t *testing.T) *config.Config { config: func(t *testing.T) *config.Config {
@ -95,6 +101,7 @@ func TestHelperPhase(t *testing.T) {
require.Error(t, err) require.Error(t, err)
return return
} }
require.NoError(t, err)
require.NotNil(t, helper) require.NotNil(t, helper)
actualPhase, actualErr := helper.Phase(tt.phaseID) actualPhase, actualErr := helper.Phase(tt.phaseID)
if tt.errContains != "" { if tt.errContains != "" {

View File

@ -3,4 +3,8 @@ kind: Phase
metadata: metadata:
name: sample name: sample
config: config:
executorRef:
apiVersion: airshipit.org/v1alpha1
kind: SomeExecutor
name: executor-name
documentEntryPoint: entrypoint documentEntryPoint: entrypoint