forked from temporalio/sdk-go
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
873 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
package envconfig | ||
|
||
import ( | ||
"context" | ||
"crypto/tls" | ||
"crypto/x509" | ||
"fmt" | ||
"net/http" | ||
"os" | ||
|
||
"go.temporal.io/sdk/client" | ||
"go.temporal.io/sdk/converter" | ||
) | ||
|
||
type ClientConfig struct { | ||
Profiles map[string]*ClientConfigProfile | ||
} | ||
|
||
type ClientConfigProfile struct { | ||
Address string | ||
Namespace string | ||
APIKey string | ||
TLS *ClientConfigTLS | ||
Codec *ClientConfigCodec | ||
GRPCMeta map[string]string | ||
} | ||
|
||
type ClientConfigTLS struct { | ||
Disabled bool | ||
ClientCertPath string | ||
ClientCertData []byte | ||
ClientKeyPath string | ||
ClientKeyData []byte | ||
ServerCACertPath string | ||
ServerCACertData []byte | ||
ServerName string | ||
DisableHostVerification bool | ||
} | ||
|
||
type ClientConfigCodec struct { | ||
Endpoint string | ||
Auth string | ||
} | ||
|
||
type ToClientOptionsOptions struct { | ||
// If true and a codec is configured, the data converter of the client will point to the codec remotely. Users | ||
// should usually not set this and rather configure the codec locally. Users should especially not enable this for | ||
// clients used by workers since they call the codec repeatedly even during workflow replay. | ||
IncludeRemoteCodec bool | ||
} | ||
|
||
func (c *ClientConfig) ToClientOptions(profile string, options ToClientOptionsOptions) (client.Options, error) { | ||
if profile == "" { | ||
profile = "default" | ||
} | ||
prof, ok := c.Profiles[profile] | ||
if !ok { | ||
return client.Options{}, fmt.Errorf("profile not found") | ||
} | ||
return prof.ToClientOptions(options) | ||
} | ||
|
||
func (c *ClientConfigProfile) ToClientOptions(options ToClientOptionsOptions) (client.Options, error) { | ||
opts := client.Options{ | ||
HostPort: c.Address, | ||
Namespace: c.Namespace, | ||
} | ||
if c.APIKey != "" { | ||
opts.Credentials = client.NewAPIKeyStaticCredentials(c.APIKey) | ||
} | ||
if c.TLS != nil { | ||
var err error | ||
if opts.ConnectionOptions.TLS, err = c.TLS.toTLSConfig(); err != nil { | ||
return client.Options{}, fmt.Errorf("invalid TLS config: %w", err) | ||
} | ||
} else if c.APIKey != "" && (c.TLS == nil || !c.TLS.Disabled) { | ||
opts.ConnectionOptions.TLS = &tls.Config{} | ||
} | ||
if c.Codec != nil && options.IncludeRemoteCodec { | ||
var err error | ||
if opts.DataConverter, err = c.Codec.toDataConverter(c.Namespace); err != nil { | ||
return client.Options{}, fmt.Errorf("invalid codec: %w", err) | ||
} | ||
} | ||
if len(c.GRPCMeta) > 0 { | ||
opts.HeadersProvider = fixedHeaders(c.GRPCMeta) | ||
} | ||
return opts, nil | ||
} | ||
|
||
func (c *ClientConfigTLS) toTLSConfig() (*tls.Config, error) { | ||
if c.Disabled { | ||
return nil, nil | ||
} | ||
conf := &tls.Config{} | ||
|
||
// Client cert | ||
if len(c.ClientCertData) > 0 || len(c.ClientKeyData) > 0 { | ||
if len(c.ClientCertData) == 0 || len(c.ClientKeyData) == 0 { | ||
return nil, fmt.Errorf("if either client cert or key data is present, other must be present too") | ||
} else if c.ClientCertPath != "" || c.ClientKeyPath != "" { | ||
return nil, fmt.Errorf("cannot have client key/cert path with data") | ||
} | ||
cert, err := tls.X509KeyPair(c.ClientCertData, c.ClientKeyData) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed loading client cert/key data: %w", err) | ||
} | ||
conf.Certificates = append(conf.Certificates, cert) | ||
} else if c.ClientCertPath != "" || c.ClientKeyPath != "" { | ||
if c.ClientCertPath == "" || c.ClientKeyPath == "" { | ||
return nil, fmt.Errorf("if either client cert or key path is present, other must be present too") | ||
} | ||
cert, err := tls.LoadX509KeyPair(c.ClientCertPath, c.ClientKeyPath) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed loading client cert/key path: %w", err) | ||
} | ||
conf.Certificates = append(conf.Certificates, cert) | ||
} | ||
|
||
// Server CA cert | ||
if len(c.ServerCACertData) > 0 || c.ServerCACertPath == "" { | ||
pool := x509.NewCertPool() | ||
serverCAData := c.ServerCACertData | ||
if len(serverCAData) == 0 { | ||
var err error | ||
if serverCAData, err = os.ReadFile(c.ServerCACertPath); err != nil { | ||
return nil, fmt.Errorf("failed reading server CA cert path: %w", err) | ||
} | ||
} else if c.ServerCACertPath == "" { | ||
return nil, fmt.Errorf("cannot have server CA cert path with data") | ||
} | ||
if !pool.AppendCertsFromPEM(serverCAData) { | ||
return nil, fmt.Errorf("failed adding server CA to CA pool") | ||
} | ||
conf.RootCAs = pool | ||
} | ||
|
||
conf.ServerName = c.ServerName | ||
conf.InsecureSkipVerify = c.DisableHostVerification | ||
return conf, nil | ||
} | ||
|
||
func (c *ClientConfigCodec) toDataConverter(namespace string) (converter.DataConverter, error) { | ||
return converter.NewRemoteDataConverter(converter.GetDefaultDataConverter(), converter.RemoteDataConverterOptions{ | ||
Endpoint: c.Endpoint, | ||
ModifyRequest: func(req *http.Request) error { | ||
req.Header.Set("X-Namespace", namespace) | ||
if c.Auth != "" { | ||
req.Header.Set("Authorization", c.Auth) | ||
} | ||
return nil | ||
}, | ||
}), nil | ||
} | ||
|
||
type fixedHeaders map[string]string | ||
|
||
func (f fixedHeaders) GetHeaders(context.Context) (map[string]string, error) { return f, nil } |
Oops, something went wrong.
a10d1ec
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested this just now. Here are the setup steps:
go.mod
file in the money transfer sample to reference the Go SDK module from step 1 (i.e., addedreplace go.temporal.io/sdk
andreplace go.temporal.io/sdk/contrib/envconfig
)starter/main.go
andworker/main.go
files to create a Temporal Client by callingclient.Dial(envconfig.MustLoadDefaultClientOptions())
.For my initial test, I launched a local Temporal Service using
temporal server start-dev
. I opened two new terminal tabs and verified that no environment variables starting withTEMPORAL_
were set in either shell. I then launcher the Worker and Starter. Both ran as expected.For my second test, I set four environment variables, using values that correspond to my Temporal Cloud account (which is configured to support mTLS authentication):
TEMPORAL_ADDRESS
TEMPORAL_NAMESPACE
TEMPORAL_TLS_CLIENT_CERT_PATH
TEMPORAL_TLS_CLIENT_KEY_PATH
I then attempted to run the Worker as before, but it failed:
I gather that this is because the
TEMPORAL_TLS_SERVER_CA_CERT_PATH
variable was not set, although I wouldn't think that would be necessary. For reference, the OMS has environment-based config built into the application and it works with the same values I used (although the variable names differ slightly between these two implementations).