Skip to content
This repository has been archived by the owner on Jun 8, 2022. It is now read-only.

add valueFrom AppConfig in depedency requirement #223

Merged
merged 2 commits into from
Sep 28, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion apis/core/v1alpha2/core_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,11 +488,22 @@ type DataInputValueFrom struct {
// ConditionRequirement specifies the requirement to match a value.
type ConditionRequirement struct {
Operator ConditionOperator `json:"op"`
Value string `json:"value"`

// +optional
// Value is mutually exclusive with ValueFrom
Value string `json:"value,omitempty"`
// +optional
// This is mutually exclusive with Value
ValueFrom ValueFrom `json:"valueFrom,omitempty"`
// +optional
FieldPath string `json:"fieldPath,omitempty"`
}

// ValueFrom get value from AppConfig object by specify a path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// ValueFrom get value from AppConfig object by specify a path
// ValueFrom gets value from AppConfig object by specifying a path

type ValueFrom struct {
FieldPath string `json:"fieldPath"`
}

// ConditionOperator specifies the operator to match a value.
type ConditionOperator string

Expand Down
16 changes: 16 additions & 0 deletions apis/core/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,18 @@ spec:
to match a value.
type: string
value:
description: Value is mutually exclusive with ValueFrom
type: string
valueFrom:
description: This is mutually exclusive with Value
properties:
fieldPath:
type: string
required:
- fieldPath
type: object
required:
- op
- value
type: object
type: array
fieldPath:
Expand Down Expand Up @@ -239,10 +247,20 @@ spec:
operator to match a value.
type: string
value:
description: Value is mutually exclusive with
ValueFrom
type: string
valueFrom:
description: This is mutually exclusive with
Value
properties:
fieldPath:
type: string
required:
- fieldPath
type: object
required:
- op
- value
type: object
type: array
fieldPath:
Expand Down
77 changes: 52 additions & 25 deletions pkg/controller/v1alpha2/applicationconfiguration/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func (r *components) Render(ctx context.Context, ac *v1alpha2.ApplicationConfigu
ds := &v1alpha2.DependencyStatus{}
res := make([]Workload, 0, len(ac.Spec.Components))
for i, acc := range ac.Spec.Components {
unsatisfied, err := r.handleDependency(ctx, workloads[i], acc, dag)
unsatisfied, err := r.handleDependency(ctx, workloads[i], acc, dag, ac)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -381,10 +381,13 @@ func addDataOutputsToDAG(dag *dag, outs []v1alpha2.DataOutput, obj *unstructured
}
}

func (r *components) handleDependency(ctx context.Context, w *Workload, acc v1alpha2.ApplicationConfigurationComponent, dag *dag) ([]v1alpha2.UnstaifiedDependency, error) {
func (r *components) handleDependency(ctx context.Context, w *Workload, acc v1alpha2.ApplicationConfigurationComponent, dag *dag, ac *v1alpha2.ApplicationConfiguration) ([]v1alpha2.UnstaifiedDependency, error) {
uds := make([]v1alpha2.UnstaifiedDependency, 0)

unsatisfied, err := r.handleDataInput(ctx, acc.DataInputs, dag, w.Workload)
unstructuredAC, err := util.Object2Unstructured(ac)
if err != nil {
return nil, errors.Wrapf(err, "handleDataInput by convert AppConfig (%s) to unstructured object failed", ac.Name)
}
unsatisfied, err := r.handleDataInput(ctx, acc.DataInputs, dag, w.Workload, unstructuredAC)
if err != nil {
return nil, errors.Wrapf(err, "handleDataInput for workload (%s/%s) failed", w.Workload.GetNamespace(), w.Workload.GetName())
}
Expand All @@ -395,7 +398,7 @@ func (r *components) handleDependency(ctx context.Context, w *Workload, acc v1al

for i, ct := range acc.Traits {
trait := w.Traits[i]
unsatisfied, err := r.handleDataInput(ctx, ct.DataInputs, dag, &trait.Object)
unsatisfied, err := r.handleDataInput(ctx, ct.DataInputs, dag, &trait.Object, unstructuredAC)
if err != nil {
return nil, errors.Wrapf(err, "handleDataInput for trait (%s/%s) failed", trait.Object.GetNamespace(), trait.Object.GetName())
}
Expand Down Expand Up @@ -428,14 +431,14 @@ func makeUnsatisfiedDependency(obj *unstructured.Unstructured, s *dagSource, in
}
}

func (r *components) handleDataInput(ctx context.Context, ins []v1alpha2.DataInput, dag *dag, obj *unstructured.Unstructured) ([]v1alpha2.UnstaifiedDependency, error) {
func (r *components) handleDataInput(ctx context.Context, ins []v1alpha2.DataInput, dag *dag, obj, ac *unstructured.Unstructured) ([]v1alpha2.UnstaifiedDependency, error) {
uds := make([]v1alpha2.UnstaifiedDependency, 0)
for _, in := range ins {
s, ok := dag.Sources[in.ValueFrom.DataOutputName]
if !ok {
return nil, errors.Wrapf(ErrDataOutputNotExist, "DataOutputName (%s)", in.ValueFrom.DataOutputName)
}
val, ready, err := r.getDataInput(ctx, s)
val, ready, err := r.getDataInput(ctx, s, ac)
if err != nil {
return nil, errors.Wrap(err, "getDataInput failed")
}
Expand Down Expand Up @@ -480,7 +483,7 @@ func fillValue(obj *unstructured.Unstructured, fs []string, val interface{}) err
return nil
}

func (r *components) getDataInput(ctx context.Context, s *dagSource) (interface{}, bool, error) {
func (r *components) getDataInput(ctx context.Context, s *dagSource, ac *unstructured.Unstructured) (interface{}, bool, error) {
obj := s.ObjectRef
key := types.NamespacedName{
Namespace: obj.Namespace,
Expand All @@ -493,6 +496,7 @@ func (r *components) getDataInput(ctx context.Context, s *dagSource) (interface{
return nil, false, errors.Wrap(resource.IgnoreNotFound(err), fmt.Sprintf("failed to get object (%s)", key.String()))
}
paved := fieldpath.Pave(u.UnstructuredContent())
pavedAC := fieldpath.Pave(ac.UnstructuredContent())

rawval, err := paved.GetValue(obj.FieldPath)
if err != nil {
Expand All @@ -508,9 +512,9 @@ func (r *components) getDataInput(ctx context.Context, s *dagSource) (interface{
// For string input we will:
// - check its value not empty if no condition is given.
// - check its value against conditions if no field path is specified.
ok, err = matchValue(s.Conditions, val, paved)
ok, err = matchValue(s.Conditions, val, paved, pavedAC)
default:
ok, err = checkConditions(s.Conditions, paved, nil)
ok, err = checkConditions(s.Conditions, paved, nil, pavedAC)
}
if err != nil {
return nil, false, err
Expand All @@ -522,29 +526,52 @@ func (r *components) getDataInput(ctx context.Context, s *dagSource) (interface{
return rawval, true, nil
}

func matchValue(conds []v1alpha2.ConditionRequirement, val string, paved *fieldpath.Paved) (bool, error) {
func matchValue(conds []v1alpha2.ConditionRequirement, val string, paved, ac *fieldpath.Paved) (bool, error) {
// If no condition is specified, it is by default to check value not empty.
if len(conds) == 0 {
return val != "", nil
}

return checkConditions(conds, paved, &val)
return checkConditions(conds, paved, &val, ac)
}

func getCheckVal(m v1alpha2.ConditionRequirement, paved *fieldpath.Paved, val *string) (string, error) {
var checkVal string
switch {
case m.FieldPath != "":
return paved.GetString(m.FieldPath)
case val != nil:
checkVal = *val
default:
return "", errors.New("FieldPath not specified")
}
return checkVal, nil
}

func getExpectVal(m v1alpha2.ConditionRequirement, ac *fieldpath.Paved) (string, error) {
if m.Value != "" {
return m.Value, nil
}
if m.ValueFrom.FieldPath == "" || ac == nil {
return "", nil
}
var err error
value, err := ac.GetString(m.ValueFrom.FieldPath)
if err != nil {
return "", fmt.Errorf("get field path %s err %v", m.ValueFrom.FieldPath, err)
}
return value, nil
}

func checkConditions(conds []v1alpha2.ConditionRequirement, paved *fieldpath.Paved, val *string) (bool, error) {
func checkConditions(conds []v1alpha2.ConditionRequirement, paved *fieldpath.Paved, val *string, ac *fieldpath.Paved) (bool, error) {
for _, m := range conds {
var checkVal string
var err error
switch {
case m.FieldPath != "":
checkVal, err = paved.GetString(m.FieldPath)
if err != nil {
return false, err
}
case val != nil:
checkVal = *val
default:
return false, errors.New("FieldPath not specified")
checkVal, err := getCheckVal(m, paved, val)
if err != nil {
return false, err
}
m.Value, err = getExpectVal(m, ac)
if err != nil {
return false, fmt.Errorf("get field path %s err %v", m.ValueFrom.FieldPath, err)
}

switch m.Operator {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1068,7 +1068,7 @@ func TestMatchValue(t *testing.T) {

for name, tc := range cases {
t.Run(name, func(t *testing.T) {
matched, err := matchValue(tc.args.conds, tc.args.val, tc.args.paved)
matched, err := matchValue(tc.args.conds, tc.args.val, tc.args.paved, nil)
if err != nil {
t.Fatal(err)
}
Expand Down