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 }