Skip to content

Commit

Permalink
Added execution context inputs to operations
Browse files Browse the repository at this point in the history
  • Loading branch information
laurentganne committed Jan 29, 2020
1 parent 2216dd3 commit 853eb13
Show file tree
Hide file tree
Showing 14 changed files with 276 additions and 42 deletions.
2 changes: 1 addition & 1 deletion deployments/definition_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ func testValueAssignmentsWithTopologyInputs(t *testing.T, ctx context.Context, d
}
for _, tt := range topoInputTests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetInputValue(ctx, deploymentID, tt.args.inputName, tt.args.nestedKeys...)
got, err := GetInputValue(ctx, nil, deploymentID, tt.args.inputName, tt.args.nestedKeys...)
if (err != nil) != tt.wantErr {
t.Errorf("GetInputValue() error = %v, wantErr %v", err, tt.wantErr)
return
Expand Down
21 changes: 17 additions & 4 deletions deployments/inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,31 @@ package deployments

import (
"context"

"github.com/pkg/errors"
"github.com/ystia/yorc/v4/tosca"
)

// GetInputValue tries to retrieve the value of the given input name.
//
// GetInputValue first checks if a non-empty field value exists for this input, if it doesn't then it checks for a non-empty field default.
// If none of them exists then it returns an empty string.
func GetInputValue(ctx context.Context, deploymentID, inputName string, nestedKeys ...string) (string, error) {
exist, paramDef, err := getParameterDefinition(ctx, deploymentID, inputName, "inputs")
if err != nil || !exist {
return "", err
func GetInputValue(ctx context.Context, inputs map[string]tosca.ParameterDefinition,
deploymentID, inputName string, nestedKeys ...string) (string, error) {

var err error
var paramDef *tosca.ParameterDefinition
paramDefVal, found := inputs[inputName]
if found {
paramDef = &paramDefVal
} else {
// No such input parameter in execution context, getting it in topology
found, paramDef, err = getParameterDefinition(ctx, deploymentID, inputName, "inputs")
if err != nil || !found {
return "", err
}
}

dataType := getTopologyInputOrOutputTypeFromParamDefinition(ctx, paramDef)
// Check first value
result, err := getValueAssignment(ctx, deploymentID, "", "", "", dataType, paramDef.Value, nestedKeys...)
Expand Down
3 changes: 2 additions & 1 deletion deployments/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,8 @@ func GetOperationInput(ctx context.Context, deploymentID, nodeName string, opera
}

for _, ins := range instances {
res, err = resolver(deploymentID).context(withNodeName(nodeName), withInstanceName(ins), withRequirementIndex(operation.RelOp.RequirementIndex)).resolveFunction(ctx, f)
res, err = resolver(deploymentID).context(withNodeName(nodeName), withInstanceName(ins),
withInputParameters(operation.Inputs), withRequirementIndex(operation.RelOp.RequirementIndex)).resolveFunction(ctx, f)
if err != nil {
return nil, err
}
Expand Down
9 changes: 8 additions & 1 deletion deployments/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type functionResolver struct {
nodeName string
instanceName string
requirementIndex string
inputs map[string]tosca.ParameterDefinition
}

type resolverContext func(*functionResolver)
Expand Down Expand Up @@ -80,6 +81,12 @@ func withRequirementIndex(reqIndex string) resolverContext {
}
}

func withInputParameters(inputs map[string]tosca.ParameterDefinition) resolverContext {
return func(fr *functionResolver) {
fr.inputs = inputs
}
}

func (fr *functionResolver) resolveFunction(ctx context.Context, fn *tosca.Function) (*TOSCAValue, error) {
if fn == nil {
return nil, errors.Errorf("Trying to resolve a nil function")
Expand Down Expand Up @@ -144,7 +151,7 @@ func (fr *functionResolver) resolveGetInput(ctx context.Context, operands []stri
return "", errors.Errorf("expecting at least one parameter for a get_input function")
}
args := getFuncNestedArgs(operands...)
return GetInputValue(ctx, fr.deploymentID, args[0], args[1:]...)
return GetInputValue(ctx, fr.inputs, fr.deploymentID, args[0], args[1:]...)
}

func (fr *functionResolver) resolveGetOperationOutput(ctx context.Context, operands []string) (string, error) {
Expand Down
18 changes: 18 additions & 0 deletions deployments/topo_output.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,24 @@ func getParameterDefinition(ctx context.Context, deploymentID, parameterName, pa
return exist, parameterDef, nil
}

// GetTopologyInputParameter returns the definition of a topology input parameter
func GetTopologyInputParameter(ctx context.Context, deploymentID, parameterName string) (bool, *tosca.ParameterDefinition, error) {

return getParameterDefinition(ctx, deploymentID, parameterName, "inputs")
}

// GetTopologyInputsNames returns the list of inputs for the deployment
func GetTopologyInputsNames(ctx context.Context, deploymentID string) ([]string, error) {
optPaths, err := storage.GetStore(types.StoreTypeDeployment).Keys(path.Join(consulutil.DeploymentKVPrefix, deploymentID, "/topology/inputs"))
if err != nil {
return nil, errors.Wrap(err, consulutil.ConsulGenericErrMsg)
}
for i := range optPaths {
optPaths[i] = path.Base(optPaths[i])
}
return optPaths, nil
}

// GetTopologyOutputValue returns the value of a given topology output
func GetTopologyOutputValue(ctx context.Context, deploymentID, outputName string, nestedKeys ...string) (*TOSCAValue, error) {
dataType, err := GetTopologyOutputType(ctx, deploymentID, outputName)
Expand Down
12 changes: 6 additions & 6 deletions prov/ansible/execution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func testExecution(t *testing.T, srv1 *testutil.TestServer) {

func testExecutionResolveInputsOnNode(t *testing.T, deploymentID, nodeName, nodeTypeName, operation string) {
ctx := context.Background()
op, err := operations.GetOperation(ctx, deploymentID, nodeName, operation, "", "")
op, err := operations.GetOperation(ctx, deploymentID, nodeName, operation, "", "", nil)
require.Nil(t, err)
execution := &executionCommon{
deploymentID: deploymentID,
Expand Down Expand Up @@ -252,7 +252,7 @@ func getWrappedCommandFunc(path string) func() string {
}

func testExecutionGenerateOnNode(t *testing.T, deploymentID, nodeName, operation string) {
op, err := operations.GetOperation(context.Background(), deploymentID, nodeName, operation, "", "")
op, err := operations.GetOperation(context.Background(), deploymentID, nodeName, operation, "", "", nil)
require.Nil(t, err)
execution, err := newExecution(context.Background(), GetConfig(), "taskIDNotUsedForNow", deploymentID, nodeName, op, nil)
require.Nil(t, err)
Expand Down Expand Up @@ -336,7 +336,7 @@ func testExecutionGenerateOnNode(t *testing.T, deploymentID, nodeName, operation

func testExecutionResolveInputsOnRelationshipSource(t *testing.T, deploymentID, nodeAName, nodeBName, operation, relationshipTypeName, requirementName, operationHost string) {
ctx := context.Background()
op, err := operations.GetOperation(ctx, deploymentID, nodeAName, operation, requirementName, operationHost)
op, err := operations.GetOperation(ctx, deploymentID, nodeAName, operation, requirementName, operationHost, nil)
require.Nil(t, err)
execution := &executionCommon{
deploymentID: deploymentID,
Expand Down Expand Up @@ -397,7 +397,7 @@ func testExecutionResolveInputsOnRelationshipSource(t *testing.T, deploymentID,
}

func testExecutionGenerateOnRelationshipSource(t *testing.T, deploymentID, nodeName, operation, requirementName, operationHost string) {
op, err := operations.GetOperation(context.Background(), deploymentID, nodeName, operation, requirementName, operationHost)
op, err := operations.GetOperation(context.Background(), deploymentID, nodeName, operation, requirementName, operationHost, nil)
require.Nil(t, err)
execution, err := newExecution(context.Background(), GetConfig(), "taskIDNotUsedForNow", deploymentID, nodeName, op, nil)
require.Nil(t, err)
Expand Down Expand Up @@ -481,7 +481,7 @@ func testExecutionGenerateOnRelationshipSource(t *testing.T, deploymentID, nodeN

func testExecutionResolveInputOnRelationshipTarget(t *testing.T, deploymentID, nodeAName, nodeBName, operation, relationshipTypeName, requirementName, operationHost string) {
ctx := context.Background()
op, err := operations.GetOperation(ctx, deploymentID, nodeAName, operation, requirementName, operationHost)
op, err := operations.GetOperation(ctx, deploymentID, nodeAName, operation, requirementName, operationHost, nil)
require.Nil(t, err)
execution := &executionCommon{
deploymentID: deploymentID,
Expand Down Expand Up @@ -540,7 +540,7 @@ func testExecutionResolveInputOnRelationshipTarget(t *testing.T, deploymentID, n
}

func testExecutionGenerateOnRelationshipTarget(t *testing.T, deploymentID, nodeName, operation, requirementName, operationHost string) {
op, err := operations.GetOperation(context.Background(), deploymentID, nodeName, operation, requirementName, operationHost)
op, err := operations.GetOperation(context.Background(), deploymentID, nodeName, operation, requirementName, operationHost, nil)
require.Nil(t, err)
execution, err := newExecution(context.Background(), GetConfig(), "taskIDNotUsedForNow", deploymentID, nodeName, op, nil)
require.Nil(t, err)
Expand Down
6 changes: 5 additions & 1 deletion prov/operations/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ import (
"github.com/ystia/yorc/v4/helper/stringutil"
"github.com/ystia/yorc/v4/log"
"github.com/ystia/yorc/v4/prov"
"github.com/ystia/yorc/v4/tosca"
)

// GetOperation returns a Prov.Operation structure describing precisely operation in order to execute it
func GetOperation(ctx context.Context, deploymentID, nodeName, operationName, requirementName, operationHost string) (prov.Operation, error) {
func GetOperation(ctx context.Context, deploymentID, nodeName, operationName, requirementName, operationHost string,
inputs map[string]tosca.ParameterDefinition) (prov.Operation, error) {

var (
implementingType, implementingNode, requirementIndex, targetNodeName string
err error
Expand Down Expand Up @@ -89,6 +92,7 @@ func GetOperation(ctx context.Context, deploymentID, nodeName, operationName, re
TargetRelationship: requirementName,
},
OperationHost: operationHost,
Inputs: inputs,
}
log.Debugf("operation:%+v", op)
return op, nil
Expand Down
3 changes: 3 additions & 0 deletions prov/provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/ystia/yorc/v4/config"
"github.com/ystia/yorc/v4/events"
"github.com/ystia/yorc/v4/tosca"
)

// DelegateExecutor is the interface that wraps the ExecDelegate method
Expand All @@ -46,6 +47,8 @@ type Operation struct {
RelOp RelationshipOperation `json:"rel_op,omitempty"`
// Node on which operation should be executed
OperationHost string `json:"operation_host,omitempty"`
// Inputs parameters provided by the execution context of the operation
Inputs map[string]tosca.ParameterDefinition `json:"inputs,omitempty"`
}

// String implements the fmt.Stringer interface
Expand Down
2 changes: 1 addition & 1 deletion rest/dep_custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (s *Server) newCustomCommandHandler(w http.ResponseWriter, r *http.Request)
}

func (s *Server) getInputNameFromCustom(ctx context.Context, deploymentID, nodeName, interfaceName, customCName string) ([]string, error) {
op, err := operations.GetOperation(context.Background(), deploymentID, nodeName, interfaceName+"."+customCName, "", "")
op, err := operations.GetOperation(context.Background(), deploymentID, nodeName, interfaceName+"."+customCName, "", "", nil)
if err != nil {
return nil, err
}
Expand Down
101 changes: 100 additions & 1 deletion tasks/workflow/step.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,11 @@ func (s *step) runActivity(wfCtx context.Context, cfg config.Configuration, depl
case builder.ActivityTypeSetState:
setNodeStatus(wfCtx, s.t.taskID, deploymentID, s.Target, activity.Value())
case builder.ActivityTypeCallOperation:
op, err := operations.GetOperation(wfCtx, s.t.targetID, s.Target, activity.Value(), s.TargetRelationship, s.OperationHost)
inputParameters, err := s.getActivityInputParameters(wfCtx, activity, deploymentID, workflowName)
if err != nil {
return err
}
op, err := operations.GetOperation(wfCtx, s.t.targetID, s.Target, activity.Value(), s.TargetRelationship, s.OperationHost, inputParameters)
if err != nil {
if deployments.IsOperationNotImplemented(err) {
// Operation not implemented just skip it
Expand Down Expand Up @@ -376,6 +380,101 @@ func (s *step) runActivity(wfCtx context.Context, cfg config.Configuration, depl
return nil
}

func (s *step) getActivityInputParameters(ctx context.Context, activity builder.Activity,
deploymentID, workflowName string) (map[string]tosca.ParameterDefinition, error) {

// TODO: get activity input parameters

// Getting workflow inputs
wf, err := deployments.GetWorkflow(ctx, deploymentID, workflowName)
if err != nil {
return nil, err
}

result := make(map[string]tosca.ParameterDefinition)

for inputName, propDef := range wf.Inputs {

if _, ok := result[inputName]; ok {
// Already defined in activity
continue
}

inputValue, err := tasks.GetTaskInput(s.t.taskID, inputName)
var valueAssign *tosca.ValueAssignment
if err != nil {
if !tasks.IsTaskDataNotFoundError(err) {
return result, err
}

// No input value in task, defining an input parameter if this property
// has a default value or is defined in the topology
if propDef.Default == nil {
// No default value, and no input in this execution context
// => no parameter is defined in this execution context
// It can still be defined in the topology
found, paramDef, err := deployments.GetTopologyInputParameter(ctx, deploymentID, inputName)
if err != nil {
return result, err
}
if found {
valueAssign = paramDef.Value
if valueAssign == nil {
valueAssign = paramDef.Default
}

}
if propDef.Required != nil && *propDef.Required && valueAssign == nil {
return result, errors.Errorf("Missing required value for input %q in step:%q workflow:%q, deploymentID:%q, taskID:%q",
inputName, s.Name, workflowName, deploymentID, s.t.taskID)
}
}
} else {

valueAssign, err = tosca.ToValueAssignment(inputValue)
if err != nil {
return result, err
}
}

result[inputName] = tosca.ParameterDefinition{
Type: propDef.Type,
Description: propDef.Description,
Required: propDef.Required,
Default: propDef.Default,
Status: propDef.Status,
EntrySchema: propDef.EntrySchema,
Value: valueAssign,
}
}

// Getting inputs at the topology level that can be used
// if inputs aren't defined at lower levels (workflow or activity)
topologyInputNames, err := deployments.GetTopologyInputsNames(ctx, deploymentID)
if err != nil {
return result, err
}

for _, inputName := range topologyInputNames {
if _, ok := result[inputName]; ok {
// Already defined in lower levels
continue
}

found, paramDef, err := deployments.GetTopologyInputParameter(ctx, deploymentID, inputName)
if err != nil {
return result, err
}

if found {
result[inputName] = *paramDef
}

}

return result, err
}

func (s *step) registerInlineWorkflow(ctx context.Context, workflowName string) error {
events.WithContextOptionalFields(ctx).NewLogEntry(events.LogLevelINFO, s.t.targetID).RegisterAsString(fmt.Sprintf("Register workflow %q from taskID:%q, deploymentID:%q", workflowName, s.t.taskID, s.t.targetID))
wfOps, err := builder.BuildInitExecutionOperations(ctx, s.t.targetID, s.t.taskID, workflowName, true)
Expand Down
7 changes: 7 additions & 0 deletions tasks/workflow/testdata/test_input_types.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,17 @@ node_types:
greetings_user:
type: string
say_goodbye:
inputs:
goodbye_msg:
type: string
required: true
implementation:
type: ystia.yorc.tests.artifacts.Implementation.Custom
file: say_goodbye
say_hello:
inputs:
hello_msg:
type: string
implementation:
type: ystia.yorc.tests.artifacts.Implementation.Custom
file: say_hello
Expand Down
9 changes: 9 additions & 0 deletions tasks/workflow/testdata/test_topo_workflow_inputs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ imports:
- test_input_types.yaml

topology_template:
inputs:
hello_msg:
type: string
default: "Hello"
description: "Hello message"
node_templates:
VirtualMachine:
type: yorc.nodes.openstack.Compute
Expand Down Expand Up @@ -49,10 +54,13 @@ topology_template:
inputs:
greetings_user: {get_input: user}
say_goodbye:
goodbye_msg: {get_input: goodbye_msg}
implementation:
type: ystia.yorc.tests.artifacts.Implementation.Custom
file: say_goodbye
say_hello:
inputs:
hello_msg: {get_input: hello_msg}
implementation:
type: ystia.yorc.tests.artifacts.Implementation.Custom
file: say_hello
Expand All @@ -67,6 +75,7 @@ topology_template:
inputs:
user:
type: string
required: true
steps:
GreetingsComponent_say_goodbye:
target: GreetingsComponent
Expand Down
3 changes: 2 additions & 1 deletion tasks/workflow/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,8 @@ func (w *worker) runCustomCommand(ctx context.Context, t *taskExecution) (contex
if err != nil {
return ctx, err
}
op, err := operations.GetOperation(ctx, t.targetID, nodeName, interfaceName+"."+commandName, "", "")
// TODO define custom command input parameters
op, err := operations.GetOperation(ctx, t.targetID, nodeName, interfaceName+"."+commandName, "", "", nil)
if err != nil {
err = setNodeStatus(ctx, t.taskID, t.targetID, nodeName, tosca.NodeStateError.String())
if err != nil {
Expand Down
Loading

0 comments on commit 853eb13

Please sign in to comment.