diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f272192..ec0c03b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: - name: Test env: CGO_ENABLED: 0 - run: go test -v ./... + run: go test ./... test-shellcheck: name: Shellcheck diff --git a/app/mtls_test.go b/app/mtls_test.go deleted file mode 100644 index ae45e3fc..00000000 --- a/app/mtls_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package app_test - -import ( - "bytes" - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "io" - "net/http" - "os" - "path/filepath" - "runtime" - "strconv" - "testing" - "text/template" - "time" - - "github.com/temporalio/cli/app" - sconfig "github.com/temporalio/cli/server/config" - "github.com/urfave/cli/v2" - "go.temporal.io/api/enums/v1" - "go.temporal.io/api/workflowservice/v1" - "go.temporal.io/sdk/client" -) - -func TestMTLSConfig(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - _, thisFile, _, _ := runtime.Caller(0) - mtlsDir := filepath.Join(thisFile, "../testdata/mtls") - - // Create temp config dir - confDir := t.TempDir() - - // Run templated config and put in temp dir - var buf bytes.Buffer - tmpl, err := template.New("temporal.yaml.template"). - Funcs(template.FuncMap{"qualified": func(s string) string { return strconv.Quote(filepath.Join(mtlsDir, s)) }}). - ParseFiles(filepath.Join(mtlsDir, "temporal.yaml.template")) - if err != nil { - t.Fatal(err) - } else if err = tmpl.Execute(&buf, nil); err != nil { - t.Fatal(err) - } else if err = os.WriteFile(filepath.Join(confDir, "temporal.yaml"), buf.Bytes(), 0644); err != nil { - t.Fatal(err) - } - buf.Reset() - tmpl, err = template.New("temporal-ui.yaml.template"). - Funcs(template.FuncMap{"qualified": func(s string) string { return strconv.Quote(filepath.Join(mtlsDir, s)) }}). - ParseFiles(filepath.Join(mtlsDir, "temporal-ui.yaml.template")) - if err != nil { - t.Fatal(err) - } else if err = tmpl.Execute(&buf, nil); err != nil { - t.Fatal(err) - } else if err = os.WriteFile(filepath.Join(confDir, "temporal-ui.yaml"), buf.Bytes(), 0644); err != nil { - t.Fatal(err) - } - - portProvider := sconfig.NewPortProvider() - var ( - frontendPort = portProvider.MustGetFreePort() - webUIPort = portProvider.MustGetFreePort() - ) - portProvider.Close() - - // Run in-memory using temp config - args := []string{ - "temporal", - "server", - "start-dev", - "--config", confDir, - "--namespace", "default", - "--log-format", "noop", - "--port", strconv.Itoa(frontendPort), - "--ui-port", strconv.Itoa(webUIPort), - } - go func() { - temporalCLI := app.BuildApp() - // Don't call os.Exit - temporalCLI.ExitErrHandler = func(_ *cli.Context, _ error) {} - - if err := temporalCLI.RunContext(ctx, args); err != nil { - fmt.Printf("CLI failed: %s\n", err) - } - }() - - // Load client cert/key for auth - clientCert, err := tls.LoadX509KeyPair( - filepath.Join(mtlsDir, "client-cert.pem"), - filepath.Join(mtlsDir, "client-key.pem"), - ) - if err != nil { - t.Fatal(err) - } - // Load server cert for CA check - serverCAPEM, err := os.ReadFile(filepath.Join(mtlsDir, "server-ca-cert.pem")) - if err != nil { - t.Fatal(err) - } - serverCAPool := x509.NewCertPool() - serverCAPool.AppendCertsFromPEM(serverCAPEM) - - // Build client options and try to connect client every 100ms for 5s - options := client.Options{ - HostPort: fmt.Sprintf("localhost:%d", frontendPort), - ConnectionOptions: client.ConnectionOptions{ - TLS: &tls.Config{ - Certificates: []tls.Certificate{clientCert}, - RootCAs: serverCAPool, - }, - }, - } - var c client.Client - for i := 0; i < 50; i++ { - if c, err = client.Dial(options); err == nil { - break - } - time.Sleep(100 * time.Millisecond) - } - if err != nil { - t.Fatal(err) - } - - // Make a call - resp, err := c.WorkflowService().DescribeNamespace(ctx, &workflowservice.DescribeNamespaceRequest{ - Namespace: "default", - }) - if err != nil { - t.Fatal(err) - } else if resp.NamespaceInfo.State != enums.NAMESPACE_STATE_REGISTERED { - t.Fatalf("Bad state: %v", resp.NamespaceInfo.State) - } - - // Pretend to be a browser to invoke the UI API - res, err := http.Get(fmt.Sprintf("http://localhost:%d/api/v1/namespaces?", webUIPort)) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - body, err := io.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - if res.StatusCode != http.StatusOK { - t.Fatalf("Unexpected response %s, with body %s", res.Status, string(body)) - } -} diff --git a/common/flags.go b/common/flags.go index 19ad8b8a..302fe36f 100644 --- a/common/flags.go +++ b/common/flags.go @@ -32,7 +32,6 @@ var ( FlagCodecAuth = "codec-auth" FlagCodecEndpoint = "codec-endpoint" FlagConcurrency = "concurrency" - FlagConfig = "config" FlagContextTimeout = "context-timeout" FlagCronSchedule = "cron" FlagDBPath = "db-filename" diff --git a/server/commands.go b/server/commands.go index 2d3d25ee..714e6a3b 100644 --- a/server/commands.go +++ b/server/commands.go @@ -129,13 +129,6 @@ func NewServerCommands(defaultCfg *sconfig.Config) []*cli.Command { EnvVars: nil, Value: nil, }, - &cli.StringFlag{ - Name: common.FlagConfig, - Aliases: []string{"c"}, - Usage: `Path to config directory.`, - EnvVars: []string{config.EnvKeyConfigDir}, - Value: "", - }, &cli.StringSliceFlag{ Name: common.FlagDynamicConfigValue, Usage: `Dynamic config value, as KEY=JSON_VALUE (string values need quotes).`, @@ -170,13 +163,6 @@ func NewServerCommands(defaultCfg *sconfig.Config) []*cli.Command { return cli.Exit(fmt.Sprintf("bad value %q passed for flag %q", c.String(common.FlagIP), common.FlagIP), 1) } - if c.IsSet(common.FlagConfig) { - cfgPath := c.String(common.FlagConfig) - if _, err := os.Stat(cfgPath); os.IsNotExist(err) { - return cli.Exit(fmt.Sprintf("bad value %q passed for flag %q: file not found", c.String(common.FlagConfig), common.FlagConfig), 1) - } - } - return nil }, Action: func(c *cli.Context) error { @@ -210,20 +196,6 @@ func NewServerCommands(defaultCfg *sconfig.Config) []*cli.Command { if err != nil { return err } - - baseConfig := &config.Config{} - if c.IsSet(common.FlagConfig) { - // Temporal server requires a couple of persistence config values to - // be explicitly set or the config loading fails. While these are the - // same values used internally, they are overridden later anyways, - // they are just here to pass validation. - baseConfig.Persistence.DefaultStore = sconfig.PersistenceStoreName - baseConfig.Persistence.NumHistoryShards = 1 - if err := config.Load("temporal", c.String(common.FlagConfig), "", &baseConfig); err != nil { - return err - } - } - interruptChan := make(chan interface{}, 1) go func() { if doneChan := c.Done(); doneChan != nil { @@ -245,7 +217,7 @@ func NewServerCommands(defaultCfg *sconfig.Config) []*cli.Command { WithUpstreamOptions( temporal.InterruptOn(interruptChan), ), - WithBaseConfig(baseConfig), + WithBaseConfig(&config.Config{}), } if c.IsSet(common.FlagDBPath) { @@ -267,11 +239,11 @@ func NewServerCommands(defaultCfg *sconfig.Config) []*cli.Command { }, } - opt, err := newUIOption(uiBaseCfg, c.String(common.FlagConfig)) - + opt, err := newUIOption(uiBaseCfg) if err != nil { return err } + if opt != nil { opts = append(opts, opt) } diff --git a/server/ui.go b/server/ui.go index 908e653d..863b0673 100644 --- a/server/ui.go +++ b/server/ui.go @@ -29,33 +29,11 @@ package server // This file should be the only one to import ui-server packages. // This is to avoid embedding the UI's static assets in the binary when the `headless` build tag is enabled. import ( - "strings" - - provider "github.com/temporalio/ui-server/v2/plugins/fs_config_provider" uiserver "github.com/temporalio/ui-server/v2/server" uiconfig "github.com/temporalio/ui-server/v2/server/config" uiserveroptions "github.com/temporalio/ui-server/v2/server/server_options" ) -func newUIOption(c *uiconfig.Config, configDir string) (ServerOption, error) { - cfg, err := MergeWithConfigFile( - c, - configDir, - ) - if err != nil { - return nil, err - } - return WithUI(uiserver.NewServer(uiserveroptions.WithConfigProvider(cfg))), nil -} - -func MergeWithConfigFile(cfg *uiconfig.Config, configDir string) (*uiconfig.Config, error) { - if configDir != "" { - if err := provider.Load(configDir, cfg, "temporal-ui"); err != nil { - if !strings.HasPrefix(err.Error(), "no config files found") { - return nil, err - } - } - } - - return cfg, nil +func newUIOption(c *uiconfig.Config) (ServerOption, error) { + return WithUI(uiserver.NewServer(uiserveroptions.WithConfigProvider(c))), nil } diff --git a/server/ui_test.go b/server/ui_test.go index 854372ae..1b05f2e2 100644 --- a/server/ui_test.go +++ b/server/ui_test.go @@ -33,55 +33,13 @@ import ( ) func TestNewUIConfig(t *testing.T) { - c := &uiconfig.Config{ + cfg := &uiconfig.Config{ Host: "localhost", Port: 8233, TemporalGRPCAddress: "localhost:7233", EnableUI: true, } - cfg, err := MergeWithConfigFile(c, "") - if err != nil { - t.Errorf("cannot create config: %s", err) - return - } - if err = cfg.Validate(); err != nil { - t.Errorf("config not valid: %s", err) - } -} - -func TestNewUIConfigWithMissingConfigFile(t *testing.T) { - c := &uiconfig.Config{ - Host: "localhost", - Port: 8233, - TemporalGRPCAddress: "localhost:7233", - EnableUI: true, - } - cfg, err := MergeWithConfigFile(c, "wibble") - if err != nil { - t.Errorf("cannot create config: %s", err) - return - } - if err = cfg.Validate(); err != nil { + if err := cfg.Validate(); err != nil { t.Errorf("config not valid: %s", err) } } - -func TestNewUIConfigWithPresentConfigFile(t *testing.T) { - c := &uiconfig.Config{ - Host: "localhost", - Port: 8233, - TemporalGRPCAddress: "localhost:7233", - EnableUI: true, - } - cfg, err := MergeWithConfigFile(c, "testdata") - if err != nil { - t.Errorf("cannot create config: %s", err) - return - } - if err = cfg.Validate(); err != nil { - t.Errorf("config not valid: %s", err) - } - if cfg.TLS.ServerName != "local.dev" { - t.Errorf("did not load expected config file") - } -}