Skip to content

Commit

Permalink
Dev server compat issues in CLI rewrite (#535)
Browse files Browse the repository at this point in the history
  • Loading branch information
cretz committed Apr 23, 2024
1 parent 4528734 commit 47d4011
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 30 deletions.
14 changes: 5 additions & 9 deletions temporalcli/commands.gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type TemporalCommand struct {
Env string
EnvFile string
LogLevel StringEnum
LogFormat StringEnum
LogFormat string
Output StringEnum
TimeFormat StringEnum
Color StringEnum
Expand All @@ -46,11 +46,10 @@ func NewTemporalCommand(cctx *CommandContext) *TemporalCommand {
cctx.BindFlagEnvVar(s.Command.PersistentFlags().Lookup("env"), "TEMPORAL_ENV")
s.Command.PersistentFlags().StringVar(&s.EnvFile, "env-file", "", "File to read all environments (defaults to `$HOME/.config/temporalio/temporal.yaml`).")
s.LogLevel = NewStringEnum([]string{"debug", "info", "warn", "error", "never"}, "info")
s.Command.PersistentFlags().Var(&s.LogLevel, "log-level", "Log level. Accepted values: debug, info, warn, error, never.")
s.LogFormat = NewStringEnum([]string{"text", "json"}, "text")
s.Command.PersistentFlags().Var(&s.LogFormat, "log-format", "Log format. Accepted values: text, json.")
s.Output = NewStringEnum([]string{"text", "json", "jsonl"}, "text")
s.Command.PersistentFlags().VarP(&s.Output, "output", "o", "Data output format. Accepted values: text, json, jsonl.")
s.Command.PersistentFlags().Var(&s.LogLevel, "log-level", "Log level. Default is \"info\" for most commands and \"warn\" for `server start-dev`. Accepted values: debug, info, warn, error, never.")
s.Command.PersistentFlags().StringVar(&s.LogFormat, "log-format", "", "Log format. Options are \"text\" and \"json\". Default is \"text\".")
s.Output = NewStringEnum([]string{"text", "json", "jsonl", "none"}, "text")
s.Command.PersistentFlags().VarP(&s.Output, "output", "o", "Data output format. Note, this does not affect logging. Accepted values: text, json, jsonl, none.")
s.TimeFormat = NewStringEnum([]string{"relative", "iso", "raw"}, "relative")
s.Command.PersistentFlags().Var(&s.TimeFormat, "time-format", "Time format. Accepted values: relative, iso, raw.")
s.Color = NewStringEnum([]string{"always", "never", "auto"}, "auto")
Expand Down Expand Up @@ -1243,7 +1242,6 @@ type TemporalServerStartDevCommand struct {
SqlitePragma []string
DynamicConfigValue []string
LogConfig bool
LogLevelServer StringEnum
}

func NewTemporalServerStartDevCommand(cctx *CommandContext, parent *TemporalServerCommand) *TemporalServerStartDevCommand {
Expand Down Expand Up @@ -1272,8 +1270,6 @@ func NewTemporalServerStartDevCommand(cctx *CommandContext, parent *TemporalServ
s.Command.Flags().StringArrayVar(&s.SqlitePragma, "sqlite-pragma", nil, "Specify SQLite pragma statements in pragma=value format.")
s.Command.Flags().StringArrayVar(&s.DynamicConfigValue, "dynamic-config-value", nil, "Dynamic config value, as KEY=JSON_VALUE (string values need quotes).")
s.Command.Flags().BoolVar(&s.LogConfig, "log-config", false, "Log the server config being used to stderr.")
s.LogLevelServer = NewStringEnum([]string{"debug", "info", "warn", "error", "never"}, "warn")
s.Command.Flags().Var(&s.LogLevelServer, "log-level-server", "Log level for the server only. Accepted values: debug, info, warn, error, never.")
s.Command.Run = func(c *cobra.Command, args []string) {
if err := s.run(cctx, args); err != nil {
cctx.Options.Fail(err)
Expand Down
18 changes: 14 additions & 4 deletions temporalcli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,9 @@ func (c *TemporalCommand) preRun(cctx *CommandContext) error {
return fmt.Errorf("invalid log level %q: %w", c.LogLevel.Value, err)
}
var handler slog.Handler
switch c.LogFormat.Value {
case "text":
switch c.LogFormat {
// We have a "pretty" alias for compatibility
case "", "text", "pretty":
handler = slog.NewTextHandler(cctx.Options.Stderr, &slog.HandlerOptions{
Level: level,
// Remove the TZ
Expand All @@ -405,7 +406,7 @@ func (c *TemporalCommand) preRun(cctx *CommandContext) error {
case "json":
handler = slog.NewJSONHandler(cctx.Options.Stderr, &slog.HandlerOptions{Level: level})
default:
return fmt.Errorf("invalid log format %q", c.LogFormat.Value)
return fmt.Errorf("invalid log format %q", c.LogFormat)
}
cctx.Logger = slog.New(handler)
}
Expand All @@ -419,8 +420,13 @@ func (c *TemporalCommand) preRun(cctx *CommandContext) error {
jsonIndent = " "
}
if cctx.Printer == nil {
printerOutput := cctx.Options.Stdout
// Disable printer by making writer noop if "none" chosen
if c.Output.Value == "none" {
printerOutput = nopWriter{}
}
cctx.Printer = &printer.Printer{
Output: cctx.Options.Stdout,
Output: printerOutput,
JSON: cctx.JSONOutput,
JSONIndent: jsonIndent,
JSONPayloadShorthand: !c.NoJsonShorthandPayloads,
Expand Down Expand Up @@ -492,3 +498,7 @@ func timestampToTime(t *timestamppb.Timestamp) time.Time {
}
return t.AsTime()
}

type nopWriter struct{}

func (nopWriter) Write(b []byte) (int, error) { return len(b), nil }
4 changes: 2 additions & 2 deletions temporalcli/commands.schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,9 @@ func (c *TemporalScheduleCreateCommand) run(cctx *CommandContext, args []string)
return err
} else if opts.Overlap, err = enumspb.ScheduleOverlapPolicyFromString(c.OverlapPolicy.Value); err != nil {
return err
} else if opts.Memo, err = stringKeysJSONValues(c.ScheduleMemo); err != nil {
} else if opts.Memo, err = stringKeysJSONValues(c.ScheduleMemo, false); err != nil {
return fmt.Errorf("invalid memo values: %w", err)
} else if opts.SearchAttributes, err = stringKeysJSONValues(c.ScheduleSearchAttribute); err != nil {
} else if opts.SearchAttributes, err = stringKeysJSONValues(c.ScheduleSearchAttribute, false); err != nil {
return fmt.Errorf("invalid search attribute values: %w", err)
}

Expand Down
33 changes: 29 additions & 4 deletions temporalcli/commands.server.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package temporalcli

import (
"encoding/json"
"fmt"

"github.com/google/uuid"
Expand All @@ -26,10 +27,18 @@ func (t *TemporalServerStartDevCommand) run(cctx *CommandContext, args []string)
CurrentClusterName: "active",
InitialFailoverVersion: 1,
}
if t.LogLevelServer.Value == "never" {
// Set the log level value of the server to the overall log level given to the
// CLI. But if it is "never" we have to do a special value, and if it was
// never changed, we have to use the default of "warn" instead of the CLI
// default of "info" since server is noisier.
logLevel := t.Parent.Parent.LogLevel.Value
if !t.Parent.Parent.LogLevel.ChangedFromDefault {
logLevel = "warn"
}
if logLevel == "never" {
opts.LogLevel = 100
} else if err := opts.LogLevel.UnmarshalText([]byte(t.LogLevelServer.Value)); err != nil {
return fmt.Errorf("invalid log level %q: %w", t.LogLevelServer.Value, err)
} else if err := opts.LogLevel.UnmarshalText([]byte(logLevel)); err != nil {
return fmt.Errorf("invalid log level %q: %w", logLevel, err)
}
// Setup UI
if !t.Headless {
Expand All @@ -46,9 +55,25 @@ func (t *TemporalServerStartDevCommand) run(cctx *CommandContext, args []string)
var err error
if opts.SqlitePragmas, err = stringKeysValues(t.SqlitePragma); err != nil {
return fmt.Errorf("invalid pragma: %w", err)
} else if opts.DynamicConfigValues, err = stringKeysJSONValues(t.DynamicConfigValue); err != nil {
} else if opts.DynamicConfigValues, err = stringKeysJSONValues(t.DynamicConfigValue, true); err != nil {
return fmt.Errorf("invalid dynamic config values: %w", err)
}
// We have to convert all dynamic config values that JSON number to int if we
// can because server dynamic config expecting int won't work with the default
// float JSON unmarshal uses
for k, v := range opts.DynamicConfigValues {
if num, ok := v.(json.Number); ok {
if newV, err := num.Int64(); err == nil {
// Dynamic config only accepts int type, not int32 nor int64
opts.DynamicConfigValues[k] = int(newV)
} else if newV, err := num.Float64(); err == nil {
opts.DynamicConfigValues[k] = newV
} else {
return fmt.Errorf("invalid JSON value for key %q", k)
}
}
}

// If not using DB file, set persistent cluster ID
if t.DbFilename == "" {
opts.ClusterID = persistentClusterID()
Expand Down
4 changes: 2 additions & 2 deletions temporalcli/commands.workflow_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,13 +265,13 @@ func buildStartOptions(sw *SharedWorkflowStartOptions, w *WorkflowStartOptions)
}
if len(sw.Memo) > 0 {
var err error
if o.Memo, err = stringKeysJSONValues(sw.Memo); err != nil {
if o.Memo, err = stringKeysJSONValues(sw.Memo, false); err != nil {
return o, fmt.Errorf("invalid memo values: %w", err)
}
}
if len(sw.SearchAttribute) > 0 {
var err error
if o.SearchAttributes, err = stringKeysJSONValues(sw.SearchAttribute); err != nil {
if o.SearchAttributes, err = stringKeysJSONValues(sw.SearchAttribute, false); err != nil {
return o, fmt.Errorf("invalid search attribute values: %w", err)
}
}
Expand Down
10 changes: 5 additions & 5 deletions temporalcli/commandsmd/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ This document has a specific structure used by a parser. Here are the rules:

* `--env` (string) - Environment to read environment-specific flags from. Default: default. Env: TEMPORAL_ENV.
* `--env-file` (string) - File to read all environments (defaults to `$HOME/.config/temporalio/temporal.yaml`).
* `--log-level` (string-enum) - Log level. Options: debug, info, warn, error, never. Default: info.
* `--log-format` (string-enum) - Log format. Options: text, json. Default: text.
* `--output`, `-o` (string-enum) - Data output format. Options: text, json, jsonl. Default: text.
* `--log-level` (string-enum) - Log level. Default is "info" for most commands and "warn" for `server start-dev`.
Options: debug, info, warn, error, never. Default: info.
* `--log-format` (string) - Log format. Options are "text" and "json". Default is "text".
* `--output`, `-o` (string-enum) - Data output format. Note, this does not affect logging. Options: text, json, jsonl,
none. Default: text.
* `--time-format` (string-enum) - Time format. Options: relative, iso, raw. Default: relative.
* `--color` (string-enum) - Set coloring. Options: always, never, auto. Default: auto.
* `--no-json-shorthand-payloads` (bool) - Always show all payloads as raw payloads even if they are JSON.
Expand Down Expand Up @@ -594,8 +596,6 @@ To persist Workflows across runs, use:
* `--sqlite-pragma` (string[]) - Specify SQLite pragma statements in pragma=value format.
* `--dynamic-config-value` (string[]) - Dynamic config value, as KEY=JSON_VALUE (string values need quotes).
* `--log-config` (bool) - Log the server config being used to stderr.
* `--log-level-server` (string-enum) - Log level for the server only. Options: debug, info, warn, error, never. Default:
warn.

### temporal task-queue: Manage Task Queues.

Expand Down
17 changes: 13 additions & 4 deletions temporalcli/strings.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package temporalcli

import (
"bytes"
"encoding/json"
"fmt"
"sort"
"strings"
)

type StringEnum struct {
Allowed []string
Value string
Allowed []string
Value string
ChangedFromDefault bool
}

func NewStringEnum(allowed []string, value string) StringEnum {
Expand All @@ -22,6 +24,7 @@ func (s *StringEnum) Set(p string) error {
for _, allowed := range s.Allowed {
if p == allowed {
s.Value = p
s.ChangedFromDefault = true
return nil
}
}
Expand Down Expand Up @@ -60,7 +63,7 @@ func stringKeysValues(s []string) (map[string]string, error) {
return ret, nil
}

func stringKeysJSONValues(s []string) (map[string]any, error) {
func stringKeysJSONValues(s []string, useJSONNumber bool) (map[string]any, error) {
if len(s) == 0 {
return nil, nil
}
Expand All @@ -70,9 +73,15 @@ func stringKeysJSONValues(s []string) (map[string]any, error) {
if len(pieces) != 2 {
return nil, fmt.Errorf("missing expected '=' in %q", item)
}
dec := json.NewDecoder(bytes.NewReader([]byte(pieces[1])))
if useJSONNumber {
dec.UseNumber()
}
var v any
if err := json.Unmarshal([]byte(pieces[1]), &v); err != nil {
if err := dec.Decode(&v); err != nil {
return nil, fmt.Errorf("invalid JSON value for key %q: %w", pieces[0], err)
} else if dec.InputOffset() != int64(len(pieces[1])) {
return nil, fmt.Errorf("invalid JSON value for key %q: unexpected trailing data", pieces[0])
}
ret[pieces[0]] = v
}
Expand Down

0 comments on commit 47d4011

Please sign in to comment.