github.com/ystia/yorc/v4@v4.3.0/plugin/action_test.go (about) 1 // Copyright 2018 Bull S.A.S. Atos Technologies - Bull, Rue Jean Jaures, B.P.68, 78340, Les Clayes-sous-Bois, France. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package plugin 16 17 import ( 18 "context" 19 "errors" 20 "testing" 21 "time" 22 23 plugin "github.com/hashicorp/go-plugin" 24 "github.com/stretchr/testify/assert" 25 "github.com/stretchr/testify/require" 26 27 "github.com/ystia/yorc/v4/config" 28 "github.com/ystia/yorc/v4/events" 29 "github.com/ystia/yorc/v4/prov" 30 ) 31 32 type mockActionOperator struct { 33 execactionCalled bool 34 ctx context.Context 35 conf config.Configuration 36 taskID, deploymentID string 37 action *prov.Action 38 contextCancelled bool 39 lof events.LogOptionalFields 40 } 41 42 func (m *mockActionOperator) ExecAction(ctx context.Context, conf config.Configuration, taskID, deploymentID string, action *prov.Action) (deregister bool, err error) { 43 44 m.execactionCalled = true 45 m.ctx = ctx 46 m.conf = conf 47 m.taskID = taskID 48 m.deploymentID = deploymentID 49 m.action = action 50 m.lof, _ = events.FromContext(ctx) 51 52 go func() { 53 <-m.ctx.Done() 54 m.contextCancelled = true 55 }() 56 if m.deploymentID == "TestCancel" { 57 <-m.ctx.Done() 58 } 59 if m.deploymentID == "TestFailure" { 60 return false, NewRPCError(errors.New("a failure occurred during plugin exec operation")) 61 } 62 return true, nil 63 } 64 65 func createMockActionOperatorClient(t *testing.T) (*mockActionOperator, *plugin.RPCClient) { 66 mock := new(mockActionOperator) 67 client, _ := plugin.TestPluginRPCConn( 68 t, 69 map[string]plugin.Plugin{ 70 ActionPluginName: &ActionPlugin{F: func() prov.ActionOperator { 71 return mock 72 }}, 73 }, 74 nil) 75 return mock, client 76 } 77 78 func setupExecActionTestEnv(t *testing.T) (*mockActionOperator, *plugin.RPCClient, 79 prov.ActionOperator, prov.Action, events.LogOptionalFields, context.Context) { 80 81 mock, client := createMockActionOperatorClient(t) 82 83 raw, err := client.Dispense(ActionPluginName) 84 require.Nil(t, err) 85 86 plugin := raw.(prov.ActionOperator) 87 testOperation := prov.Operation{ 88 Name: "testOperationName", 89 ImplementationArtifact: "tosca.artifacts.Implementation.Bash", 90 RelOp: prov.RelationshipOperation{ 91 IsRelationshipOperation: true, 92 RequirementIndex: "1", 93 TargetNodeName: "AnotherNode", 94 }, 95 } 96 testAsyncOperation := prov.AsyncOperation{ 97 DeploymentID: "testDeploymentID", 98 TaskID: "testTaskID", 99 ExecutionID: "testExecutionID", 100 WorkflowName: "testWorkflowName", 101 StepName: "testStepName", 102 NodeName: "testNodeName", 103 Operation: testOperation, 104 } 105 testData := map[string]string{ 106 "data1": "value1", 107 "data2": "value2", 108 } 109 testAction := prov.Action{ 110 ID: "testActionID", 111 ActionType: "testActionType", 112 AsyncOperation: testAsyncOperation, 113 Data: testData, 114 } 115 lof := events.LogOptionalFields{ 116 events.WorkFlowID: "testWF", 117 events.InterfaceName: "action", 118 events.OperationName: "testOperationName", 119 } 120 ctx := events.NewContext(context.Background(), lof) 121 122 return mock, client, plugin, testAction, lof, ctx 123 124 } 125 func TestActionOperatorExecAction(t *testing.T) { 126 t.Parallel() 127 mock, client, plugin, action, lof, ctx := setupExecActionTestEnv(t) 128 defer client.Close() 129 deregister, err := plugin.ExecAction( 130 ctx, 131 config.Configuration{Consul: config.Consul{Address: "test", Datacenter: "testdc"}}, 132 "testTaskID", "testDeploymentID", &action) 133 require.NoError(t, err, "Failed to cal plugin ExecAction") 134 require.True(t, mock.execactionCalled) 135 require.Equal(t, "test", mock.conf.Consul.Address) 136 require.Equal(t, "testdc", mock.conf.Consul.Datacenter) 137 require.Equal(t, "testTaskID", mock.taskID) 138 require.Equal(t, "testDeploymentID", mock.deploymentID) 139 require.Equal(t, action, *mock.action) 140 require.True(t, deregister, "Wrong return value for ExecAction") 141 assert.Equal(t, lof, mock.lof) 142 } 143 144 func TestActionOperatorExecActionWrongContext(t *testing.T) { 145 t.Parallel() 146 _, client, plugin, action, _, _ := setupExecActionTestEnv(t) 147 defer client.Close() 148 var wrongContext context.Context 149 _, err := plugin.ExecAction( 150 wrongContext, 151 config.Configuration{Consul: config.Consul{Address: "test", Datacenter: "testdc"}}, 152 "testTaskID", "testDeploymentID", &action) 153 require.Error(t, err, "Expected an error calling ExecAction with wrong context") 154 155 } 156 157 func TestActionOperatorExecActionWithFailure(t *testing.T) { 158 t.Parallel() 159 mock, client, plugin, action, _, ctx := setupExecActionTestEnv(t) 160 defer client.Close() 161 deregister, err := plugin.ExecAction( 162 ctx, 163 config.Configuration{Consul: config.Consul{Address: "test", Datacenter: "testdc"}}, 164 "testTaskID", "TestFailure", &action) 165 require.True(t, mock.execactionCalled) 166 require.Error(t, err, "An error was expected during executing plugin operation") 167 require.EqualError(t, err, "a failure occurred during plugin exec operation") 168 require.False(t, deregister, "Wrong return value for ExecAction") 169 } 170 171 func TestActionOperatorExecActionWithCancel(t *testing.T) { 172 t.Parallel() 173 mock, client := createMockActionOperatorClient(t) 174 defer client.Close() 175 176 raw, err := client.Dispense(ActionPluginName) 177 require.NoError(t, err, "Unexpected error calling client.Dispense() for Action Plugin") 178 179 plugin := raw.(prov.ActionOperator) 180 lof := events.LogOptionalFields{ 181 events.WorkFlowID: "testWF", 182 events.InterfaceName: "delegate", 183 events.OperationName: "myTest", 184 } 185 ctx := events.NewContext(context.Background(), lof) 186 ctx, cancelF := context.WithCancel(ctx) 187 go func() { 188 _, err := plugin.ExecAction( 189 ctx, 190 config.Configuration{Consul: config.Consul{Address: "test", Datacenter: "testdc"}}, 191 "testTaskID", "TestCancel", &prov.Action{}) 192 require.NoError(t, err) 193 }() 194 cancelF() 195 // Wait for cancellation signal to be dispatched 196 time.Sleep(50 * time.Millisecond) 197 require.True(t, mock.contextCancelled, "Context not cancelled") 198 } 199 200 func TestActionOperatorGetActionTypes(t *testing.T) { 201 mock := new(mockActionOperator) 202 client, _ := plugin.TestPluginRPCConn( 203 t, 204 map[string]plugin.Plugin{ 205 ActionPluginName: &ActionPlugin{ 206 F: func() prov.ActionOperator { 207 return mock 208 }, 209 ActionTypes: []string{"TestActionType1", "TestActionType2"}}, 210 }, 211 nil) 212 defer client.Close() 213 raw, err := client.Dispense(ActionPluginName) 214 require.NoError(t, err, "Unexpected error calling client.Dispense() for Action Plugin") 215 plugin := raw.(ActionOperator) 216 217 actionTypes, err := plugin.GetActionTypes() 218 require.NoError(t, err, "Unexpected error calling plugin.GetActionTypes()") 219 require.Len(t, actionTypes, 2) 220 require.Contains(t, actionTypes, "TestActionType1") 221 require.Contains(t, actionTypes, "TestActionType2") 222 223 }