diff --git a/cli/deployment/flags.go b/cli/deployment/flags.go index 871a1f43eb..3a03bea762 100644 --- a/cli/deployment/flags.go +++ b/cli/deployment/flags.go @@ -2,6 +2,7 @@ package deployment import ( "flag" + "fmt" "os" "path/filepath" "reflect" @@ -11,6 +12,7 @@ import ( "github.com/spf13/pflag" "github.com/coder/coder/cli/cliflag" + "github.com/coder/coder/cli/cliui" "github.com/coder/coder/codersdk" ) @@ -18,21 +20,21 @@ const ( secretValue = "********" ) -func Flags() codersdk.DeploymentFlags { - return codersdk.DeploymentFlags{ - AccessURL: codersdk.StringFlag{ +func Flags() *codersdk.DeploymentFlags { + return &codersdk.DeploymentFlags{ + AccessURL: &codersdk.StringFlag{ Name: "Access URL", Flag: "access-url", EnvVar: "CODER_ACCESS_URL", Description: "External URL to access your deployment. This must be accessible by all provisioned workspaces.", }, - WildcardAccessURL: codersdk.StringFlag{ + WildcardAccessURL: &codersdk.StringFlag{ Name: "Wildcard Address URL", Flag: "wildcard-access-url", EnvVar: "CODER_WILDCARD_ACCESS_URL", Description: `Specifies the wildcard hostname to use for workspace applications in the form "*.example.com".`, }, - Address: codersdk.StringFlag{ + Address: &codersdk.StringFlag{ Name: "Bind Address", Flag: "address", EnvVar: "CODER_ADDRESS", @@ -40,197 +42,201 @@ func Flags() codersdk.DeploymentFlags { Description: "Bind address of the server.", Default: "127.0.0.1:3000", }, - AutobuildPollInterval: codersdk.DurationFlag{ + AutobuildPollInterval: &codersdk.DurationFlag{ Name: "Autobuild Poll Interval", Flag: "autobuild-poll-interval", EnvVar: "CODER_AUTOBUILD_POLL_INTERVAL", Description: "Interval to poll for scheduled workspace builds.", + Hidden: true, Default: time.Minute, }, - DerpServerEnable: codersdk.BoolFlag{ + DerpServerEnable: &codersdk.BoolFlag{ Name: "DERP Server Enabled", Flag: "derp-server-enable", EnvVar: "CODER_DERP_SERVER_ENABLE", Description: "Whether to enable or disable the embedded DERP relay server.", Default: true, }, - DerpServerRegionID: codersdk.IntFlag{ + DerpServerRegionID: &codersdk.IntFlag{ Name: "DERP Server Region ID", Flag: "derp-server-region-id", EnvVar: "CODER_DERP_SERVER_REGION_ID", Description: "Region ID to use for the embedded DERP server.", Default: 999, }, - DerpServerRegionCode: codersdk.StringFlag{ + DerpServerRegionCode: &codersdk.StringFlag{ Name: "DERP Server Region Code", Flag: "derp-server-region-code", EnvVar: "CODER_DERP_SERVER_REGION_CODE", Description: "Region code to use for the embedded DERP server.", Default: "coder", }, - DerpServerRegionName: codersdk.StringFlag{ + DerpServerRegionName: &codersdk.StringFlag{ Name: "DERP Server Region Name", Flag: "derp-server-region-name", EnvVar: "CODER_DERP_SERVER_REGION_NAME", Description: "Region name that for the embedded DERP server.", Default: "Coder Embedded Relay", }, - DerpServerSTUNAddresses: codersdk.StringArrayFlag{ + DerpServerSTUNAddresses: &codersdk.StringArrayFlag{ Name: "DERP Server STUN Addresses", Flag: "derp-server-stun-addresses", EnvVar: "CODER_DERP_SERVER_STUN_ADDRESSES", Description: "Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections.", Default: []string{"stun.l.google.com:19302"}, }, - DerpConfigURL: codersdk.StringFlag{ + DerpConfigURL: &codersdk.StringFlag{ Name: "DERP Config URL", Flag: "derp-config-url", EnvVar: "CODER_DERP_CONFIG_URL", Description: "URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/", }, - DerpConfigPath: codersdk.StringFlag{ + DerpConfigPath: &codersdk.StringFlag{ Name: "DERP Config Path", Flag: "derp-config-path", EnvVar: "CODER_DERP_CONFIG_PATH", Description: "Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/", }, - PromEnabled: codersdk.BoolFlag{ + PromEnabled: &codersdk.BoolFlag{ Name: "Prometheus Enabled", Flag: "prometheus-enable", EnvVar: "CODER_PROMETHEUS_ENABLE", Description: "Serve prometheus metrics on the address defined by `prometheus-address`.", }, - PromAddress: codersdk.StringFlag{ + PromAddress: &codersdk.StringFlag{ Name: "Prometheus Address", Flag: "prometheus-address", EnvVar: "CODER_PROMETHEUS_ADDRESS", Description: "The bind address to serve prometheus metrics.", Default: "127.0.0.1:2112", }, - PprofEnabled: codersdk.BoolFlag{ + PprofEnabled: &codersdk.BoolFlag{ Name: "pprof Enabled", Flag: "pprof-enable", EnvVar: "CODER_PPROF_ENABLE", Description: "Serve pprof metrics on the address defined by `pprof-address`.", }, - PprofAddress: codersdk.StringFlag{ + PprofAddress: &codersdk.StringFlag{ Name: "pprof Address", Flag: "pprof-address", EnvVar: "CODER_PPROF_ADDRESS", Description: "The bind address to serve pprof.", Default: "127.0.0.1:6060", }, - CacheDir: codersdk.StringFlag{ + CacheDir: &codersdk.StringFlag{ Name: "Cache Directory", Flag: "cache-dir", EnvVar: "CODER_CACHE_DIRECTORY", Description: "The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.", Default: defaultCacheDir(), }, - InMemoryDatabase: codersdk.BoolFlag{ + InMemoryDatabase: &codersdk.BoolFlag{ Name: "In-Memory Database", Flag: "in-memory", EnvVar: "CODER_INMEMORY", Description: "Controls whether data will be stored in an in-memory database.", + Hidden: true, }, - ProvisionerDaemonCount: codersdk.IntFlag{ + ProvisionerDaemonCount: &codersdk.IntFlag{ Name: "Provisioner Daemons", Flag: "provisioner-daemons", EnvVar: "CODER_PROVISIONER_DAEMONS", Description: "Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this.", Default: 3, }, - PostgresURL: codersdk.StringFlag{ + PostgresURL: &codersdk.StringFlag{ Name: "Postgres URL", Flag: "postgres-url", EnvVar: "CODER_PG_CONNECTION_URL", Description: "URL of a PostgreSQL database. If empty, PostgreSQL binaries will be downloaded from Maven (https://repo1.maven.org/maven2) and store all data in the config root. Access the built-in database with \"coder server postgres-builtin-url\"", Secret: true, }, - OAuth2GithubClientID: codersdk.StringFlag{ + OAuth2GithubClientID: &codersdk.StringFlag{ Name: "Oauth2 Github Client ID", Flag: "oauth2-github-client-id", EnvVar: "CODER_OAUTH2_GITHUB_CLIENT_ID", Description: "Client ID for Login with GitHub.", }, - OAuth2GithubClientSecret: codersdk.StringFlag{ + OAuth2GithubClientSecret: &codersdk.StringFlag{ Name: "Oauth2 Github Client Secret", Flag: "oauth2-github-client-secret", EnvVar: "CODER_OAUTH2_GITHUB_CLIENT_SECRET", Description: "Client secret for Login with GitHub.", Secret: true, }, - OAuth2GithubAllowedOrganizations: codersdk.StringArrayFlag{ + OAuth2GithubAllowedOrganizations: &codersdk.StringArrayFlag{ Name: "Oauth2 Github Allowed Organizations", Flag: "oauth2-github-allowed-orgs", EnvVar: "CODER_OAUTH2_GITHUB_ALLOWED_ORGS", Description: "Organizations the user must be a member of to Login with GitHub.", + Default: []string{}, }, - OAuth2GithubAllowedTeams: codersdk.StringArrayFlag{ + OAuth2GithubAllowedTeams: &codersdk.StringArrayFlag{ Name: "Oauth2 Github Allowed Teams", Flag: "oauth2-github-allowed-teams", EnvVar: "CODER_OAUTH2_GITHUB_ALLOWED_TEAMS", Description: "Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /.", + Default: []string{}, }, - OAuth2GithubAllowSignups: codersdk.BoolFlag{ + OAuth2GithubAllowSignups: &codersdk.BoolFlag{ Name: "Oauth2 Github Allow Signups", Flag: "oauth2-github-allow-signups", EnvVar: "CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS", Description: "Whether new users can sign up with GitHub.", }, - OAuth2GithubEnterpriseBaseURL: codersdk.StringFlag{ + OAuth2GithubEnterpriseBaseURL: &codersdk.StringFlag{ Name: "Oauth2 Github Enterprise Base URL", Flag: "oauth2-github-enterprise-base-url", EnvVar: "CODER_OAUTH2_GITHUB_ENTERPRISE_BASE_URL", Description: "Base URL of a GitHub Enterprise deployment to use for Login with GitHub.", }, - OIDCAllowSignups: codersdk.BoolFlag{ + OIDCAllowSignups: &codersdk.BoolFlag{ Name: "OIDC Allow Signups", Flag: "oidc-allow-signups", EnvVar: "CODER_OIDC_ALLOW_SIGNUPS", Description: "Whether new users can sign up with OIDC.", Default: true, }, - OIDCClientID: codersdk.StringFlag{ + OIDCClientID: &codersdk.StringFlag{ Name: "OIDC Client ID", Flag: "oidc-client-id", EnvVar: "CODER_OIDC_CLIENT_ID", Description: "Client ID to use for Login with OIDC.", }, - OIDCClientSecret: codersdk.StringFlag{ + OIDCClientSecret: &codersdk.StringFlag{ Name: "OIDC Client Secret", Flag: "oidc-client-secret", EnvVar: "CODER_OIDC_CLIENT_SECRET", Description: "Client secret to use for Login with OIDC.", Secret: true, }, - OIDCEmailDomain: codersdk.StringFlag{ + OIDCEmailDomain: &codersdk.StringFlag{ Name: "OIDC Email Domain", Flag: "oidc-email-domain", EnvVar: "CODER_OIDC_EMAIL_DOMAIN", Description: "Email domain that clients logging in with OIDC must match.", }, - OIDCIssuerURL: codersdk.StringFlag{ + OIDCIssuerURL: &codersdk.StringFlag{ Name: "OIDC Issuer URL", Flag: "oidc-issuer-url", EnvVar: "CODER_OIDC_ISSUER_URL", Description: "Issuer URL to use for Login with OIDC.", }, - OIDCScopes: codersdk.StringArrayFlag{ + OIDCScopes: &codersdk.StringArrayFlag{ Name: "OIDC Scopes", Flag: "oidc-scopes", EnvVar: "CODER_OIDC_SCOPES", Description: "Scopes to grant when authenticating with OIDC.", Default: []string{oidc.ScopeOpenID, "profile", "email"}, }, - TelemetryEnable: codersdk.BoolFlag{ + TelemetryEnable: &codersdk.BoolFlag{ Name: "Telemetry Enabled", Flag: "telemetry", EnvVar: "CODER_TELEMETRY", Description: "Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product.", Default: flag.Lookup("test.v") == nil, }, - TelemetryTraceEnable: codersdk.BoolFlag{ + TelemetryTraceEnable: &codersdk.BoolFlag{ Name: "Trace Telemetry Enabled", Flag: "telemetry-trace", EnvVar: "CODER_TELEMETRY_TRACE", @@ -238,20 +244,21 @@ func Flags() codersdk.DeploymentFlags { Description: "Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option.", Default: flag.Lookup("test.v") == nil, }, - TelemetryURL: codersdk.StringFlag{ + TelemetryURL: &codersdk.StringFlag{ Name: "Telemetry URL", Flag: "telemetry-url", EnvVar: "CODER_TELEMETRY_URL", Description: "URL to send telemetry.", + Hidden: true, Default: "https://telemetry.coder.com", }, - TLSEnable: codersdk.BoolFlag{ + TLSEnable: &codersdk.BoolFlag{ Name: "TLS Enabled", Flag: "tls-enable", EnvVar: "CODER_TLS_ENABLE", Description: "Whether TLS will be enabled.", }, - TLSCertFiles: codersdk.StringArrayFlag{ + TLSCertFiles: &codersdk.StringArrayFlag{ Name: "TLS Cert Files", Flag: "tls-cert-file", EnvVar: "CODER_TLS_CERT_FILE", @@ -260,13 +267,13 @@ func Flags() codersdk.DeploymentFlags { "and the CA certificate together. The primary certificate should appear first in the combined file.", Default: []string{}, }, - TLSClientCAFile: codersdk.StringFlag{ + TLSClientCAFile: &codersdk.StringFlag{ Name: "TLS Client CA File", Flag: "tls-client-ca-file", EnvVar: "CODER_TLS_CLIENT_CA_FILE", Description: "PEM-encoded Certificate Authority file used for checking the authenticity of client", }, - TLSClientAuth: codersdk.StringFlag{ + TLSClientAuth: &codersdk.StringFlag{ Name: "TLS Client Auth", Flag: "tls-client-auth", EnvVar: "CODER_TLS_CLIENT_AUTH", @@ -274,33 +281,33 @@ func Flags() codersdk.DeploymentFlags { `Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify"`, Default: "request", }, - TLSKeyFiles: codersdk.StringArrayFlag{ + TLSKeyFiles: &codersdk.StringArrayFlag{ Name: "TLS Key Files", Flag: "tls-key-file", EnvVar: "CODER_TLS_KEY_FILE", Description: "Paths to the private keys for each of the certificates. It requires a PEM-encoded file", Default: []string{}, }, - TLSMinVersion: codersdk.StringFlag{ + TLSMinVersion: &codersdk.StringFlag{ Name: "TLS Min Version", Flag: "tls-min-version", EnvVar: "CODER_TLS_MIN_VERSION", Description: `Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13"`, Default: "tls12", }, - TraceEnable: codersdk.BoolFlag{ + TraceEnable: &codersdk.BoolFlag{ Name: "Trace Enabled", Flag: "trace", EnvVar: "CODER_TRACE", Description: "Whether application tracing data is collected.", }, - SecureAuthCookie: codersdk.BoolFlag{ + SecureAuthCookie: &codersdk.BoolFlag{ Name: "Secure Auth Cookie", Flag: "secure-auth-cookie", EnvVar: "CODER_SECURE_AUTH_COOKIE", Description: "Controls if the 'Secure' property is set on browser session cookies", }, - SSHKeygenAlgorithm: codersdk.StringFlag{ + SSHKeygenAlgorithm: &codersdk.StringFlag{ Name: "SSH Keygen Algorithm", Flag: "ssh-keygen-algorithm", EnvVar: "CODER_SSH_KEYGEN_ALGORITHM", @@ -308,35 +315,38 @@ func Flags() codersdk.DeploymentFlags { `Accepted values are "ed25519", "ecdsa", or "rsa4096"`, Default: "ed25519", }, - AutoImportTemplates: codersdk.StringArrayFlag{ + AutoImportTemplates: &codersdk.StringArrayFlag{ Name: "Auto Import Templates", Flag: "auto-import-template", EnvVar: "CODER_TEMPLATE_AUTOIMPORT", Description: "Templates to auto-import. Available auto-importable templates are: kubernetes", + Hidden: true, Default: []string{}, }, - MetricsCacheRefreshInterval: codersdk.DurationFlag{ + MetricsCacheRefreshInterval: &codersdk.DurationFlag{ Name: "Metrics Cache Refresh Interval", Flag: "metrics-cache-refresh-interval", EnvVar: "CODER_METRICS_CACHE_REFRESH_INTERVAL", Description: "How frequently metrics are refreshed", + Hidden: true, Default: time.Hour, }, - AgentStatRefreshInterval: codersdk.DurationFlag{ + AgentStatRefreshInterval: &codersdk.DurationFlag{ Name: "Agent Stats Refresh Interval", Flag: "agent-stats-refresh-interval", EnvVar: "CODER_AGENT_STATS_REFRESH_INTERVAL", Description: "How frequently agent stats are recorded", + Hidden: true, Default: 10 * time.Minute, }, - Verbose: codersdk.BoolFlag{ + Verbose: &codersdk.BoolFlag{ Name: "Verbose Logging", Flag: "verbose", EnvVar: "CODER_VERBOSE", Shorthand: "v", Description: "Enables verbose logging.", }, - AuditLogging: codersdk.BoolFlag{ + AuditLogging: &codersdk.BoolFlag{ Name: "Audit Logging", Flag: "audit-logging", EnvVar: "CODER_AUDIT_LOGGING", @@ -344,14 +354,14 @@ func Flags() codersdk.DeploymentFlags { Default: true, Enterprise: true, }, - BrowserOnly: codersdk.BoolFlag{ + BrowserOnly: &codersdk.BoolFlag{ Name: "Browser Only", Flag: "browser-only", EnvVar: "CODER_BROWSER_ONLY", Description: "Whether Coder only allows connections to workspaces via the browser.", Enterprise: true, }, - SCIMAuthHeader: codersdk.StringFlag{ + SCIMAuthHeader: &codersdk.StringFlag{ Name: "SCIM Authentication Header", Flag: "scim-auth-header", EnvVar: "CODER_SCIM_API_KEY", @@ -359,7 +369,7 @@ func Flags() codersdk.DeploymentFlags { Secret: true, Enterprise: true, }, - UserWorkspaceQuota: codersdk.IntFlag{ + UserWorkspaceQuota: &codersdk.IntFlag{ Name: "User Workspace Quota", Flag: "user-workspace-quota", EnvVar: "CODER_USER_WORKSPACE_QUOTA", @@ -375,10 +385,12 @@ func RemoveSensitiveValues(df codersdk.DeploymentFlags) codersdk.DeploymentFlags t := v.Type() for i := 0; i < t.NumField(); i++ { fv := v.Field(i) - if v, ok := fv.Interface().(codersdk.StringFlag); ok { - if v.Secret && v.Value != "" { + if vp, ok := fv.Interface().(*codersdk.StringFlag); ok { + if vp.Secret && vp.Value != "" { + // Make a copy and remove the value. + v := *vp v.Value = secretValue - fv.Set(reflect.ValueOf(v)) + fv.Set(reflect.ValueOf(&v)) } } } @@ -386,6 +398,43 @@ func RemoveSensitiveValues(df codersdk.DeploymentFlags) codersdk.DeploymentFlags return df } +//nolint:revive +func AttachFlags(flagset *pflag.FlagSet, df *codersdk.DeploymentFlags, enterprise bool) { + v := reflect.ValueOf(df).Elem() + t := v.Type() + for i := 0; i < t.NumField(); i++ { + fv := v.Field(i) + fve := fv.Elem() + e := fve.FieldByName("Enterprise").Bool() + if e != enterprise { + continue + } + if e { + d := fve.FieldByName("Description").String() + d += cliui.Styles.Keyword.Render(" This is an Enterprise feature. Contact sales@coder.com for licensing") + fve.FieldByName("Description").SetString(d) + } + + switch v := fv.Interface().(type) { + case *codersdk.StringFlag: + StringFlag(flagset, v) + case *codersdk.StringArrayFlag: + StringArrayFlag(flagset, v) + case *codersdk.IntFlag: + IntFlag(flagset, v) + case *codersdk.BoolFlag: + BoolFlag(flagset, v) + case *codersdk.DurationFlag: + DurationFlag(flagset, v) + default: + panic(fmt.Sprintf("unknown flag type: %T", v)) + } + if fve.FieldByName("Hidden").Bool() { + _ = flagset.MarkHidden(fve.FieldByName("Flag").String()) + } + } +} + func StringFlag(flagset *pflag.FlagSet, fl *codersdk.StringFlag) { cliflag.StringVarP(flagset, &fl.Value, diff --git a/cli/deployment/flags_test.go b/cli/deployment/flags_test.go new file mode 100644 index 0000000000..8b411068db --- /dev/null +++ b/cli/deployment/flags_test.go @@ -0,0 +1,32 @@ +package deployment_test + +import ( + "testing" + + "github.com/spf13/pflag" + "github.com/stretchr/testify/require" + + "github.com/coder/coder/cli/deployment" +) + +func TestFlags(t *testing.T) { + t.Parallel() + + df := deployment.Flags() + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + deployment.AttachFlags(fs, df, false) + + require.NotNil(t, fs.Lookup("access-url")) + require.False(t, fs.Lookup("access-url").Hidden) + require.True(t, fs.Lookup("telemetry-url").Hidden) + require.NotEmpty(t, fs.Lookup("telemetry-url").DefValue) + require.Nil(t, fs.Lookup("audit-logging")) + + df = deployment.Flags() + fs = pflag.NewFlagSet("test-enterprise", pflag.ContinueOnError) + deployment.AttachFlags(fs, df, true) + + require.Nil(t, fs.Lookup("access-url")) + require.NotNil(t, fs.Lookup("audit-logging")) + require.Contains(t, fs.Lookup("audit-logging").Usage, "This is an Enterprise feature") +} diff --git a/cli/root.go b/cli/root.go index 44988f0bb9..18a6f423f0 100644 --- a/cli/root.go +++ b/cli/root.go @@ -99,9 +99,7 @@ func Core() []*cobra.Command { } func AGPL() []*cobra.Command { - df := deployment.Flags() - all := append(Core(), Server(df, func(_ context.Context, o *coderd.Options) (*coderd.API, error) { - o.DeploymentFlags = &df + all := append(Core(), Server(deployment.Flags(), func(_ context.Context, o *coderd.Options) (*coderd.API, error) { return coderd.New(o), nil })) return all diff --git a/cli/server.go b/cli/server.go index bd5f9a143c..e3cad09ca2 100644 --- a/cli/server.go +++ b/cli/server.go @@ -67,7 +67,7 @@ import ( ) // nolint:gocyclo -func Server(dflags codersdk.DeploymentFlags, newAPI func(context.Context, *coderd.Options) (*coderd.API, error)) *cobra.Command { +func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *coderd.Options) (*coderd.API, error)) *cobra.Command { root := &cobra.Command{ Use: "server", Short: "Start a Coder server", @@ -318,7 +318,7 @@ func Server(dflags codersdk.DeploymentFlags, newAPI func(context.Context, *coder MetricsCacheRefreshInterval: dflags.MetricsCacheRefreshInterval.Value, AgentStatsRefreshInterval: dflags.AgentStatRefreshInterval.Value, Experimental: ExperimentalEnabled(cmd), - DeploymentFlags: &dflags, + DeploymentFlags: dflags, } if dflags.OAuth2GithubClientSecret.Value != "" { @@ -712,58 +712,7 @@ func Server(dflags codersdk.DeploymentFlags, newAPI func(context.Context, *coder }, }) - deployment.StringFlag(root.Flags(), &dflags.AccessURL) - deployment.StringFlag(root.Flags(), &dflags.WildcardAccessURL) - deployment.StringFlag(root.Flags(), &dflags.Address) - deployment.DurationFlag(root.Flags(), &dflags.AutobuildPollInterval) - _ = root.Flags().MarkHidden(dflags.AutobuildPollInterval.Flag) - deployment.BoolFlag(root.Flags(), &dflags.DerpServerEnable) - deployment.IntFlag(root.Flags(), &dflags.DerpServerRegionID) - deployment.StringFlag(root.Flags(), &dflags.DerpServerRegionCode) - deployment.StringFlag(root.Flags(), &dflags.DerpServerRegionName) - deployment.StringArrayFlag(root.Flags(), &dflags.DerpServerSTUNAddresses) - deployment.StringFlag(root.Flags(), &dflags.DerpConfigURL) - deployment.StringFlag(root.Flags(), &dflags.DerpConfigPath) - deployment.BoolFlag(root.Flags(), &dflags.PromEnabled) - deployment.StringFlag(root.Flags(), &dflags.PromAddress) - deployment.BoolFlag(root.Flags(), &dflags.PprofEnabled) - deployment.StringFlag(root.Flags(), &dflags.CacheDir) - deployment.BoolFlag(root.Flags(), &dflags.InMemoryDatabase) - _ = root.Flags().MarkHidden(dflags.InMemoryDatabase.Flag) - deployment.IntFlag(root.Flags(), &dflags.ProvisionerDaemonCount) - deployment.StringFlag(root.Flags(), &dflags.PostgresURL) - deployment.StringFlag(root.Flags(), &dflags.OAuth2GithubClientID) - deployment.StringFlag(root.Flags(), &dflags.OAuth2GithubClientSecret) - deployment.StringArrayFlag(root.Flags(), &dflags.OAuth2GithubAllowedOrganizations) - deployment.StringArrayFlag(root.Flags(), &dflags.OAuth2GithubAllowedTeams) - deployment.BoolFlag(root.Flags(), &dflags.OAuth2GithubAllowSignups) - deployment.StringFlag(root.Flags(), &dflags.OAuth2GithubEnterpriseBaseURL) - deployment.BoolFlag(root.Flags(), &dflags.OIDCAllowSignups) - deployment.StringFlag(root.Flags(), &dflags.OIDCClientID) - deployment.StringFlag(root.Flags(), &dflags.OIDCClientSecret) - deployment.StringFlag(root.Flags(), &dflags.OIDCEmailDomain) - deployment.StringFlag(root.Flags(), &dflags.OIDCIssuerURL) - deployment.StringArrayFlag(root.Flags(), &dflags.OIDCScopes) - deployment.BoolFlag(root.Flags(), &dflags.TelemetryEnable) - deployment.BoolFlag(root.Flags(), &dflags.TelemetryTraceEnable) - deployment.StringFlag(root.Flags(), &dflags.TelemetryURL) - _ = root.Flags().MarkHidden(dflags.TelemetryURL.Flag) - deployment.BoolFlag(root.Flags(), &dflags.TLSEnable) - deployment.StringArrayFlag(root.Flags(), &dflags.TLSCertFiles) - deployment.StringFlag(root.Flags(), &dflags.TLSClientCAFile) - deployment.StringFlag(root.Flags(), &dflags.TLSClientAuth) - deployment.StringArrayFlag(root.Flags(), &dflags.TLSKeyFiles) - deployment.StringFlag(root.Flags(), &dflags.TLSMinVersion) - deployment.BoolFlag(root.Flags(), &dflags.TraceEnable) - deployment.BoolFlag(root.Flags(), &dflags.SecureAuthCookie) - deployment.StringFlag(root.Flags(), &dflags.SSHKeygenAlgorithm) - deployment.StringArrayFlag(root.Flags(), &dflags.AutoImportTemplates) - _ = root.Flags().MarkHidden(dflags.AutoImportTemplates.Flag) - deployment.DurationFlag(root.Flags(), &dflags.MetricsCacheRefreshInterval) - _ = root.Flags().MarkHidden(dflags.MetricsCacheRefreshInterval.Flag) - deployment.DurationFlag(root.Flags(), &dflags.AgentStatRefreshInterval) - _ = root.Flags().MarkHidden(dflags.AgentStatRefreshInterval.Flag) - deployment.BoolFlag(root.Flags(), &dflags.Verbose) + deployment.AttachFlags(root.Flags(), dflags, false) return root } diff --git a/coderd/flags_test.go b/coderd/flags_test.go index b653168316..dbff599238 100644 --- a/coderd/flags_test.go +++ b/coderd/flags_test.go @@ -30,7 +30,7 @@ func TestDeploymentFlagSecrets(t *testing.T) { df.SCIMAuthHeader.Value = hi client := coderdtest.New(t, &coderdtest.Options{ - DeploymentFlags: &df, + DeploymentFlags: df, }) _ = coderdtest.CreateFirstUser(t, client) scrubbed, err := client.DeploymentFlags(ctx) diff --git a/codersdk/flags.go b/codersdk/flags.go index e603907242..92f02941a5 100644 --- a/codersdk/flags.go +++ b/codersdk/flags.go @@ -10,57 +10,57 @@ import ( ) type DeploymentFlags struct { - AccessURL StringFlag `json:"access_url"` - WildcardAccessURL StringFlag `json:"wildcard_access_url"` - Address StringFlag `json:"address"` - AutobuildPollInterval DurationFlag `json:"autobuild_poll_interval"` - DerpServerEnable BoolFlag `json:"derp_server_enabled"` - DerpServerRegionID IntFlag `json:"derp_server_region_id"` - DerpServerRegionCode StringFlag `json:"derp_server_region_code"` - DerpServerRegionName StringFlag `json:"derp_server_region_name"` - DerpServerSTUNAddresses StringArrayFlag `json:"derp_server_stun_address"` - DerpConfigURL StringFlag `json:"derp_config_url"` - DerpConfigPath StringFlag `json:"derp_config_path"` - PromEnabled BoolFlag `json:"prom_enabled"` - PromAddress StringFlag `json:"prom_address"` - PprofEnabled BoolFlag `json:"pprof_enabled"` - PprofAddress StringFlag `json:"pprof_address"` - CacheDir StringFlag `json:"cache_dir"` - InMemoryDatabase BoolFlag `json:"in_memory_database"` - ProvisionerDaemonCount IntFlag `json:"provisioner_daemon_count"` - PostgresURL StringFlag `json:"postgres_url"` - OAuth2GithubClientID StringFlag `json:"oauth2_github_client_id"` - OAuth2GithubClientSecret StringFlag `json:"oauth2_github_client_secret"` - OAuth2GithubAllowedOrganizations StringArrayFlag `json:"oauth2_github_allowed_organizations"` - OAuth2GithubAllowedTeams StringArrayFlag `json:"oauth2_github_allowed_teams"` - OAuth2GithubAllowSignups BoolFlag `json:"oauth2_github_allow_signups"` - OAuth2GithubEnterpriseBaseURL StringFlag `json:"oauth2_github_enterprise_base_url"` - OIDCAllowSignups BoolFlag `json:"oidc_allow_signups"` - OIDCClientID StringFlag `json:"oidc_client_id"` - OIDCClientSecret StringFlag `json:"oidc_cliet_secret"` - OIDCEmailDomain StringFlag `json:"oidc_email_domain"` - OIDCIssuerURL StringFlag `json:"oidc_issuer_url"` - OIDCScopes StringArrayFlag `json:"oidc_scopes"` - TelemetryEnable BoolFlag `json:"telemetry_enable"` - TelemetryTraceEnable BoolFlag `json:"telemetry_trace_enable"` - TelemetryURL StringFlag `json:"telemetry_url"` - TLSEnable BoolFlag `json:"tls_enable"` - TLSCertFiles StringArrayFlag `json:"tls_cert_files"` - TLSClientCAFile StringFlag `json:"tls_client_ca_file"` - TLSClientAuth StringFlag `json:"tls_client_auth"` - TLSKeyFiles StringArrayFlag `json:"tls_key_tiles"` - TLSMinVersion StringFlag `json:"tls_min_version"` - TraceEnable BoolFlag `json:"trace_enable"` - SecureAuthCookie BoolFlag `json:"secure_auth_cookie"` - SSHKeygenAlgorithm StringFlag `json:"ssh_keygen_algorithm"` - AutoImportTemplates StringArrayFlag `json:"auto_import_templates"` - MetricsCacheRefreshInterval DurationFlag `json:"metrics_cache_refresh_interval"` - AgentStatRefreshInterval DurationFlag `json:"agent_stat_refresh_interval"` - Verbose BoolFlag `json:"verbose"` - AuditLogging BoolFlag `json:"audit_logging"` - BrowserOnly BoolFlag `json:"browser_only"` - SCIMAuthHeader StringFlag `json:"scim_auth_header"` - UserWorkspaceQuota IntFlag `json:"user_workspace_quota"` + AccessURL *StringFlag `json:"access_url" typescript:",notnull"` + WildcardAccessURL *StringFlag `json:"wildcard_access_url" typescript:",notnull"` + Address *StringFlag `json:"address" typescript:",notnull"` + AutobuildPollInterval *DurationFlag `json:"autobuild_poll_interval" typescript:",notnull"` + DerpServerEnable *BoolFlag `json:"derp_server_enabled" typescript:",notnull"` + DerpServerRegionID *IntFlag `json:"derp_server_region_id" typescript:",notnull"` + DerpServerRegionCode *StringFlag `json:"derp_server_region_code" typescript:",notnull"` + DerpServerRegionName *StringFlag `json:"derp_server_region_name" typescript:",notnull"` + DerpServerSTUNAddresses *StringArrayFlag `json:"derp_server_stun_address" typescript:",notnull"` + DerpConfigURL *StringFlag `json:"derp_config_url" typescript:",notnull"` + DerpConfigPath *StringFlag `json:"derp_config_path" typescript:",notnull"` + PromEnabled *BoolFlag `json:"prom_enabled" typescript:",notnull"` + PromAddress *StringFlag `json:"prom_address" typescript:",notnull"` + PprofEnabled *BoolFlag `json:"pprof_enabled" typescript:",notnull"` + PprofAddress *StringFlag `json:"pprof_address" typescript:",notnull"` + CacheDir *StringFlag `json:"cache_dir" typescript:",notnull"` + InMemoryDatabase *BoolFlag `json:"in_memory_database" typescript:",notnull"` + ProvisionerDaemonCount *IntFlag `json:"provisioner_daemon_count" typescript:",notnull"` + PostgresURL *StringFlag `json:"postgres_url" typescript:",notnull"` + OAuth2GithubClientID *StringFlag `json:"oauth2_github_client_id" typescript:",notnull"` + OAuth2GithubClientSecret *StringFlag `json:"oauth2_github_client_secret" typescript:",notnull"` + OAuth2GithubAllowedOrganizations *StringArrayFlag `json:"oauth2_github_allowed_organizations" typescript:",notnull"` + OAuth2GithubAllowedTeams *StringArrayFlag `json:"oauth2_github_allowed_teams" typescript:",notnull"` + OAuth2GithubAllowSignups *BoolFlag `json:"oauth2_github_allow_signups" typescript:",notnull"` + OAuth2GithubEnterpriseBaseURL *StringFlag `json:"oauth2_github_enterprise_base_url" typescript:",notnull"` + OIDCAllowSignups *BoolFlag `json:"oidc_allow_signups" typescript:",notnull"` + OIDCClientID *StringFlag `json:"oidc_client_id" typescript:",notnull"` + OIDCClientSecret *StringFlag `json:"oidc_cliet_secret" typescript:",notnull"` + OIDCEmailDomain *StringFlag `json:"oidc_email_domain" typescript:",notnull"` + OIDCIssuerURL *StringFlag `json:"oidc_issuer_url" typescript:",notnull"` + OIDCScopes *StringArrayFlag `json:"oidc_scopes" typescript:",notnull"` + TelemetryEnable *BoolFlag `json:"telemetry_enable" typescript:",notnull"` + TelemetryTraceEnable *BoolFlag `json:"telemetry_trace_enable" typescript:",notnull"` + TelemetryURL *StringFlag `json:"telemetry_url" typescript:",notnull"` + TLSEnable *BoolFlag `json:"tls_enable" typescript:",notnull"` + TLSCertFiles *StringArrayFlag `json:"tls_cert_files" typescript:",notnull"` + TLSClientCAFile *StringFlag `json:"tls_client_ca_file" typescript:",notnull"` + TLSClientAuth *StringFlag `json:"tls_client_auth" typescript:",notnull"` + TLSKeyFiles *StringArrayFlag `json:"tls_key_tiles" typescript:",notnull"` + TLSMinVersion *StringFlag `json:"tls_min_version" typescript:",notnull"` + TraceEnable *BoolFlag `json:"trace_enable" typescript:",notnull"` + SecureAuthCookie *BoolFlag `json:"secure_auth_cookie" typescript:",notnull"` + SSHKeygenAlgorithm *StringFlag `json:"ssh_keygen_algorithm" typescript:",notnull"` + AutoImportTemplates *StringArrayFlag `json:"auto_import_templates" typescript:",notnull"` + MetricsCacheRefreshInterval *DurationFlag `json:"metrics_cache_refresh_interval" typescript:",notnull"` + AgentStatRefreshInterval *DurationFlag `json:"agent_stat_refresh_interval" typescript:",notnull"` + Verbose *BoolFlag `json:"verbose" typescript:",notnull"` + AuditLogging *BoolFlag `json:"audit_logging" typescript:",notnull"` + BrowserOnly *BoolFlag `json:"browser_only" typescript:",notnull"` + SCIMAuthHeader *StringFlag `json:"scim_auth_header" typescript:",notnull"` + UserWorkspaceQuota *IntFlag `json:"user_workspace_quota" typescript:",notnull"` } type StringFlag struct { @@ -71,6 +71,7 @@ type StringFlag struct { Description string `json:"description"` Enterprise bool `json:"enterprise"` Secret bool `json:"secret"` + Hidden bool `json:"hidden"` Default string `json:"default"` Value string `json:"value"` } @@ -82,6 +83,7 @@ type BoolFlag struct { Shorthand string `json:"shorthand"` Description string `json:"description"` Enterprise bool `json:"enterprise"` + Hidden bool `json:"hidden"` Default bool `json:"default"` Value bool `json:"value"` } @@ -93,6 +95,7 @@ type IntFlag struct { Shorthand string `json:"shorthand"` Description string `json:"description"` Enterprise bool `json:"enterprise"` + Hidden bool `json:"hidden"` Default int `json:"default"` Value int `json:"value"` } @@ -104,6 +107,7 @@ type DurationFlag struct { Shorthand string `json:"shorthand"` Description string `json:"description"` Enterprise bool `json:"enterprise"` + Hidden bool `json:"hidden"` Default time.Duration `json:"default"` Value time.Duration `json:"value"` } @@ -115,6 +119,7 @@ type StringArrayFlag struct { Shorthand string `json:"shorthand"` Description string `json:"description"` Enterprise bool `json:"enterprise"` + Hidden bool `json:"hidden"` Default []string `json:"default"` Value []string `json:"value"` } diff --git a/codersdk/templateversions.go b/codersdk/templateversions.go index 5baad0a9e9..7e861c3569 100644 --- a/codersdk/templateversions.go +++ b/codersdk/templateversions.go @@ -107,8 +107,8 @@ func (c *Client) TemplateVersionLogsAfter(ctx context.Context, version uuid.UUID // CreateTemplateVersionDryRunRequest defines the request parameters for // CreateTemplateVersionDryRun. type CreateTemplateVersionDryRunRequest struct { - WorkspaceName string - ParameterValues []CreateParameterRequest + WorkspaceName string `json:"workspace_name"` + ParameterValues []CreateParameterRequest `json:"parameter_values"` } // CreateTemplateVersionDryRun begins a dry-run provisioner job against the diff --git a/enterprise/cli/server.go b/enterprise/cli/server.go index 1546a7be60..62af6f2888 100644 --- a/enterprise/cli/server.go +++ b/enterprise/cli/server.go @@ -5,7 +5,6 @@ import ( "github.com/spf13/cobra" - "github.com/coder/coder/cli/cliui" "github.com/coder/coder/cli/deployment" "github.com/coder/coder/enterprise/coderd" @@ -16,7 +15,6 @@ import ( func server() *cobra.Command { dflags := deployment.Flags() cmd := agpl.Server(dflags, func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, error) { - options.DeploymentFlags = &dflags o := &coderd.Options{ AuditLogging: dflags.AuditLogging.Value, BrowserOnly: dflags.BrowserOnly.Value, @@ -32,17 +30,7 @@ func server() *cobra.Command { return api.AGPL, nil }) - // append enterprise description to flags - enterpriseOnly := cliui.Styles.Keyword.Render(" This is an Enterprise feature. Contact sales@coder.com for licensing") - dflags.AuditLogging.Description += enterpriseOnly - dflags.BrowserOnly.Description += enterpriseOnly - dflags.SCIMAuthHeader.Description += enterpriseOnly - dflags.UserWorkspaceQuota.Description += enterpriseOnly - - deployment.BoolFlag(cmd.Flags(), &dflags.AuditLogging) - deployment.BoolFlag(cmd.Flags(), &dflags.BrowserOnly) - deployment.StringFlag(cmd.Flags(), &dflags.SCIMAuthHeader) - deployment.IntFlag(cmd.Flags(), &dflags.UserWorkspaceQuota) + deployment.AttachFlags(cmd.Flags(), dflags, true) return cmd } diff --git a/scripts/apitypings/main.go b/scripts/apitypings/main.go index 2381a9f15e..c6bc015dfb 100644 --- a/scripts/apitypings/main.go +++ b/scripts/apitypings/main.go @@ -11,6 +11,7 @@ import ( "sort" "strings" + "github.com/fatih/structtag" "golang.org/x/tools/go/packages" "golang.org/x/xerrors" @@ -282,32 +283,50 @@ func (g *Generator) buildStruct(obj types.Object, st *types.Struct) (string, err } field := st.Field(i) tag := reflect.StructTag(st.Tag(i)) + tags, err := structtag.Parse(string(tag)) + if err != nil { + panic("invalid struct tags on type " + obj.String()) + } // Use the json name if present - jsonName := tag.Get("json") - arr := strings.Split(jsonName, ",") - jsonName = arr[0] + jsonTag, err := tags.Get("json") + var ( + jsonName string + jsonOptional bool + ) + if err == nil { + jsonName = jsonTag.Name + if len(jsonTag.Options) > 0 && jsonTag.Options[0] == "omitempty" { + jsonOptional = true + } + } if jsonName == "" { jsonName = field.Name() } - jsonOptional := false - if len(arr) > 1 && arr[1] == "omitempty" { - jsonOptional = true + + // Infer the type. + tsType, err := g.typescriptType(field.Type()) + if err != nil { + return "", xerrors.Errorf("typescript type: %w", err) } - var tsType TypescriptType - // If a `typescript:"string"` exists, we take this, and do not try to infer. - typescriptTag := tag.Get("typescript") - if typescriptTag == "-" { - // Ignore this field - continue - } else if typescriptTag != "" { - tsType.ValueType = typescriptTag - } else { - var err error - tsType, err = g.typescriptType(field.Type()) - if err != nil { - return "", xerrors.Errorf("typescript type: %w", err) + // If a `typescript:"string"` exists, we take this, and ignore what we + // inferred. + typescriptTag, err := tags.Get("typescript") + if err == nil { + if err == nil && typescriptTag.Name == "-" { + // Completely ignore this field. + continue + } else if typescriptTag.Name != "" { + tsType = TypescriptType{ + ValueType: typescriptTag.Name, + } + } + + // If you specify `typescript:",notnull"` then mark the type as not + // optional. + if len(typescriptTag.Options) > 0 && typescriptTag.Options[0] == "notnull" { + tsType.Optional = false } } diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 6ad32fecce..9725b5ed23 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -133,6 +133,7 @@ export interface BoolFlag { readonly shorthand: string readonly description: string readonly enterprise: boolean + readonly hidden: boolean readonly default: boolean readonly value: boolean } @@ -196,8 +197,8 @@ export interface CreateTemplateRequest { // From codersdk/templateversions.go export interface CreateTemplateVersionDryRunRequest { - readonly WorkspaceName: string - readonly ParameterValues: CreateParameterRequest[] + readonly workspace_name: string + readonly parameter_values: CreateParameterRequest[] } // From codersdk/organizations.go @@ -319,6 +320,7 @@ export interface DurationFlag { readonly shorthand: string readonly description: string readonly enterprise: boolean + readonly hidden: boolean // This is likely an enum in an external package ("time.Duration") readonly default: number // This is likely an enum in an external package ("time.Duration") @@ -383,6 +385,7 @@ export interface IntFlag { readonly shorthand: string readonly description: string readonly enterprise: boolean + readonly hidden: boolean readonly default: number readonly value: number } @@ -546,6 +549,7 @@ export interface StringArrayFlag { readonly shorthand: string readonly description: string readonly enterprise: boolean + readonly hidden: boolean readonly default: string[] readonly value: string[] } @@ -559,6 +563,7 @@ export interface StringFlag { readonly description: string readonly enterprise: boolean readonly secret: boolean + readonly hidden: boolean readonly default: string readonly value: string }