github.com/ystia/yorc/v4@v4.3.0/plugin/operation.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 "net/rpc" 20 "time" 21 22 plugin "github.com/hashicorp/go-plugin" 23 "github.com/pkg/errors" 24 25 "github.com/ystia/yorc/v4/config" 26 "github.com/ystia/yorc/v4/events" 27 "github.com/ystia/yorc/v4/prov" 28 ) 29 30 // OperationExecutor is an extension of prov.OperationExecutor that expose its supported node types 31 type OperationExecutor interface { 32 prov.OperationExecutor 33 // Returns a list of regexp matches for node types 34 GetSupportedArtifactTypes() ([]string, error) 35 } 36 37 // OperationPlugin is public for use by reflexion and should be considered as private to this package. 38 // Please do not use it directly. 39 type OperationPlugin struct { 40 F func() prov.OperationExecutor 41 SupportedTypes []string 42 } 43 44 // Server is public for use by reflexion and should be considered as private to this package. 45 // Please do not use it directly. 46 func (p *OperationPlugin) Server(b *plugin.MuxBroker) (interface{}, error) { 47 oes := &OperationExecutorServer{Broker: b, SupportedTypes: p.SupportedTypes} 48 if p.F != nil { 49 oes.OpExecutor = p.F() 50 } else if len(p.SupportedTypes) > 0 { 51 return nil, errors.New("If OperationSupportedArtifactTypes is defined then you have to defined an OperationFunc") 52 } 53 54 return oes, nil 55 } 56 57 // Client is public for use by reflexion and should be considered as private to this package. 58 // Please do not use it directly. 59 func (p *OperationPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { 60 return &OperationExecutorClient{Broker: b, Client: c}, nil 61 } 62 63 // OperationExecutorClient is public for use by reflexion and should be considered as private to this package. 64 // Please do not use it directly. 65 type OperationExecutorClient struct { 66 Broker *plugin.MuxBroker 67 Client *rpc.Client 68 } 69 70 // ExecAsyncOperation is public for use by reflexion and should be considered as private to this package. 71 // Please do not use it directly. 72 func (c *OperationExecutorClient) ExecAsyncOperation(ctx context.Context, conf config.Configuration, taskID, deploymentID, nodeName string, operation prov.Operation, stepName string) (*prov.Action, time.Duration, error) { 73 74 lof, ok := events.FromContext(ctx) 75 if !ok { 76 return nil, 0, errors.New("Missing contextual log optionnal fields") 77 } 78 79 id := c.Broker.NextId() 80 closeChan := make(chan struct{}, 0) 81 defer close(closeChan) 82 go clientMonitorContextCancellation(ctx, closeChan, id, c.Broker) 83 84 var resp OperationExecutorExecAsyncOperationResponse 85 args := &OperationExecutorExecAsyncOperationArgs{ 86 ChannelID: id, 87 Conf: conf, 88 TaskID: taskID, 89 DeploymentID: deploymentID, 90 NodeName: nodeName, 91 Operation: operation, 92 StepName: stepName, 93 LogOptionalFields: lof, 94 } 95 err := c.Client.Call("Plugin.ExecAsyncOperation", args, &resp) 96 if err != nil { 97 return nil, 0, errors.Wrap(err, "Failed to call ExecAsyncOperation for plugin") 98 } 99 return resp.Action, resp.MonitoringTimeInterval, toError(resp.Error) 100 } 101 102 // ExecOperation is public for use by reflexion and should be considered as private to this package. 103 // Please do not use it directly. 104 func (c *OperationExecutorClient) ExecOperation(ctx context.Context, conf config.Configuration, taskID, deploymentID, nodeName string, operation prov.Operation) error { 105 lof, ok := events.FromContext(ctx) 106 if !ok { 107 return errors.New("Missing contextual log optionnal fields") 108 } 109 id := c.Broker.NextId() 110 closeChan := make(chan struct{}, 0) 111 defer close(closeChan) 112 go clientMonitorContextCancellation(ctx, closeChan, id, c.Broker) 113 114 var resp OperationExecutorExecOperationResponse 115 args := &OperationExecutorExecOperationArgs{ 116 ChannelID: id, 117 Conf: conf, 118 TaskID: taskID, 119 DeploymentID: deploymentID, 120 NodeName: nodeName, 121 Operation: operation, 122 LogOptionalFields: lof, 123 } 124 err := c.Client.Call("Plugin.ExecOperation", args, &resp) 125 if err != nil { 126 return errors.Wrap(err, "Failed to call ExecOperation for plugin") 127 } 128 return toError(resp.Error) 129 } 130 131 // OperationExecutorServer is public for use by reflexion and should be considered as private to this package. 132 // Please do not use it directly. 133 type OperationExecutorServer struct { 134 Broker *plugin.MuxBroker 135 OpExecutor prov.OperationExecutor 136 SupportedTypes []string 137 } 138 139 // OperationExecutorExecOperationArgs is public for use by reflexion and should be considered as private to this package. 140 // Please do not use it directly. 141 type OperationExecutorExecOperationArgs struct { 142 ChannelID uint32 143 Conf config.Configuration 144 TaskID string 145 DeploymentID string 146 NodeName string 147 Operation prov.Operation 148 LogOptionalFields events.LogOptionalFields 149 } 150 151 // OperationExecutorExecOperationResponse is public for use by reflexion and should be considered as private to this package. 152 // Please do not use it directly. 153 type OperationExecutorExecOperationResponse struct { 154 Error *RPCError 155 } 156 157 // OperationExecutorExecAsyncOperationArgs is public for use by reflexion and should be considered as private to this package. 158 // Please do not use it directly. 159 type OperationExecutorExecAsyncOperationArgs struct { 160 ChannelID uint32 161 Conf config.Configuration 162 TaskID string 163 DeploymentID string 164 NodeName string 165 Operation prov.Operation 166 StepName string 167 LogOptionalFields events.LogOptionalFields 168 } 169 170 // OperationExecutorExecAsyncOperationResponse is public for use by reflexion and should be considered as private to this package. 171 // Please do not use it directly. 172 type OperationExecutorExecAsyncOperationResponse struct { 173 Action *prov.Action 174 MonitoringTimeInterval time.Duration 175 Error *RPCError 176 } 177 178 // ExecOperation is public for use by reflexion and should be considered as private to this package. 179 // Please do not use it directly. 180 func (s *OperationExecutorServer) ExecOperation(args *OperationExecutorExecOperationArgs, reply *OperationExecutorExecOperationResponse) error { 181 182 ctx, cancelFunc := context.WithCancel(events.NewContext(context.Background(), args.LogOptionalFields)) 183 defer cancelFunc() 184 185 go s.Broker.AcceptAndServe(args.ChannelID, &RPCContextCanceller{CancelFunc: cancelFunc}) 186 err := s.OpExecutor.ExecOperation(ctx, args.Conf, args.TaskID, args.DeploymentID, args.NodeName, args.Operation) 187 var resp OperationExecutorExecOperationResponse 188 if err != nil { 189 resp.Error = NewRPCError(err) 190 } 191 *reply = resp 192 return nil 193 } 194 195 // ExecAsyncOperation is public for use by reflexion and should be considered as private to this package. 196 // Please do not use it directly. 197 func (s *OperationExecutorServer) ExecAsyncOperation(args *OperationExecutorExecAsyncOperationArgs, reply *OperationExecutorExecAsyncOperationResponse) error { 198 199 ctx, cancelFunc := context.WithCancel(events.NewContext(context.Background(), args.LogOptionalFields)) 200 defer cancelFunc() 201 202 go s.Broker.AcceptAndServe(args.ChannelID, &RPCContextCanceller{CancelFunc: cancelFunc}) 203 var err error 204 var resp OperationExecutorExecAsyncOperationResponse 205 resp.Action, resp.MonitoringTimeInterval, err = s.OpExecutor.ExecAsyncOperation(ctx, args.Conf, args.TaskID, args.DeploymentID, args.NodeName, args.Operation, args.StepName) 206 if err != nil { 207 resp.Error = NewRPCError(err) 208 } 209 *reply = resp 210 return nil 211 } 212 213 // GetSupportedArtifactTypes is public for use by reflexion and should be considered as private to this package. 214 // Please do not use it directly. 215 func (s *OperationExecutorServer) GetSupportedArtifactTypes(_ interface{}, reply *OperationExecutorGetTypesResponse) error { 216 *reply = OperationExecutorGetTypesResponse{SupportedTypes: s.SupportedTypes} 217 return nil 218 } 219 220 // OperationExecutorGetTypesResponse is public for use by reflexion and should be considered as private to this package. 221 // Please do not use it directly. 222 type OperationExecutorGetTypesResponse struct { 223 SupportedTypes []string 224 Error *RPCError 225 } 226 227 // GetSupportedArtifactTypes is public for use by reflexion and should be considered as private to this package. 228 // Please do not use it directly. 229 func (c *OperationExecutorClient) GetSupportedArtifactTypes() ([]string, error) { 230 var resp OperationExecutorGetTypesResponse 231 err := c.Client.Call("Plugin.GetSupportedArtifactTypes", new(interface{}), &resp) 232 if err != nil { 233 return nil, errors.Wrap(err, "Failed to get supported types for plugin") 234 } 235 return resp.SupportedTypes, toError(resp.Error) 236 }