go.uber.org/cadence@v1.2.9/test/test_utils.go (about)

     1  // Copyright (c) 2017-2020 Uber Technologies Inc.
     2  // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc.
     3  //
     4  // Permission is hereby granted, free of charge, to any person obtaining a copy
     5  // of this software and associated documentation files (the "Software"), to deal
     6  // in the Software without restriction, including without limitation the rights
     7  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8  // copies of the Software, and to permit persons to whom the Software is
     9  // furnished to do so, subject to the following conditions:
    10  //
    11  // The above copyright notice and this permission notice shall be included in
    12  // all copies or substantial portions of the Software.
    13  //
    14  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    20  // THE SOFTWARE.
    21  
    22  package test
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  	"os"
    28  	"strings"
    29  
    30  	"go.uber.org/yarpc"
    31  	"go.uber.org/yarpc/transport/grpc"
    32  	"go.uber.org/yarpc/transport/tchannel"
    33  
    34  	"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient"
    35  	"go.uber.org/cadence/compatibility"
    36  	"go.uber.org/cadence/workflow"
    37  
    38  	apiv1 "github.com/uber/cadence-idl/go/proto/api/v1"
    39  )
    40  
    41  type (
    42  	// Config contains the integration test configuration
    43  	Config struct {
    44  		ServiceAddr       string
    45  		ServiceName       string
    46  		IsStickyOff       bool
    47  		EnableGrpcAdapter bool
    48  		Debug             bool
    49  	}
    50  
    51  	// context.WithValue need this type instead of basic type string to avoid lint error
    52  	contextKey string
    53  )
    54  
    55  func newConfig() Config {
    56  	cfg := Config{
    57  		ServiceName:       "cadence-frontend",
    58  		ServiceAddr:       "127.0.0.1:7933",
    59  		EnableGrpcAdapter: false,
    60  		IsStickyOff:       true,
    61  	}
    62  	if name := getEnvServiceName(); name != "" {
    63  		cfg.ServiceName = name
    64  	}
    65  	if addr := getEnvServiceAddr(); addr != "" {
    66  		cfg.ServiceAddr = addr
    67  	}
    68  	if so := getEnvStickyOff(); so != "" {
    69  		cfg.IsStickyOff = so == "true"
    70  	}
    71  	if grpc := getEnableGrpcAdapter(); grpc != "" {
    72  		cfg.EnableGrpcAdapter = grpc == "true"
    73  	}
    74  	if debug := getDebug(); debug != "" {
    75  		cfg.Debug = debug == "true"
    76  	}
    77  	return cfg
    78  }
    79  
    80  func getEnvServiceName() string {
    81  	return strings.TrimSpace(os.Getenv("SERVICE_NAME"))
    82  }
    83  
    84  func getEnvServiceAddr() string {
    85  	return strings.TrimSpace(os.Getenv("SERVICE_ADDR"))
    86  }
    87  
    88  func getEnvStickyOff() string {
    89  	return strings.ToLower(strings.TrimSpace(os.Getenv("STICKY_OFF")))
    90  }
    91  
    92  func getEnableGrpcAdapter() string {
    93  	return strings.ToLower(strings.TrimSpace(os.Getenv("ENABLE_GRPC_ADAPTER")))
    94  }
    95  
    96  func getDebug() string {
    97  	return strings.ToLower(strings.TrimSpace(os.Getenv("DEBUG")))
    98  }
    99  
   100  type rpcClient struct {
   101  	workflowserviceclient.Interface
   102  	dispatcher *yarpc.Dispatcher
   103  }
   104  
   105  func (c *rpcClient) Close() {
   106  	c.dispatcher.Stop()
   107  }
   108  
   109  // newRPCClient builds and returns a new rpc client that is able to
   110  // make calls to the localhost cadence-server container
   111  func newRPCClient(
   112  	serviceName string, serviceAddr string) (*rpcClient, error) {
   113  	transport, err := tchannel.NewTransport(tchannel.ServiceName("integration-test"))
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	outbound := transport.NewSingleOutbound(serviceAddr)
   118  	dispatcher := yarpc.NewDispatcher(yarpc.Config{
   119  		Name: "integration-test",
   120  		Outbounds: yarpc.Outbounds{
   121  			serviceName: {
   122  				Unary: outbound,
   123  			},
   124  		},
   125  	})
   126  	if err := dispatcher.Start(); err != nil {
   127  		return nil, err
   128  	}
   129  	client := workflowserviceclient.New(dispatcher.ClientConfig(serviceName))
   130  	return &rpcClient{Interface: client, dispatcher: dispatcher}, nil
   131  }
   132  
   133  // newRPCClient builds and returns a new rpc client that is able to
   134  // make calls to the localhost cadence-server container via grpc adapter
   135  func newGRPCAdapterClient(
   136  	serviceName string, serviceAddr string) (*rpcClient, error) {
   137  	transport := grpc.NewTransport()
   138  	outbound := transport.NewSingleOutbound(serviceAddr)
   139  	dispatcher := yarpc.NewDispatcher(yarpc.Config{
   140  		Name: "integration-test",
   141  		Outbounds: yarpc.Outbounds{
   142  			serviceName: {
   143  				Unary: outbound,
   144  			},
   145  		},
   146  	})
   147  	if err := dispatcher.Start(); err != nil {
   148  		return nil, err
   149  	}
   150  	clientConfig := dispatcher.ClientConfig(serviceName)
   151  	adapter := compatibility.NewThrift2ProtoAdapter(
   152  		apiv1.NewDomainAPIYARPCClient(clientConfig),
   153  		apiv1.NewWorkflowAPIYARPCClient(clientConfig),
   154  		apiv1.NewWorkerAPIYARPCClient(clientConfig),
   155  		apiv1.NewVisibilityAPIYARPCClient(clientConfig))
   156  	return &rpcClient{Interface: adapter, dispatcher: dispatcher}, nil
   157  }
   158  
   159  // stringMapPropagator propagates the list of keys across a workflow,
   160  // interpreting the payloads as strings.
   161  // BORROWED FROM 'internal' PACKAGE TESTS.
   162  type stringMapPropagator struct {
   163  	keys map[string]struct{}
   164  }
   165  
   166  // NewStringMapPropagator returns a context propagator that propagates a set of
   167  // string key-value pairs across a workflow
   168  func NewStringMapPropagator(keys []string) workflow.ContextPropagator {
   169  	keyMap := make(map[string]struct{}, len(keys))
   170  	for _, key := range keys {
   171  		keyMap[key] = struct{}{}
   172  	}
   173  	return &stringMapPropagator{keyMap}
   174  }
   175  
   176  // Inject injects values from context into headers for propagation
   177  func (s *stringMapPropagator) Inject(ctx context.Context, writer workflow.HeaderWriter) error {
   178  	for key := range s.keys {
   179  		value, ok := ctx.Value(contextKey(key)).(string)
   180  		if !ok {
   181  			return fmt.Errorf("unable to extract key from context %v", key)
   182  		}
   183  		writer.Set(key, []byte(value))
   184  	}
   185  	return nil
   186  }
   187  
   188  // InjectFromWorkflow injects values from context into headers for propagation
   189  func (s *stringMapPropagator) InjectFromWorkflow(ctx workflow.Context, writer workflow.HeaderWriter) error {
   190  	for key := range s.keys {
   191  		value, ok := ctx.Value(contextKey(key)).(string)
   192  		if !ok {
   193  			return fmt.Errorf("unable to extract key from context %v", key)
   194  		}
   195  		writer.Set(key, []byte(value))
   196  	}
   197  	return nil
   198  }
   199  
   200  // Extract extracts values from headers and puts them into context
   201  func (s *stringMapPropagator) Extract(ctx context.Context, reader workflow.HeaderReader) (context.Context, error) {
   202  	if err := reader.ForEachKey(func(key string, value []byte) error {
   203  		if _, ok := s.keys[key]; ok {
   204  			ctx = context.WithValue(ctx, contextKey(key), string(value))
   205  		}
   206  		return nil
   207  	}); err != nil {
   208  		return nil, err
   209  	}
   210  	return ctx, nil
   211  }
   212  
   213  // ExtractToWorkflow extracts values from headers and puts them into context
   214  func (s *stringMapPropagator) ExtractToWorkflow(ctx workflow.Context, reader workflow.HeaderReader) (workflow.Context, error) {
   215  	if err := reader.ForEachKey(func(key string, value []byte) error {
   216  		if _, ok := s.keys[key]; ok {
   217  			ctx = workflow.WithValue(ctx, contextKey(key), string(value))
   218  		}
   219  		return nil
   220  	}); err != nil {
   221  		return nil, err
   222  	}
   223  	return ctx, nil
   224  }