github.com/argoproj/argo-events@v1.9.1/sensors/triggers/apache-openwhisk/apache-openwhisk.go (about)

     1  /*
     2  Copyright 2020 BlackRock, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  package apache_openwhisk
    17  
    18  import (
    19  	"context"
    20  	"encoding/json"
    21  	"fmt"
    22  	"net/http"
    23  
    24  	"github.com/apache/openwhisk-client-go/whisk"
    25  	"go.uber.org/zap"
    26  
    27  	"github.com/argoproj/argo-events/common"
    28  	"github.com/argoproj/argo-events/common/logging"
    29  	apicommon "github.com/argoproj/argo-events/pkg/apis/common"
    30  	"github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
    31  	"github.com/argoproj/argo-events/sensors/policy"
    32  	"github.com/argoproj/argo-events/sensors/triggers"
    33  )
    34  
    35  // TriggerImpl implements the Trigger interface for OpenWhisk trigger.
    36  type TriggerImpl struct {
    37  	// OpenWhiskClient is OpenWhisk API client
    38  	OpenWhiskClient *whisk.Client
    39  	// Sensor object
    40  	Sensor *v1alpha1.Sensor
    41  	// Trigger definition
    42  	Trigger *v1alpha1.Trigger
    43  	// logger to log stuff
    44  	Logger *zap.SugaredLogger
    45  }
    46  
    47  // NewTriggerImpl returns a new TriggerImpl
    48  func NewTriggerImpl(sensor *v1alpha1.Sensor, trigger *v1alpha1.Trigger, openWhiskClients common.StringKeyedMap[*whisk.Client], logger *zap.SugaredLogger) (*TriggerImpl, error) {
    49  	openwhisktrigger := trigger.Template.OpenWhisk
    50  
    51  	client, ok := openWhiskClients.Load(trigger.Template.Name)
    52  	if !ok {
    53  		logger.Debugw("OpenWhisk trigger value", zap.Any("name", trigger.Template.Name), zap.Any("trigger", *trigger.Template.OpenWhisk))
    54  		logger.Infow("instantiating OpenWhisk client", zap.Any("trigger-name", trigger.Template.Name))
    55  
    56  		config, err := whisk.GetDefaultConfig()
    57  		if err != nil {
    58  			return nil, fmt.Errorf("failed to get default configuration, %w", err)
    59  		}
    60  
    61  		config.Host = openwhisktrigger.Host
    62  
    63  		if openwhisktrigger.AuthToken != nil {
    64  			token, err := common.GetSecretFromVolume(openwhisktrigger.AuthToken)
    65  			if err != nil {
    66  				return nil, fmt.Errorf("failed to retrieve auth token, %w", err)
    67  			}
    68  			config.AuthToken = token
    69  		}
    70  
    71  		if openwhisktrigger.Namespace != "" {
    72  			config.Namespace = openwhisktrigger.Namespace
    73  		}
    74  		if openwhisktrigger.Version != "" {
    75  			config.Version = openwhisktrigger.Version
    76  		}
    77  
    78  		logger.Debugw("configuration for OpenWhisk client", zap.Any("config", *config))
    79  
    80  		client, err = whisk.NewClient(http.DefaultClient, config)
    81  		if err != nil {
    82  			return nil, fmt.Errorf("failed to instantiate OpenWhisk client, %w", err)
    83  		}
    84  
    85  		openWhiskClients.Store(trigger.Template.Name, client)
    86  	}
    87  
    88  	return &TriggerImpl{
    89  		OpenWhiskClient: client,
    90  		Sensor:          sensor,
    91  		Trigger:         trigger,
    92  		Logger:          logger.With(logging.LabelTriggerType, apicommon.OpenWhiskTrigger),
    93  	}, nil
    94  }
    95  
    96  // GetTriggerType returns the type of the trigger
    97  func (t *TriggerImpl) GetTriggerType() apicommon.TriggerType {
    98  	return apicommon.OpenWhiskTrigger
    99  }
   100  
   101  // FetchResource fetches the trigger. As the OpenWhisk trigger simply executes a http request, there
   102  // is no need to fetch any resource from external source
   103  func (t *TriggerImpl) FetchResource(ctx context.Context) (interface{}, error) {
   104  	return t.Trigger.Template.OpenWhisk, nil
   105  }
   106  
   107  // ApplyResourceParameters applies parameters to the trigger resource
   108  func (t *TriggerImpl) ApplyResourceParameters(events map[string]*v1alpha1.Event, resource interface{}) (interface{}, error) {
   109  	fetchedResource, ok := resource.(*v1alpha1.OpenWhiskTrigger)
   110  	if !ok {
   111  		return nil, fmt.Errorf("failed to interpret the fetched trigger resource")
   112  	}
   113  
   114  	resourceBytes, err := json.Marshal(fetchedResource)
   115  	if err != nil {
   116  		return nil, fmt.Errorf("failed to marshal the OpenWhisk trigger resource, %w", err)
   117  	}
   118  	parameters := fetchedResource.Parameters
   119  	if parameters != nil {
   120  		updatedResourceBytes, err := triggers.ApplyParams(resourceBytes, parameters, events)
   121  		if err != nil {
   122  			return nil, err
   123  		}
   124  		var openwhisktrigger *v1alpha1.OpenWhiskTrigger
   125  		if err := json.Unmarshal(updatedResourceBytes, &openwhisktrigger); err != nil {
   126  			return nil, fmt.Errorf("failed to unmarshal the updated OpenWhisk trigger resource after applying resource parameters, %w", err)
   127  		}
   128  
   129  		t.Logger.Debugw("applied parameters to the OpenWhisk trigger", zap.Any("name", t.Trigger.Template.Name), zap.Any("trigger", *openwhisktrigger))
   130  
   131  		return openwhisktrigger, nil
   132  	}
   133  
   134  	return resource, nil
   135  }
   136  
   137  // Execute executes the trigger
   138  func (t *TriggerImpl) Execute(ctx context.Context, events map[string]*v1alpha1.Event, resource interface{}) (interface{}, error) {
   139  	var payload []byte
   140  	var err error
   141  
   142  	openwhisktrigger, ok := resource.(*v1alpha1.OpenWhiskTrigger)
   143  	if !ok {
   144  		return nil, fmt.Errorf("failed to interpret the OpenWhisk trigger resource")
   145  	}
   146  
   147  	if openwhisktrigger.Payload != nil {
   148  		payload, err = triggers.ConstructPayload(events, openwhisktrigger.Payload)
   149  		if err != nil {
   150  			return nil, err
   151  		}
   152  
   153  		t.Logger.Debugw("payload for the OpenWhisk action invocation", zap.Any("name", t.Trigger.Template.Name), zap.Any("payload", string(payload)))
   154  	}
   155  
   156  	response, status, err := t.OpenWhiskClient.Actions.Invoke(openwhisktrigger.ActionName, payload, true, true)
   157  	if err != nil {
   158  		return nil, fmt.Errorf("failed to invoke action %s, %w", openwhisktrigger.ActionName, err)
   159  	}
   160  
   161  	t.Logger.Debugw("response for the OpenWhisk action invocation", zap.Any("name", t.Trigger.Template.Name), zap.Any("response", response))
   162  
   163  	return status, nil
   164  }
   165  
   166  // ApplyPolicy applies policy on the trigger
   167  func (t *TriggerImpl) ApplyPolicy(ctx context.Context, resource interface{}) error {
   168  	if t.Trigger.Policy == nil || t.Trigger.Policy.Status == nil || t.Trigger.Policy.Status.Allow == nil {
   169  		return nil
   170  	}
   171  	response, ok := resource.(*http.Response)
   172  	if !ok {
   173  		return fmt.Errorf("failed to interpret the trigger execution response")
   174  	}
   175  
   176  	p := policy.NewStatusPolicy(response.StatusCode, t.Trigger.Policy.Status.GetAllow())
   177  
   178  	return p.ApplyPolicy(ctx)
   179  }