mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What Changed This PR introduces a new `--config-file` flag (and the `TEMPORAL_SERVER_CONFIG_FILE_PATH ` environment variable) to remove the dependency on `dockerize` in the Temporal server Docker image. When a configuration file is specified using either the CLI flag or the environment variable, the server will load configuration **only** from that file. Users who want templating behavior similar to `dockerize` can enable it by adding the comment `# enable-template` at the top of the configuration file. --- ### Key Changes 1. **New `--config-file` flag:** * Adds a global `--config-file` flag that accepts a path to a single configuration file (absolute or relative to the project root). * Can also be set via the `TEMPORAL_SERVER_CONFIG_FILE_PATH ` environment variable. 2. **Deprecated legacy flags:** * The `--config`, `--env`, and `--zone` flags are now marked as **deprecated** in CLI help text. * These flags still work for backward compatibility. 3. **Embedded config template:** * The `config_template.yaml` file is now embedded in the binary to support loading configuration from environment variables. * Templating is supported if the file includes the `# enable-template` comment at the top. 4. **Templating support:** * Configuration files can use templating by including `# enable-template` at the beginning of the YAML file. --- ### Configuration Loading Priority (Highest to Lowest) 1. **`--config-file` specified** → Load that specific file 2. **`--config`, `--env`, or `--zone` specified** → Load from configuration directory (**deprecated**) 3. **No configuration specified** → Load from embedded template using environment variables (default) --- ### Expected Behavior The following examples illustrate how the new configuration loading logic behaves: * **Default behavior:** Running `temporal start` without flags loads configuration from environment variables only using the embedded template. * **Using `--config-file`:** `temporal --config-file=/path/to/config.yaml start` loads configuration from the specified file path. * **Using `TEMPORAL_SERVER_CONFIG_FILE_PATH`:** Setting `TEMPORAL_SERVER_CONFIG_FILE_PATH=/path/to/config.yaml temporal start` has the same effect as using the flag. * **Validation and error handling:** The CLI returns clear error messages when conflicting flags or environment variables are used, or when a specified file does not exist. --- ## Breaking Change The default behavior of `temporal start` has changed. It now loads configuration **from environment variables** instead of using a default template path. --------- Co-authored-by: Alex Stanfield <[email protected]> Co-authored-by: michaely520 <[email protected]> Co-authored-by: Yichao Yang <[email protected]> Co-authored-by: David Reiss <[email protected]>
191 lines
6.7 KiB
Go
191 lines
6.7 KiB
Go
package temporal
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"go.temporal.io/server/client"
|
|
"go.temporal.io/server/common/authorization"
|
|
"go.temporal.io/server/common/config"
|
|
"go.temporal.io/server/common/dynamicconfig"
|
|
"go.temporal.io/server/common/log"
|
|
"go.temporal.io/server/common/membership/static"
|
|
"go.temporal.io/server/common/metrics"
|
|
persistenceclient "go.temporal.io/server/common/persistence/client"
|
|
"go.temporal.io/server/common/persistence/visibility"
|
|
"go.temporal.io/server/common/primitives"
|
|
"go.temporal.io/server/common/resolver"
|
|
"go.temporal.io/server/common/rpc/encryption"
|
|
"go.temporal.io/server/common/searchattribute"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
type (
|
|
ServerOption interface {
|
|
apply(*serverOptions)
|
|
}
|
|
|
|
applyFunc func(*serverOptions)
|
|
)
|
|
|
|
func (f applyFunc) apply(s *serverOptions) { f(s) }
|
|
|
|
// WithConfig sets a custom configuration
|
|
func WithConfig(cfg *config.Config) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.config = cfg
|
|
})
|
|
}
|
|
|
|
// WithConfigLoader sets a custom configuration load
|
|
func WithConfigLoader(configDir string, env string, zone string) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.configDir, s.env, s.zone = configDir, env, zone
|
|
})
|
|
}
|
|
|
|
// WithServerConfigFilePath sets a custom configuration load
|
|
func WithServerConfigFilePath(filePath string) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.configFilePath = filePath
|
|
})
|
|
}
|
|
|
|
// ForServices indicates which supplied services (e.g. frontend, history, matching, worker) within the server to start
|
|
func ForServices(names []string) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.serviceNames = make(map[primitives.ServiceName]struct{})
|
|
for _, name := range names {
|
|
s.serviceNames[primitives.ServiceName(name)] = struct{}{}
|
|
}
|
|
})
|
|
}
|
|
|
|
// WithStaticHosts disables dynamic service membership and resolves service hosts statically.
|
|
// At least one host must be provided for all required services (frontend, history, matching, worker).
|
|
// And a self-address must be provided for all services passed to ForServices.
|
|
func WithStaticHosts(hostsByService map[primitives.ServiceName]static.Hosts) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.hostsByService = hostsByService
|
|
})
|
|
}
|
|
|
|
// InterruptOn interrupts server on the signal from server. If channel is nil Start() will block forever.
|
|
func InterruptOn(interruptCh <-chan interface{}) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.startupSynchronizationMode.blockingStart = true
|
|
s.startupSynchronizationMode.interruptCh = interruptCh
|
|
})
|
|
}
|
|
|
|
// WithLogger sets a custom logger
|
|
func WithLogger(logger log.Logger) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.logger = logger
|
|
})
|
|
}
|
|
|
|
// WithNamespaceLogger sets an optional logger for all frontend operations
|
|
func WithNamespaceLogger(namespaceLogger log.Logger) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.namespaceLogger = namespaceLogger
|
|
})
|
|
}
|
|
|
|
// WithAuthorizer sets a low level authorizer to allow/deny all API calls
|
|
func WithAuthorizer(authorizer authorization.Authorizer) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.authorizer = authorizer
|
|
})
|
|
}
|
|
|
|
// WithTLSConfigFactory overrides default provider of TLS configuration
|
|
func WithTLSConfigFactory(tlsConfigProvider encryption.TLSConfigProvider) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.tlsConfigProvider = tlsConfigProvider
|
|
})
|
|
}
|
|
|
|
// WithClaimMapper configures a role mapper for authorization
|
|
func WithClaimMapper(claimMapper func(cfg *config.Config) authorization.ClaimMapper) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.claimMapper = claimMapper(s.config)
|
|
})
|
|
}
|
|
|
|
// WithAudienceGetter configures JWT audience getter for authorization
|
|
func WithAudienceGetter(audienceGetter func(cfg *config.Config) authorization.JWTAudienceMapper) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.audienceGetter = audienceGetter(s.config)
|
|
})
|
|
}
|
|
|
|
// WithPersistenceServiceResolver sets a custom persistence service resolver which will convert service name or address value from config to another address
|
|
func WithPersistenceServiceResolver(r resolver.ServiceResolver) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.persistenceServiceResolver = r
|
|
})
|
|
}
|
|
|
|
// WithElasticsearchHttpClient sets a custom HTTP client which is used to make requests to Elasticsearch
|
|
func WithElasticsearchHttpClient(c *http.Client) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.elasticsearchHttpClient = c
|
|
})
|
|
}
|
|
|
|
// WithDynamicConfigClient sets custom client for reading dynamic configuration.
|
|
func WithDynamicConfigClient(c dynamicconfig.Client) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.dynamicConfigClient = c
|
|
})
|
|
}
|
|
|
|
// WithCustomDataStoreFactory sets a custom AbstractDataStoreFactory
|
|
// NOTE: this option is experimental and may be changed or removed in future release.
|
|
func WithCustomDataStoreFactory(customFactory persistenceclient.AbstractDataStoreFactory) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.customDataStoreFactory = customFactory
|
|
})
|
|
}
|
|
|
|
func WithCustomVisibilityStoreFactory(customFactory visibility.VisibilityStoreFactory) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.customVisibilityStoreFactory = customFactory
|
|
})
|
|
}
|
|
|
|
// WithClientFactoryProvider sets a custom ClientFactoryProvider
|
|
// NOTE: this option is experimental and may be changed or removed in future release.
|
|
func WithClientFactoryProvider(clientFactoryProvider client.FactoryProvider) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.clientFactoryProvider = clientFactoryProvider
|
|
})
|
|
}
|
|
|
|
// WithSearchAttributesMapper sets a custom search attributes mapper which converts search attributes aliases to field names and vice versa.
|
|
func WithSearchAttributesMapper(m searchattribute.Mapper) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.searchAttributesMapper = m
|
|
})
|
|
}
|
|
|
|
// WithChainedFrontendGrpcInterceptors sets a chain of ordered custom grpc interceptors that will be invoked for all
|
|
// Frontend gRPC API calls. The list of custom interceptors will be appended to the end of the internal
|
|
// ServerInterceptors. The custom interceptors will be invoked in the order as they appear in the supplied list, after
|
|
// the internal ServerInterceptors.
|
|
func WithChainedFrontendGrpcInterceptors(
|
|
interceptors ...grpc.UnaryServerInterceptor,
|
|
) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.customFrontendInterceptors = interceptors
|
|
})
|
|
}
|
|
|
|
// WithCustomerMetricsProvider sets a custom implementation of the metrics.MetricsHandler interface
|
|
// metrics.MetricsHandler is the base interface for publishing metric events
|
|
func WithCustomMetricsHandler(provider metrics.Handler) ServerOption {
|
|
return applyFunc(func(s *serverOptions) {
|
|
s.metricHandler = provider
|
|
})
|
|
}
|