From 305f2afd4db450f7e23e53184e32f9959c5e802a Mon Sep 17 00:00:00 2001 From: Garrett Date: Thu, 13 Oct 2022 20:04:44 +0000 Subject: [PATCH 01/43] wip --- cli/deployment/config.go | 136 ++++++++++++++++++++++ cli/deployment/flags.go | 122 ++++++++++---------- codersdk/config.go | 241 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 438 insertions(+), 61 deletions(-) create mode 100644 cli/deployment/config.go create mode 100644 codersdk/config.go diff --git a/cli/deployment/config.go b/cli/deployment/config.go new file mode 100644 index 0000000000000..735ee034e07b6 --- /dev/null +++ b/cli/deployment/config.go @@ -0,0 +1,136 @@ +package deployment + +import ( + "flag" + "time" + + "github.com/coreos/go-oidc/v3/oidc" + + "github.com/coder/coder/codersdk" +) + +func Config() codersdk.DeploymentConfig { + return codersdk.DeploymentConfig{ + // External URL to access your deployment. This must be accessible by all provisioned workspaces. + AccessURL: "", + // Specifies the wildcard hostname to use for workspace applications in the form "*.example.com". + WildcardAccessURL: "", + // Bind address of the server. + Address: "127.0.0.1:3000", + // Interval to poll for scheduled workspace builds. + AutobuildPollInterval: time.Minute, + DERP: codersdk.DERPConfig{ + Server: codersdk.DERPServerConfig{ + // Whether to enable or disable the embedded DERP relay server. + Enable: true, + // Region ID to use for the embedded DERP server. + RegionID: 999, + // Region code to use for the embedded DERP server. + RegionCode: "coder", + // Region name that for the embedded DERP server. + RegionName: "Coder Embedded Relay", + // Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections. + STUNAddresses: []string{"stun.l.google.com:19302"}, + }, + Config: codersdk.DERPConfigConfig{ + // URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/ + URL: "", + // Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/ + Path: "", + }, + }, + Prometheus: codersdk.PrometheusConfig{ + // Serve prometheus metrics on the address defined by `prometheus.address`. + Enable: false, + // The bind address to serve prometheus metrics. + Address: "127.0.0.1:2112", + }, + Pprof: codersdk.PprofConfig{ + // Serve pprof metrics on the address defined by `pprof.address`. + Enable: false, + // The bind address to serve pprof. + Address: "127.0.0.1:6060", + }, + // The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. + CacheDir: defaultCacheDir(), + // Controls whether data will be stored in an in-memory database. + InMemoryDatabase: false, + // Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. + ProvisionerDaemonCount: 3, + // 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". + PostgresURL: "", + Oauth2Github: codersdk.Oauth2GithubConfig{ + // Client ID for Login with GitHub. + ClientID: "", + // Client secret for Login with GitHub. + ClientSecret: "", + // Organizations the user must be a member of to Login with GitHub. + AllowedOrganizations: []string{}, + // Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /. + AllowedTeams: []string{}, + // Whether new users can sign up with GitHub. + AllowSignups: true, + // Base URL of a GitHub Enterprise deployment to use for Login with GitHub. + EnterpriseBaseURL: "", + }, + + OIDC: codersdk.OIDCConfig{ + // Whether new users can sign up with OIDC. + AllowSignups: true, + // Client ID to use for Login with OIDC. + ClientID: "", + // Client secret to use for Login with OIDC. + ClientSecret: "", + // Email domain that clients logging in with OIDC must match. + EmailDomain: "", + // Issuer URL to use for Login with OIDC. + IssuerURL: "", + // Scopes to grant when authenticating with OIDC. + Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, + }, + Telemetry: codersdk.TelemetryConfig{ + // Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product. + Enable: flag.Lookup("test.v") == nil, + // Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option. + TraceEnable: flag.Lookup("test.v") == nil, + // URL to send telemetry. + URL: "https://telemetry.coder.com", + }, + TLSConfig: codersdk.TLSConfig{ + // Whether TLS will be enabled. + Enable: false, + // Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file. + CertFiles: []string{}, + // PEM-encoded Certificate Authority file used for checking the authenticity of client + ClientCAFile: "", + // Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". + ClientAuth: "request", + // Paths to the private keys for each of the certificates. It requires a PEM-encoded file. + KeyFiles: []string{}, + // Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" + MinVersion: "tls12", + }, + // Whether application tracing data is collected. + TraceEnable: false, + // Controls if the 'Secure' property is set on browser session cookies. + SecureAuthCookie: false, + // The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". + SSHKeygenAlgorithm: "ed25519", + // Templates to auto-import. Available auto-importable templates are: kubernetes + AutoImportTemplates: []string{}, + // How frequently metrics are refreshed + MetricsCacheRefreshInterval: time.Hour, + // How frequently agent stats are recorded + AgentStatRefreshInterval: 10 * time.Minute, + // Enables verbose logging. + Verbose: false, + // Specifies whether audit logging is enabled. + AuditLogging: true, + // Whether Coder only allows connections to workspaces via the browser. + BrowserOnly: false, + // Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. + SCIMAuthHeader: "", + // Enables and sets a limit on how many workspaces each user can create. + UserWorkspaceQuota: 0, + } +} diff --git a/cli/deployment/flags.go b/cli/deployment/flags.go index 792051f805f70..0b5baf3028552 100644 --- a/cli/deployment/flags.go +++ b/cli/deployment/flags.go @@ -22,19 +22,19 @@ const ( func Flags() *codersdk.DeploymentFlags { return &codersdk.DeploymentFlags{ - AccessURL: &codersdk.StringFlag{ + AccessURL: &codersdk.Flag[string]{ 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.Flag[string]{ 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" or "*-suffix.example.com". Ports or schemes should not be included. The scheme will be copied from the access URL.`, }, - Address: &codersdk.StringFlag{ + Address: &codersdk.Flag[string]{ Name: "Bind Address", Flag: "address", EnvVar: "CODER_ADDRESS", @@ -42,7 +42,7 @@ func Flags() *codersdk.DeploymentFlags { Description: "Bind address of the server.", Default: "127.0.0.1:3000", }, - AutobuildPollInterval: &codersdk.DurationFlag{ + AutobuildPollInterval: &codersdk.Flag[time.Duration]{ Name: "Autobuild Poll Interval", Flag: "autobuild-poll-interval", EnvVar: "CODER_AUTOBUILD_POLL_INTERVAL", @@ -50,35 +50,35 @@ func Flags() *codersdk.DeploymentFlags { Hidden: true, Default: time.Minute, }, - DerpServerEnable: &codersdk.BoolFlag{ + DerpServerEnable: &codersdk.Flag[bool]{ 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.Flag[int]{ 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.Flag[string]{ 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.Flag[string]{ 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.Flag[[]string]{ Name: "DERP Server STUN Addresses", Flag: "derp-server-stun-addresses", EnvVar: "CODER_DERP_SERVER_STUN_ADDRESSES", @@ -98,152 +98,152 @@ func Flags() *codersdk.DeploymentFlags { 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.Flag[string]{ 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.Flag[bool]{ 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.Flag[string]{ 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.Flag[bool]{ 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.Flag[string]{ 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.Flag[string]{ 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.Flag[bool]{ 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.Flag[int]{ 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.Flag[string]{ 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.Flag[string]{ 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.Flag[string]{ 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.Flag[[]string]{ 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.Flag[[]string]{ 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.Flag[bool]{ 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.Flag[string]{ 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.Flag[bool]{ 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.Flag[string]{ 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.Flag[string]{ 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.Flag[string]{ 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.Flag[string]{ 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.Flag[[]string]{ 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.Flag[bool]{ 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.Flag[bool]{ Name: "Trace Telemetry Enabled", Flag: "telemetry-trace", EnvVar: "CODER_TELEMETRY_TRACE", @@ -251,7 +251,7 @@ 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.Flag[string]{ Name: "Telemetry URL", Flag: "telemetry-url", EnvVar: "CODER_TELEMETRY_URL", @@ -259,13 +259,13 @@ func Flags() *codersdk.DeploymentFlags { Hidden: true, Default: "https://telemetry.coder.com", }, - TLSEnable: &codersdk.BoolFlag{ + TLSEnable: &codersdk.Flag[bool]{ Name: "TLS Enabled", Flag: "tls-enable", EnvVar: "CODER_TLS_ENABLE", Description: "Whether TLS will be enabled.", }, - TLSCertFiles: &codersdk.StringArrayFlag{ + TLSCertFiles: &codersdk.Flag[[]string]{ Name: "TLS Cert Files", Flag: "tls-cert-file", EnvVar: "CODER_TLS_CERT_FILE", @@ -274,13 +274,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.Flag[string]{ 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.Flag[string]{ Name: "TLS Client Auth", Flag: "tls-client-auth", EnvVar: "CODER_TLS_CLIENT_AUTH", @@ -288,33 +288,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.Flag[[]string]{ 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.Flag[string]{ 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.Flag[bool]{ Name: "Trace Enabled", Flag: "trace", EnvVar: "CODER_TRACE", Description: "Whether application tracing data is collected.", }, - SecureAuthCookie: &codersdk.BoolFlag{ + SecureAuthCookie: &codersdk.Flag[bool]{ 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.Flag[string]{ Name: "SSH Keygen Algorithm", Flag: "ssh-keygen-algorithm", EnvVar: "CODER_SSH_KEYGEN_ALGORITHM", @@ -322,7 +322,7 @@ func Flags() *codersdk.DeploymentFlags { `Accepted values are "ed25519", "ecdsa", or "rsa4096"`, Default: "ed25519", }, - AutoImportTemplates: &codersdk.StringArrayFlag{ + AutoImportTemplates: &codersdk.Flag[[]string]{ Name: "Auto Import Templates", Flag: "auto-import-template", EnvVar: "CODER_TEMPLATE_AUTOIMPORT", @@ -330,7 +330,7 @@ func Flags() *codersdk.DeploymentFlags { Hidden: true, Default: []string{}, }, - MetricsCacheRefreshInterval: &codersdk.DurationFlag{ + MetricsCacheRefreshInterval: &codersdk.Flag[time.Duration]{ Name: "Metrics Cache Refresh Interval", Flag: "metrics-cache-refresh-interval", EnvVar: "CODER_METRICS_CACHE_REFRESH_INTERVAL", @@ -338,7 +338,7 @@ func Flags() *codersdk.DeploymentFlags { Hidden: true, Default: time.Hour, }, - AgentStatRefreshInterval: &codersdk.DurationFlag{ + AgentStatRefreshInterval: &codersdk.Flag[time.Duration]{ Name: "Agent Stats Refresh Interval", Flag: "agent-stats-refresh-interval", EnvVar: "CODER_AGENT_STATS_REFRESH_INTERVAL", @@ -346,14 +346,14 @@ func Flags() *codersdk.DeploymentFlags { Hidden: true, Default: 10 * time.Minute, }, - Verbose: &codersdk.BoolFlag{ + Verbose: &codersdk.Flag[bool]{ Name: "Verbose Logging", Flag: "verbose", EnvVar: "CODER_VERBOSE", Shorthand: "v", Description: "Enables verbose logging.", }, - AuditLogging: &codersdk.BoolFlag{ + AuditLogging: &codersdk.Flag[bool]{ Name: "Audit Logging", Flag: "audit-logging", EnvVar: "CODER_AUDIT_LOGGING", @@ -361,14 +361,14 @@ func Flags() *codersdk.DeploymentFlags { Default: true, Enterprise: true, }, - BrowserOnly: &codersdk.BoolFlag{ + BrowserOnly: &codersdk.Flag[bool]{ 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.Flag[string]{ Name: "SCIM Authentication Header", Flag: "scim-auth-header", EnvVar: "CODER_SCIM_API_KEY", @@ -376,7 +376,7 @@ func Flags() *codersdk.DeploymentFlags { Secret: true, Enterprise: true, }, - UserWorkspaceQuota: &codersdk.IntFlag{ + UserWorkspaceQuota: &codersdk.Flag[int]{ Name: "User Workspace Quota", Flag: "user-workspace-quota", EnvVar: "CODER_USER_WORKSPACE_QUOTA", @@ -392,7 +392,7 @@ func RemoveSensitiveValues(df codersdk.DeploymentFlags) codersdk.DeploymentFlags t := v.Type() for i := 0; i < t.NumField(); i++ { fv := v.Field(i) - if vp, ok := fv.Interface().(*codersdk.StringFlag); ok { + if vp, ok := fv.Interface().(*codersdk.Flag[string]); ok { if vp.Secret && vp.Value != "" { // Make a copy and remove the value. v := *vp @@ -423,15 +423,15 @@ func AttachFlags(flagset *pflag.FlagSet, df *codersdk.DeploymentFlags, enterpris } switch v := fv.Interface().(type) { - case *codersdk.StringFlag: + case *codersdk.Flag[string]: StringFlag(flagset, v) - case *codersdk.StringArrayFlag: + case *codersdk.Flag[[]string]: StringArrayFlag(flagset, v) - case *codersdk.IntFlag: + case *codersdk.Flag[int]: IntFlag(flagset, v) - case *codersdk.BoolFlag: + case *codersdk.Flag[bool]: BoolFlag(flagset, v) - case *codersdk.DurationFlag: + case *codersdk.Flag[time.Duration]: DurationFlag(flagset, v) default: panic(fmt.Sprintf("unknown flag type: %T", v)) @@ -442,7 +442,7 @@ func AttachFlags(flagset *pflag.FlagSet, df *codersdk.DeploymentFlags, enterpris } } -func StringFlag(flagset *pflag.FlagSet, fl *codersdk.StringFlag) { +func StringFlag(flagset *pflag.FlagSet, fl *codersdk.Flag[string]) { cliflag.StringVarP(flagset, &fl.Value, fl.Flag, @@ -453,7 +453,7 @@ func StringFlag(flagset *pflag.FlagSet, fl *codersdk.StringFlag) { ) } -func BoolFlag(flagset *pflag.FlagSet, fl *codersdk.BoolFlag) { +func BoolFlag(flagset *pflag.FlagSet, fl *codersdk.Flag[bool]) { cliflag.BoolVarP(flagset, &fl.Value, fl.Flag, @@ -464,7 +464,7 @@ func BoolFlag(flagset *pflag.FlagSet, fl *codersdk.BoolFlag) { ) } -func IntFlag(flagset *pflag.FlagSet, fl *codersdk.IntFlag) { +func IntFlag(flagset *pflag.FlagSet, fl *codersdk.Flag[int]) { cliflag.IntVarP(flagset, &fl.Value, fl.Flag, @@ -475,7 +475,7 @@ func IntFlag(flagset *pflag.FlagSet, fl *codersdk.IntFlag) { ) } -func DurationFlag(flagset *pflag.FlagSet, fl *codersdk.DurationFlag) { +func DurationFlag(flagset *pflag.FlagSet, fl *codersdk.Flag[time.Duration]) { cliflag.DurationVarP(flagset, &fl.Value, fl.Flag, @@ -486,7 +486,7 @@ func DurationFlag(flagset *pflag.FlagSet, fl *codersdk.DurationFlag) { ) } -func StringArrayFlag(flagset *pflag.FlagSet, fl *codersdk.StringArrayFlag) { +func StringArrayFlag(flagset *pflag.FlagSet, fl *codersdk.Flag[[]string]) { cliflag.StringArrayVarP(flagset, &fl.Value, fl.Flag, diff --git a/codersdk/config.go b/codersdk/config.go new file mode 100644 index 0000000000000..cbde81ab21496 --- /dev/null +++ b/codersdk/config.go @@ -0,0 +1,241 @@ +package codersdk + +import ( + "context" + "encoding/json" + "net/http" + "time" + + "golang.org/x/xerrors" +) + +type DeploymentConfig struct { + // External URL to access your deployment. This must be accessible by all provisioned workspaces. + AccessURL string `json:"access_url"` + // Specifies the wildcard hostname to use for workspace applications in the form "*.example.com". + WildcardAccessURL string `json:"wildcard_access_url"` + // Bind address of the server. + Address string `json:"address"` + // Interval to poll for scheduled workspace builds. + AutobuildPollInterval time.Duration `json:"autobuild_poll_interval"` + DERP DERPConfig `json:"derp"` + Prometheus PrometheusConfig `json:"prometheus"` + Pprof PprofConfig `json:"pprof"` + // The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. + CacheDir string `json:"cache_dir"` + // Controls whether data will be stored in an in-memory database. + InMemoryDatabase bool `json:"in_memory_database"` + // Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. + ProvisionerDaemonCount int `json:"provisioner_daemon_count"` + // 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". + PostgresURL string `json:"postgres_url"` + Oauth2Github Oauth2GithubConfig `json:"oauth2_github"` + OIDC OIDCConfig `json:"oidc"` + Telemetry TelemetryConfig `json:"telemetry"` + TLSConfig TLSConfig `json:"tls_config"` + // Whether application tracing data is collected. + TraceEnable bool `json:"trace_enable"` + // Controls if the 'Secure' property is set on browser session cookies. + SecureAuthCookie bool `json:"secure_auth_cookie"` + // The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". + SSHKeygenAlgorithm string `json:"ssh_keygen_algorithm"` + // Templates to auto-import. Available auto-importable templates are: kubernetes + AutoImportTemplates []string `json:"auto_import_templates"` + // How frequently metrics are refreshed + MetricsCacheRefreshInterval time.Duration `json:"metrics_cache_refresh_interval"` + // How frequently agent stats are recorded + AgentStatRefreshInterval time.Duration `json:"agent_stat_refresh_interval"` + // Enables verbose logging. + Verbose bool `json:"verbose"` + // Specifies whether audit logging is enabled. + AuditLogging bool `json:"audit_logging"` + // Whether Coder only allows connections to workspaces via the browser. + BrowserOnly bool `json:"browser_only"` + // Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. + SCIMAuthHeader string `json:"scim_auth_header"` + // Enables and sets a limit on how many workspaces each user can create. + UserWorkspaceQuota int `json:"user_workspace_quota"` +} + +type DERPConfig struct { + Server DERPServerConfig `json:"server"` + Config DERPConfigConfig `json:"config"` +} +type DERPServerConfig struct { + // Whether to enable or disable the embedded DERP relay server. + Enable bool `json:"enabled"` + // Region ID to use for the embedded DERP server. + RegionID int `json:"region_id"` + // Region code to use for the embedded DERP server. + RegionCode string `json:"region_code"` + // Region name that for the embedded DERP server. + RegionName string `json:"region_name"` + // Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections. + STUNAddresses []string `json:"stun_address"` +} + +type DERPConfigConfig struct { + // URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/ + URL string `json:"url"` + // Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/ + Path string `json:"path"` +} + +type PrometheusConfig struct { + // Serve prometheus metrics on the address defined by prometheus address. + Enable bool `json:"enabled"` + // The bind address to serve prometheus metrics. + Address string `json:"address"` +} + +type PprofConfig struct { + // Serve pprof metrics on the address defined by pprof address. + Enable bool `json:"enabled"` + // The bind address to serve pprof. + Address string `json:"address"` +} + +type Oauth2GithubConfig struct { + // Client ID for Login with GitHub. + ClientID string `json:"client_id"` + // Client secret for Login with GitHub. + ClientSecret string `json:"client_secret"` + // Organizations the user must be a member of to Login with GitHub. + AllowedOrganizations []string `json:"allowed_organizations"` + // Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /. + AllowedTeams []string `json:"allowed_teams"` + // Whether new users can sign up with GitHub. + AllowSignups bool `json:"allow_signups"` + // Base URL of a GitHub Enterprise deployment to use for Login with GitHub. + EnterpriseBaseURL string `json:"enterprise_base_url"` +} + +type OIDCConfig struct { + // Whether new users can sign up with OIDC. + AllowSignups bool `json:"allow_signups"` + // Client ID to use for Login with OIDC. + ClientID string `json:"client_id"` + // Client secret to use for Login with OIDC. + ClientSecret string `json:"cliet_secret"` + // Email domain that clients logging in with OIDC must match. + EmailDomain string `json:"email_domain"` + // Issuer URL to use for Login with OIDC. + IssuerURL string `json:"issuer_url"` + // Scopes to grant when authenticating with OIDC. + Scopes []string `json:"scopes"` +} + +type TelemetryConfig struct { + // Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product. + Enable bool `json:"enable"` + // Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option. + TraceEnable bool `json:"trace_enable"` + // URL to send telemetry. + URL string `json:"url"` +} + +type TLSConfig struct { + // Whether TLS will be enabled. + Enable bool `json:"tls_enable"` + // Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file. + CertFiles []string `json:"tls_cert_files"` + // PEM-encoded Certificate Authority file used for checking the authenticity of client + ClientCAFile string `json:"tls_client_ca_file"` + // Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". + ClientAuth string `json:"tls_client_auth"` + // Paths to the private keys for each of the certificates. It requires a PEM-encoded file. + KeyFiles []string `json:"tls_key_tiles"` + // Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" + MinVersion string `json:"tls_min_version"` +} + +type DeploymentFlags struct { + AccessURL *Flag[string] `json:"access_url"` + WildcardAccessURL *Flag[string] `json:"wildcard_access_url"` + Address *Flag[string] `json:"address"` + AutobuildPollInterval *Flag[time.Duration] `json:"autobuild_poll_interval"` + DerpServerEnable *Flag[bool] `json:"enabled"` + DerpServerRegionID *Flag[int] `json:"region_id"` + DerpServerRegionCode *Flag[string] `json:"region_code"` + DerpServerRegionName *Flag[string] `json:"region_name"` + DerpServerSTUNAddresses *Flag[[]string] `json:"stun_address"` + DerpConfigURL *Flag[string] `json:"derp_config_url"` + DerpConfigPath *Flag[string] `json:"derp_config_path"` + PromEnabled *Flag[bool] `json:"prom_enabled"` + PromAddress *Flag[string] `json:"prom_address"` + PprofEnabled *Flag[bool] `json:"pprof_enabled"` + PprofAddress *Flag[string] `json:"pprof_address"` + CacheDir *Flag[string] `json:"cache_dir"` + InMemoryDatabase *Flag[bool] `json:"in_memory_database"` + ProvisionerDaemonCount *Flag[int] `json:"provisioner_daemon_count"` + PostgresURL *Flag[string] `json:"postgres_url"` + OAuth2GithubClientID *Flag[string] `json:"client_id"` + OAuth2GithubClientSecret *Flag[string] `json:"client_secret"` + OAuth2GithubAllowedOrganizations *Flag[[]string] `json:"allowed_organizations"` + OAuth2GithubAllowedTeams *Flag[[]string] `json:"allowed_teams"` + OAuth2GithubAllowSignups *Flag[bool] `json:"allow_signups"` + OAuth2GithubEnterpriseBaseURL *Flag[string] `json:"enterprise_base_url"` + OIDCAllowSignups *Flag[bool] `json:"allow_signups"` + OIDCClientID *Flag[string] `json:"client_id"` + OIDCClientSecret *Flag[string] `json:"cliet_secret"` + OIDCEmailDomain *Flag[string] `json:"email_domain"` + OIDCIssuerURL *Flag[string] `json:"issuer_url"` + OIDCScopes *Flag[[]string] `json:"scopes"` + TelemetryEnable *Flag[bool] `json:"telemetry_enable"` + TelemetryTraceEnable *Flag[bool] `json:"telemetry_trace_enable"` + TelemetryURL *Flag[string] `json:"telemetry_url"` + TLSEnable *Flag[bool] `json:"tls_enable"` + TLSCertFiles *Flag[[]string] `json:"tls_cert_files"` + TLSClientCAFile *Flag[string] `json:"tls_client_ca_file"` + TLSClientAuth *Flag[string] `json:"tls_client_auth"` + TLSKeyFiles *Flag[[]string] `json:"tls_key_tiles"` + TLSMinVersion *Flag[string] `json:"tls_min_version"` + TraceEnable *Flag[bool] `json:"trace_enable"` + SecureAuthCookie *Flag[bool] `json:"secure_auth_cookie"` + SSHKeygenAlgorithm *Flag[string] `json:"ssh_keygen_algorithm"` + AutoImportTemplates *Flag[[]string] `json:"auto_import_templates"` + MetricsCacheRefreshInterval *Flag[time.Duration] `json:"metrics_cache_refresh_interval"` + AgentStatRefreshInterval *Flag[time.Duration] `json:"agent_stat_refresh_interval"` + Verbose *Flag[bool] `json:"verbose"` + AuditLogging *Flag[bool] `json:"audit_logging"` + BrowserOnly *Flag[bool] `json:"browser_only"` + SCIMAuthHeader *Flag[string] `json:"scim_auth_header"` + UserWorkspaceQuota *Flag[int] `json:"user_workspace_quota"` +} + +type Flaggable interface { + string | int | bool | time.Duration | []string +} + +type Flag[T Flaggable] struct { + Name string `json:"name"` + Flag string `json:"flag"` + EnvVar string `json:"env_var"` + Shorthand string `json:"shorthand"` + Description string `json:"description"` + Enterprise bool `json:"enterprise"` + Hidden bool `json:"hidden"` + Secret bool `json:"secret"` + Default T `json:"default"` + Value T `json:"value"` +} + +func (f *Flag[T]) IsEnterprise() bool { + return f.Enterprise +} + +// DeploymentFlags returns the deployment level flags for the coder server. +func (c *Client) DeploymentFlags(ctx context.Context) (DeploymentFlags, error) { + res, err := c.Request(ctx, http.MethodGet, "/api/v2/flags/deployment", nil) + if err != nil { + return DeploymentFlags{}, xerrors.Errorf("execute request: %w", err) + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return DeploymentFlags{}, readBodyAsError(res) + } + + var df DeploymentFlags + return df, json.NewDecoder(res.Body).Decode(&df) +} From bf152f35aa5f8eb86f8a40cc06634351bb417099 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 14 Oct 2022 20:39:26 +0000 Subject: [PATCH 02/43] idk --- cli/deployment/config.go | 148 ++---------- cli/deployment/flags_test.go | 32 --- cli/root.go | 2 +- cli/server.go | 165 +++++++------- coderd/coderd.go | 4 +- coderd/coderdtest/coderdtest.go | 11 +- coderd/flags.go | 24 +- coderd/flags_test.go | 69 +++--- codersdk/config.go | 393 ++++++++++++++++---------------- enterprise/cli/server.go | 19 +- go.mod | 11 +- go.sum | 14 ++ server.yaml | 0 13 files changed, 392 insertions(+), 500 deletions(-) delete mode 100644 cli/deployment/flags_test.go create mode 100644 server.yaml diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 735ee034e07b6..6ec3942bca08c 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -1,136 +1,30 @@ package deployment import ( - "flag" - "time" - - "github.com/coreos/go-oidc/v3/oidc" + "github.com/spf13/pflag" + "github.com/spf13/viper" "github.com/coder/coder/codersdk" ) -func Config() codersdk.DeploymentConfig { - return codersdk.DeploymentConfig{ - // External URL to access your deployment. This must be accessible by all provisioned workspaces. - AccessURL: "", - // Specifies the wildcard hostname to use for workspace applications in the form "*.example.com". - WildcardAccessURL: "", - // Bind address of the server. - Address: "127.0.0.1:3000", - // Interval to poll for scheduled workspace builds. - AutobuildPollInterval: time.Minute, - DERP: codersdk.DERPConfig{ - Server: codersdk.DERPServerConfig{ - // Whether to enable or disable the embedded DERP relay server. - Enable: true, - // Region ID to use for the embedded DERP server. - RegionID: 999, - // Region code to use for the embedded DERP server. - RegionCode: "coder", - // Region name that for the embedded DERP server. - RegionName: "Coder Embedded Relay", - // Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections. - STUNAddresses: []string{"stun.l.google.com:19302"}, - }, - Config: codersdk.DERPConfigConfig{ - // URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/ - URL: "", - // Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/ - Path: "", - }, - }, - Prometheus: codersdk.PrometheusConfig{ - // Serve prometheus metrics on the address defined by `prometheus.address`. - Enable: false, - // The bind address to serve prometheus metrics. - Address: "127.0.0.1:2112", - }, - Pprof: codersdk.PprofConfig{ - // Serve pprof metrics on the address defined by `pprof.address`. - Enable: false, - // The bind address to serve pprof. - Address: "127.0.0.1:6060", - }, - // The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. - CacheDir: defaultCacheDir(), - // Controls whether data will be stored in an in-memory database. - InMemoryDatabase: false, - // Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. - ProvisionerDaemonCount: 3, - // 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". - PostgresURL: "", - Oauth2Github: codersdk.Oauth2GithubConfig{ - // Client ID for Login with GitHub. - ClientID: "", - // Client secret for Login with GitHub. - ClientSecret: "", - // Organizations the user must be a member of to Login with GitHub. - AllowedOrganizations: []string{}, - // Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /. - AllowedTeams: []string{}, - // Whether new users can sign up with GitHub. - AllowSignups: true, - // Base URL of a GitHub Enterprise deployment to use for Login with GitHub. - EnterpriseBaseURL: "", - }, +func DefaultViper() *viper.Viper { + v := viper.New() + v.SetDefault("access_url", "") + + return v +} + +func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { + _ = flagset.StringP("access-url", "", vip.GetString("access-url"), "usage") + _ = vip.BindPFlag("access-url", flagset.Lookup("access-url")) +} + +func AttachEnterpriseFlags(flagset *pflag.FlagSet, vip *viper.Viper) { + _ = flagset.StringP("access-url", "", vip.GetString("access-url"), "usage") + _ = vip.BindPFlag("access-url", flagset.Lookup("access-url")) +} - OIDC: codersdk.OIDCConfig{ - // Whether new users can sign up with OIDC. - AllowSignups: true, - // Client ID to use for Login with OIDC. - ClientID: "", - // Client secret to use for Login with OIDC. - ClientSecret: "", - // Email domain that clients logging in with OIDC must match. - EmailDomain: "", - // Issuer URL to use for Login with OIDC. - IssuerURL: "", - // Scopes to grant when authenticating with OIDC. - Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, - }, - Telemetry: codersdk.TelemetryConfig{ - // Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product. - Enable: flag.Lookup("test.v") == nil, - // Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option. - TraceEnable: flag.Lookup("test.v") == nil, - // URL to send telemetry. - URL: "https://telemetry.coder.com", - }, - TLSConfig: codersdk.TLSConfig{ - // Whether TLS will be enabled. - Enable: false, - // Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file. - CertFiles: []string{}, - // PEM-encoded Certificate Authority file used for checking the authenticity of client - ClientCAFile: "", - // Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". - ClientAuth: "request", - // Paths to the private keys for each of the certificates. It requires a PEM-encoded file. - KeyFiles: []string{}, - // Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" - MinVersion: "tls12", - }, - // Whether application tracing data is collected. - TraceEnable: false, - // Controls if the 'Secure' property is set on browser session cookies. - SecureAuthCookie: false, - // The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". - SSHKeygenAlgorithm: "ed25519", - // Templates to auto-import. Available auto-importable templates are: kubernetes - AutoImportTemplates: []string{}, - // How frequently metrics are refreshed - MetricsCacheRefreshInterval: time.Hour, - // How frequently agent stats are recorded - AgentStatRefreshInterval: 10 * time.Minute, - // Enables verbose logging. - Verbose: false, - // Specifies whether audit logging is enabled. - AuditLogging: true, - // Whether Coder only allows connections to workspaces via the browser. - BrowserOnly: false, - // Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. - SCIMAuthHeader: "", - // Enables and sets a limit on how many workspaces each user can create. - UserWorkspaceQuota: 0, - } +func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { + cfg := codersdk.DeploymentConfig{} + return cfg, vip.Unmarshal(cfg) } diff --git a/cli/deployment/flags_test.go b/cli/deployment/flags_test.go deleted file mode 100644 index 8b411068dba28..0000000000000 --- a/cli/deployment/flags_test.go +++ /dev/null @@ -1,32 +0,0 @@ -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 ea803cdfa5d81..8bddf8c4bd6fd 100644 --- a/cli/root.go +++ b/cli/root.go @@ -101,7 +101,7 @@ func Core() []*cobra.Command { } func AGPL() []*cobra.Command { - all := append(Core(), Server(deployment.Flags(), func(_ context.Context, o *coderd.Options) (*coderd.API, io.Closer, error) { + all := append(Core(), Server(deployment.DefaultViper(), func(_ context.Context, o *coderd.Options) (*coderd.API, io.Closer, error) { api := coderd.New(o) return api, api, nil })) diff --git a/cli/server.go b/cli/server.go index 99f23e9278315..9c1f38af7b23b 100644 --- a/cli/server.go +++ b/cli/server.go @@ -32,6 +32,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/afero" "github.com/spf13/cobra" + "github.com/spf13/viper" "go.opentelemetry.io/otel/trace" "golang.org/x/mod/semver" "golang.org/x/oauth2" @@ -70,14 +71,18 @@ import ( ) // nolint:gocyclo -func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Closer, error)) *cobra.Command { +func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Closer, error)) *cobra.Command { root := &cobra.Command{ Use: "server", Short: "Start a Coder server", RunE: func(cmd *cobra.Command, args []string) error { + cfg, err := deployment.Config(vip) + if err != nil { + return xerrors.Errorf("failed to read config: %w", err) + } printLogo(cmd) logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) - if dflags.Verbose.Value { + if cfg.Verbose { logger = logger.Leveled(slog.LevelDebug) } @@ -106,22 +111,21 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code var ( tracerProvider trace.TracerProvider - err error sqlDriver = "postgres" ) // Coder tracing should be disabled if telemetry is disabled unless // --telemetry-trace was explicitly provided. - shouldCoderTrace := dflags.TelemetryEnable.Value && !isTest() + shouldCoderTrace := cfg.Telemetry.Enable && !isTest() // Only override if telemetryTraceEnable was specifically set. // By default we want it to be controlled by telemetryEnable. if cmd.Flags().Changed("telemetry-trace") { - shouldCoderTrace = dflags.TelemetryTraceEnable.Value + shouldCoderTrace = cfg.Telemetry.TraceEnable } - if dflags.TraceEnable.Value || shouldCoderTrace { + if cfg.TraceEnable || shouldCoderTrace { sdkTracerProvider, closeTracing, err := tracing.TracerProvider(ctx, "coderd", tracing.TracerOpts{ - Default: dflags.TraceEnable.Value, + Default: cfg.TraceEnable, Coder: shouldCoderTrace, }) if err != nil { @@ -146,10 +150,10 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code config := createConfig(cmd) builtinPostgres := false // Only use built-in if PostgreSQL URL isn't specified! - if !dflags.InMemoryDatabase.Value && dflags.PostgresURL.Value == "" { + if !cfg.InMemoryDatabase && cfg.PostgresURL == "" { var closeFunc func() error cmd.Printf("Using built-in PostgreSQL (%s)\n", config.PostgresPath()) - dflags.PostgresURL.Value, closeFunc, err = startBuiltinPostgres(ctx, config, logger) + cfg.PostgresURL, closeFunc, err = startBuiltinPostgres(ctx, config, logger) if err != nil { return err } @@ -162,20 +166,19 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code }() } - listener, err := net.Listen("tcp", dflags.Address.Value) + listener, err := net.Listen("tcp", cfg.Address) if err != nil { - return xerrors.Errorf("listen %q: %w", dflags.Address.Value, err) + return xerrors.Errorf("listen %q: %w", cfg.Address, err) } defer listener.Close() - var tlsConfig *tls.Config - if dflags.TLSEnable.Value { + if cfg.TLS.Enable { tlsConfig, err = configureTLS( - dflags.TLSMinVersion.Value, - dflags.TLSClientAuth.Value, - dflags.TLSCertFiles.Value, - dflags.TLSKeyFiles.Value, - dflags.TLSClientCAFile.Value, + cfg.TLS.MinVersion, + cfg.TLS.ClientAuth, + cfg.TLS.CertFiles, + cfg.TLS.KeyFiles, + cfg.TLS.ClientCAFile, ) if err != nil { return xerrors.Errorf("configure tls: %w", err) @@ -197,7 +200,7 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code Scheme: "http", Host: tcpAddr.String(), } - if dflags.TLSEnable.Value { + if cfg.TLS.Enable { localURL.Scheme = "https" } @@ -229,7 +232,7 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code } } - accessURLParsed, err := parseURL(ctx, dflags.AccessURL.Value) + accessURLParsed, err := parseURL(ctx, cfg.AccessURL) if err != nil { return xerrors.Errorf("parse URL: %w", err) } @@ -264,17 +267,17 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code return err } - sshKeygenAlgorithm, err := gitsshkey.ParseAlgorithm(dflags.SSHKeygenAlgorithm.Value) + sshKeygenAlgorithm, err := gitsshkey.ParseAlgorithm(cfg.SSHKeygenAlgorithm) if err != nil { - return xerrors.Errorf("parse ssh keygen algorithm %s: %w", dflags.SSHKeygenAlgorithm.Value, err) + return xerrors.Errorf("parse ssh keygen algorithm %s: %w", cfg.SSHKeygenAlgorithm, err) } // Validate provided auto-import templates. var ( - validatedAutoImportTemplates = make([]coderd.AutoImportTemplate, len(dflags.AutoImportTemplates.Value)) - seenValidatedAutoImportTemplates = make(map[coderd.AutoImportTemplate]struct{}, len(dflags.AutoImportTemplates.Value)) + validatedAutoImportTemplates = make([]coderd.AutoImportTemplate, len(cfg.AutoImportTemplates)) + seenValidatedAutoImportTemplates = make(map[coderd.AutoImportTemplate]struct{}, len(cfg.AutoImportTemplates)) ) - for i, autoImportTemplate := range dflags.AutoImportTemplates.Value { + for i, autoImportTemplate := range cfg.AutoImportTemplates { var v coderd.AutoImportTemplate switch autoImportTemplate { case "kubernetes": @@ -292,27 +295,27 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code defaultRegion := &tailcfg.DERPRegion{ EmbeddedRelay: true, - RegionID: dflags.DerpServerRegionID.Value, - RegionCode: dflags.DerpServerRegionCode.Value, - RegionName: dflags.DerpServerRegionName.Value, + RegionID: cfg.DERP.Server.RegionID, + RegionCode: cfg.DERP.Server.RegionCode, + RegionName: cfg.DERP.Server.RegionName, Nodes: []*tailcfg.DERPNode{{ - Name: fmt.Sprintf("%db", dflags.DerpServerRegionID.Value), - RegionID: dflags.DerpServerRegionID.Value, + Name: fmt.Sprintf("%db", cfg.DERP.Server.RegionID), + RegionID: cfg.DERP.Server.RegionID, HostName: accessURLParsed.Hostname(), DERPPort: accessURLPort, STUNPort: -1, ForceHTTP: accessURLParsed.Scheme == "http", }}, } - if !dflags.DerpServerEnable.Value { + if !cfg.DERP.Server.Enable { defaultRegion = nil } - derpMap, err := tailnet.NewDERPMap(ctx, defaultRegion, dflags.DerpServerSTUNAddresses.Value, dflags.DerpConfigURL.Value, dflags.DerpConfigPath.Value) + derpMap, err := tailnet.NewDERPMap(ctx, defaultRegion, cfg.DERP.Server.STUNAddresses, cfg.DERP.Config.URL, cfg.DERP.Config.Path) if err != nil { return xerrors.Errorf("create derp map: %w", err) } - appHostname := strings.TrimSpace(dflags.WildcardAccessURL.Value) + appHostname := strings.TrimSpace(cfg.WildcardAccessURL.Value) var appHostnameRegex *regexp.Regexp if appHostname != "" { appHostnameRegex, err = httpapi.CompileHostnamePattern(appHostname) @@ -329,45 +332,45 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code Database: databasefake.New(), DERPMap: derpMap, Pubsub: database.NewPubsubInMemory(), - CacheDir: dflags.CacheDir.Value, + CacheDir: cfg.CacheDir, GoogleTokenValidator: googleTokenValidator, - SecureAuthCookie: dflags.SecureAuthCookie.Value, + SecureAuthCookie: cfg.SecureAuthCookie, SSHKeygenAlgorithm: sshKeygenAlgorithm, TracerProvider: tracerProvider, Telemetry: telemetry.NewNoop(), AutoImportTemplates: validatedAutoImportTemplates, - MetricsCacheRefreshInterval: dflags.MetricsCacheRefreshInterval.Value, - AgentStatsRefreshInterval: dflags.AgentStatRefreshInterval.Value, + MetricsCacheRefreshInterval: cfg.MetricsCacheRefreshInterval, + AgentStatsRefreshInterval: cfg.AgentStatRefreshInterval, Experimental: ExperimentalEnabled(cmd), - DeploymentFlags: dflags, + DeploymentConfig: &cfg, } if tlsConfig != nil { options.TLSCertificates = tlsConfig.Certificates } - if dflags.OAuth2GithubClientSecret.Value != "" { + if cfg.OAuth2Github.ClientSecret != "" { options.GithubOAuth2Config, err = configureGithubOAuth2(accessURLParsed, - dflags.OAuth2GithubClientID.Value, - dflags.OAuth2GithubClientSecret.Value, - dflags.OAuth2GithubAllowSignups.Value, - dflags.OAuth2GithubAllowedOrganizations.Value, - dflags.OAuth2GithubAllowedTeams.Value, - dflags.OAuth2GithubEnterpriseBaseURL.Value, + cfg.OAuth2Github.ClientID, + cfg.OAuth2Github.ClientSecret, + cfg.OAuth2Github.AllowSignups, + cfg.OAuth2Github.AllowedOrganizations, + cfg.OAuth2Github.AllowedTeams, + cfg.OAuth2Github.EnterpriseBaseURL, ) if err != nil { return xerrors.Errorf("configure github oauth2: %w", err) } } - if dflags.OIDCClientSecret.Value != "" { - if dflags.OIDCClientID.Value == "" { + if cfg.OIDC.ClientSecret != "" { + if cfg.OIDC.ClientID == "" { return xerrors.Errorf("OIDC client ID be set!") } - if dflags.OIDCIssuerURL.Value == "" { + if cfg.OIDC.IssuerURL == "" { return xerrors.Errorf("OIDC issuer URL must be set!") } - oidcProvider, err := oidc.NewProvider(ctx, dflags.OIDCIssuerURL.Value) + oidcProvider, err := oidc.NewProvider(ctx, cfg.OIDC.IssuerURL) if err != nil { return xerrors.Errorf("configure oidc provider: %w", err) } @@ -377,25 +380,25 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code } options.OIDCConfig = &coderd.OIDCConfig{ OAuth2Config: &oauth2.Config{ - ClientID: dflags.OIDCClientID.Value, - ClientSecret: dflags.OIDCClientSecret.Value, + ClientID: cfg.OIDC.ClientID, + ClientSecret: cfg.OIDC.ClientSecret, RedirectURL: redirectURL.String(), Endpoint: oidcProvider.Endpoint(), - Scopes: dflags.OIDCScopes.Value, + Scopes: cfg.OIDC.Scopes, }, Verifier: oidcProvider.Verifier(&oidc.Config{ - ClientID: dflags.OIDCClientID.Value, + ClientID: cfg.OIDC.ClientID, }), - EmailDomain: dflags.OIDCEmailDomain.Value, - AllowSignups: dflags.OIDCAllowSignups.Value, + EmailDomain: cfg.OIDC.EmailDomain, + AllowSignups: cfg.OIDC.AllowSignups, } } - if dflags.InMemoryDatabase.Value { + if cfg.InMemoryDatabase { options.Database = databasefake.New() options.Pubsub = database.NewPubsubInMemory() } else { - sqlDB, err := sql.Open(sqlDriver, dflags.PostgresURL.Value) + sqlDB, err := sql.Open(sqlDriver, cfg.PostgresURL) if err != nil { return xerrors.Errorf("dial postgres: %w", err) } @@ -427,7 +430,7 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code return xerrors.Errorf("migrate up: %w", err) } options.Database = database.New(sqlDB) - options.Pubsub, err = database.NewPubsub(ctx, sqlDB, dflags.PostgresURL.Value) + options.Pubsub, err = database.NewPubsub(ctx, sqlDB, cfg.PostgresURL) if err != nil { return xerrors.Errorf("create pubsub: %w", err) } @@ -450,26 +453,26 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code } // Parse the raw telemetry URL! - telemetryURL, err := parseURL(ctx, dflags.TelemetryURL.Value) + telemetryURL, err := parseURL(ctx, cfg.Telemetry.URL) if err != nil { return xerrors.Errorf("parse telemetry url: %w", err) } // Disable telemetry if the in-memory database is used unless explicitly defined! - if dflags.InMemoryDatabase.Value && !cmd.Flags().Changed(dflags.TelemetryEnable.Flag) { - dflags.TelemetryEnable.Value = false + if cfg.InMemoryDatabase && !cmd.Flags().Changed("telemetry") { + cfg.Telemetry.Enable = false } - if dflags.TelemetryEnable.Value { + if cfg.Telemetry.Enable { options.Telemetry, err = telemetry.New(telemetry.Options{ BuiltinPostgres: builtinPostgres, DeploymentID: deploymentID, Database: options.Database, Logger: logger.Named("telemetry"), URL: telemetryURL, - GitHubOAuth: dflags.OAuth2GithubClientID.Value != "", - OIDCAuth: dflags.OIDCClientID.Value != "", - OIDCIssuerURL: dflags.OIDCIssuerURL.Value, - Prometheus: dflags.PromEnabled.Value, - STUN: len(dflags.DerpServerSTUNAddresses.Value) != 0, + GitHubOAuth: cfg.OAuth2Github.ClientID != "", + OIDCAuth: cfg.OIDC.ClientID != "", + OIDCIssuerURL: cfg.OIDC.IssuerURL, + Prometheus: cfg.Prometheus.Enable, + STUN: len(cfg.DERP.Server.STUNAddresses) != 0, Tunnel: tunnel != nil, }) if err != nil { @@ -480,11 +483,11 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code // This prevents the pprof import from being accidentally deleted. _ = pprof.Handler - if dflags.PprofEnabled.Value { + if cfg.Pprof.Enable { //nolint:revive - defer serveHandler(ctx, logger, nil, dflags.PprofAddress.Value, "pprof")() + defer serveHandler(ctx, logger, nil, cfg.Pprof.Address, "pprof")() } - if dflags.PromEnabled.Value { + if cfg.Prometheus.Enable { options.PrometheusRegistry = prometheus.NewRegistry() closeUsersFunc, err := prometheusmetrics.ActiveUsers(ctx, options.PrometheusRegistry, options.Database, 0) if err != nil { @@ -501,7 +504,7 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code //nolint:revive defer serveHandler(ctx, logger, promhttp.InstrumentMetricHandler( options.PrometheusRegistry, promhttp.HandlerFor(options.PrometheusRegistry, promhttp.HandlerOpts{}), - ), dflags.PromAddress.Value, "prometheus")() + ), cfg.Prometheus.Address, "prometheus")() } // We use a separate coderAPICloser so the Enterprise API @@ -513,7 +516,7 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code } client := codersdk.New(localURL) - if dflags.TLSEnable.Value { + if cfg.TLS.Enable { // Secure transport isn't needed for locally communicating! client.HTTPClient.Transport = &http.Transport{ TLSClientConfig: &tls.Config{ @@ -537,8 +540,8 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code _ = daemon.Close() } }() - for i := 0; i < dflags.ProvisionerDaemonCount.Value; i++ { - daemon, err := newProvisionerDaemon(ctx, coderAPI, logger, dflags.CacheDir.Value, errCh, false) + for i := 0; i < cfg.ProvisionerDaemonCount; i++ { + daemon, err := newProvisionerDaemon(ctx, coderAPI, logger, cfg.CacheDir, errCh, false) if err != nil { return xerrors.Errorf("create provisioner daemon: %w", err) } @@ -604,7 +607,7 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code return xerrors.Errorf("notify systemd: %w", err) } - autobuildPoller := time.NewTicker(dflags.AutobuildPollInterval.Value) + autobuildPoller := time.NewTicker(cfg.AutobuildPollInterval) defer autobuildPoller.Stop() autobuildExecutor := executor.New(ctx, options.Database, logger, autobuildPoller.C) autobuildExecutor.Run() @@ -669,7 +672,7 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code go func() { defer wg.Done() - if dflags.Verbose.Value { + if cfg.Verbose { cmd.Printf("Shutting down provisioner daemon %d...\n", id) } err := shutdownWithTimeout(provisionerDaemon.Shutdown, 5*time.Second) @@ -682,7 +685,7 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code cmd.PrintErrf("Close provisioner daemon %d: %s\n", id, err) return } - if dflags.Verbose.Value { + if cfg.Verbose { cmd.Printf("Gracefully shut down provisioner daemon %d\n", id) } }() @@ -732,9 +735,13 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code Use: "postgres-builtin-serve", Short: "Run the built-in PostgreSQL deployment.", RunE: func(cmd *cobra.Command, args []string) error { + dcfg, err := deployment.Config(vip) + if err != nil { + return xerrors.Errorf("failed to read config: %w", err) + } cfg := createConfig(cmd) logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) - if dflags.Verbose.Value { + if dcfg.Verbose { logger = logger.Leveled(slog.LevelDebug) } @@ -755,7 +762,7 @@ func Server(dflags *codersdk.DeploymentFlags, newAPI func(context.Context, *code }, }) - deployment.AttachFlags(root.Flags(), dflags, false) + deployment.AttachFlags(root.Flags(), vip) return root } diff --git a/coderd/coderd.go b/coderd/coderd.go index 75263926d329c..e2aa6f8d581a7 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -92,7 +92,7 @@ type Options struct { MetricsCacheRefreshInterval time.Duration AgentStatsRefreshInterval time.Duration Experimental bool - DeploymentFlags *codersdk.DeploymentFlags + DeploymentConfig *codersdk.DeploymentConfig } // New constructs a Coder API handler. @@ -288,7 +288,7 @@ func New(options *Options) *API { }) r.Route("/flags", func(r chi.Router) { r.Use(apiKeyMiddleware) - r.Get("/deployment", api.deploymentFlags) + // r.Get("/deployment", api.deploymentFlags) }) r.Route("/audit", func(r chi.Router) { r.Use( diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index 05e3d6a27d2d4..62d63c9154466 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -91,7 +91,7 @@ type Options struct { IncludeProvisionerDaemon bool MetricsCacheRefreshInterval time.Duration AgentStatsRefreshInterval time.Duration - DeploymentFlags *codersdk.DeploymentFlags + DeploymentConfig *codersdk.DeploymentConfig // Overriding the database is heavily discouraged. // It should only be used in cases where multiple Coder @@ -265,11 +265,20 @@ func NewOptions(t *testing.T, options *Options) (func(http.Handler), context.Can }, }, }, +<<<<<<< HEAD AutoImportTemplates: options.AutoImportTemplates, MetricsCacheRefreshInterval: options.MetricsCacheRefreshInterval, AgentStatsRefreshInterval: options.AgentStatsRefreshInterval, DeploymentFlags: options.DeploymentFlags, } +======= + }, + AutoImportTemplates: options.AutoImportTemplates, + MetricsCacheRefreshInterval: options.MetricsCacheRefreshInterval, + AgentStatsRefreshInterval: options.AgentStatsRefreshInterval, + DeploymentConfig: options.DeploymentConfig, + } +>>>>>>> idk } // NewWithAPI constructs an in-memory API instance and returns a client to talk to it. diff --git a/coderd/flags.go b/coderd/flags.go index 7e2f1376df116..944828f041223 100644 --- a/coderd/flags.go +++ b/coderd/flags.go @@ -1,18 +1,10 @@ package coderd -import ( - "net/http" - - "github.com/coder/coder/cli/deployment" - "github.com/coder/coder/coderd/httpapi" - "github.com/coder/coder/coderd/rbac" -) - -func (api *API) deploymentFlags(rw http.ResponseWriter, r *http.Request) { - if !api.Authorize(r, rbac.ActionRead, rbac.ResourceDeploymentFlags) { - httpapi.Forbidden(rw) - return - } - - httpapi.Write(r.Context(), rw, http.StatusOK, deployment.RemoveSensitiveValues(*api.DeploymentFlags)) -} +// func (api *API) deploymentFlags(rw http.ResponseWriter, r *http.Request) { +// if !api.Authorize(r, rbac.ActionRead, rbac.ResourceDeploymentFlags) { +// httpapi.Forbidden(rw) +// return +// } + +// httpapi.Write(r.Context(), rw, http.StatusOK, deployment.RemoveSensitiveValues(*api.DeploymentFlags)) +// } diff --git a/coderd/flags_test.go b/coderd/flags_test.go index dbff5992385d4..a5ae3d916a6ef 100644 --- a/coderd/flags_test.go +++ b/coderd/flags_test.go @@ -1,47 +1,36 @@ package coderd_test -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/coder/coder/cli/deployment" - "github.com/coder/coder/coderd/coderdtest" - "github.com/coder/coder/testutil" -) - const ( secretValue = "********" ) -func TestDeploymentFlagSecrets(t *testing.T) { - t.Parallel() - hi := "hi" - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - df := deployment.Flags() - // check if copy works for non-secret values - df.AccessURL.Value = hi - // check if secrets are removed - df.OAuth2GithubClientSecret.Value = hi - df.OIDCClientSecret.Value = hi - df.PostgresURL.Value = hi - df.SCIMAuthHeader.Value = hi +// func TestDeploymentFlagSecrets(t *testing.T) { +// t.Parallel() +// hi := "hi" +// ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) +// defer cancel() +// df := deployment.Flags() +// // check if copy works for non-secret values +// df.AccessURL.Value = hi +// // check if secrets are removed +// df.OAuth2GithubClientSecret.Value = hi +// df.OIDCClientSecret.Value = hi +// df.PostgresURL.Value = hi +// df.SCIMAuthHeader.Value = hi - client := coderdtest.New(t, &coderdtest.Options{ - DeploymentFlags: df, - }) - _ = coderdtest.CreateFirstUser(t, client) - scrubbed, err := client.DeploymentFlags(ctx) - require.NoError(t, err) - // ensure df is unchanged - require.EqualValues(t, hi, df.OAuth2GithubClientSecret.Value) - // ensure normal values pass through - require.EqualValues(t, hi, scrubbed.AccessURL.Value) - // ensure secrets are removed - require.EqualValues(t, secretValue, scrubbed.OAuth2GithubClientSecret.Value) - require.EqualValues(t, secretValue, scrubbed.OIDCClientSecret.Value) - require.EqualValues(t, secretValue, scrubbed.PostgresURL.Value) - require.EqualValues(t, secretValue, scrubbed.SCIMAuthHeader.Value) -} +// client := coderdtest.New(t, &coderdtest.Options{ +// DeploymentFlags: df, +// }) +// _ = coderdtest.CreateFirstUser(t, client) +// scrubbed, err := client.DeploymentFlags(ctx) +// require.NoError(t, err) +// // ensure df is unchanged +// require.EqualValues(t, hi, df.OAuth2GithubClientSecret.Value) +// // ensure normal values pass through +// require.EqualValues(t, hi, scrubbed.AccessURL.Value) +// // ensure secrets are removed +// require.EqualValues(t, secretValue, scrubbed.OAuth2GithubClientSecret.Value) +// require.EqualValues(t, secretValue, scrubbed.OIDCClientSecret.Value) +// require.EqualValues(t, secretValue, scrubbed.PostgresURL.Value) +// require.EqualValues(t, secretValue, scrubbed.SCIMAuthHeader.Value) +// } diff --git a/codersdk/config.go b/codersdk/config.go index cbde81ab21496..2caddc438b2ef 100644 --- a/codersdk/config.go +++ b/codersdk/config.go @@ -10,232 +10,241 @@ import ( ) type DeploymentConfig struct { - // External URL to access your deployment. This must be accessible by all provisioned workspaces. - AccessURL string `json:"access_url"` - // Specifies the wildcard hostname to use for workspace applications in the form "*.example.com". - WildcardAccessURL string `json:"wildcard_access_url"` - // Bind address of the server. - Address string `json:"address"` - // Interval to poll for scheduled workspace builds. - AutobuildPollInterval time.Duration `json:"autobuild_poll_interval"` - DERP DERPConfig `json:"derp"` - Prometheus PrometheusConfig `json:"prometheus"` - Pprof PprofConfig `json:"pprof"` - // The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. - CacheDir string `json:"cache_dir"` - // Controls whether data will be stored in an in-memory database. - InMemoryDatabase bool `json:"in_memory_database"` - // Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. - ProvisionerDaemonCount int `json:"provisioner_daemon_count"` - // 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". - PostgresURL string `json:"postgres_url"` - Oauth2Github Oauth2GithubConfig `json:"oauth2_github"` - OIDC OIDCConfig `json:"oidc"` - Telemetry TelemetryConfig `json:"telemetry"` - TLSConfig TLSConfig `json:"tls_config"` - // Whether application tracing data is collected. - TraceEnable bool `json:"trace_enable"` - // Controls if the 'Secure' property is set on browser session cookies. - SecureAuthCookie bool `json:"secure_auth_cookie"` - // The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". - SSHKeygenAlgorithm string `json:"ssh_keygen_algorithm"` - // Templates to auto-import. Available auto-importable templates are: kubernetes - AutoImportTemplates []string `json:"auto_import_templates"` - // How frequently metrics are refreshed - MetricsCacheRefreshInterval time.Duration `json:"metrics_cache_refresh_interval"` - // How frequently agent stats are recorded - AgentStatRefreshInterval time.Duration `json:"agent_stat_refresh_interval"` - // Enables verbose logging. - Verbose bool `json:"verbose"` - // Specifies whether audit logging is enabled. - AuditLogging bool `json:"audit_logging"` - // Whether Coder only allows connections to workspaces via the browser. - BrowserOnly bool `json:"browser_only"` - // Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. - SCIMAuthHeader string `json:"scim_auth_header"` - // Enables and sets a limit on how many workspaces each user can create. - UserWorkspaceQuota int `json:"user_workspace_quota"` + // Usage: External URL to access your deployment. This must be accessible by all provisioned workspaces. + // Flag: "access-url" + AccessURL string `mapstructure:"access_url"` + // Usage: Specifies the wildcard hostname to use for workspace applications in the form "*.example.com". + // Flag: "wildcard-access-url" + WildcardAccessURL string `mapstructure:"wildcard_access_url"` + // Usage: Bind address of the server. + // Flag: "address" + // Shorthand: "a" + // Default: "127.0.0.1:3000" + Address string `mapstructure:"address"` + // Usage: Interval to poll for scheduled workspace builds. + // Flag: "autobuild-poll-interval" + // Hidden: true + // Default: time.Minute + AutobuildPollInterval time.Duration `mapstructure:"autobuild_poll_interval"` + DERP DERPConfig `mapstructure:"derp"` + Prometheus PrometheusConfig `mapstructure:"prometheus"` + Pprof PprofConfig `mapstructure:"pprof"` + // Usage: The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. + // Flag: "cache-dir" + // Default: defaultCacheDir() + CacheDir string `mapstructure:"cache_dir"` + // Usage: Controls whether data will be stored in an in-memory database. + // Flag: "in-memory" + // Hidden: true + InMemoryDatabase bool `mapstructure:"in_memory_database"` + // Usage: Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. + // Flag: "provisioner-daemons" + // Default: 3 + ProvisionerDaemonCount int `mapstructure:"provisioner_daemon_count"` + // Usage: 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". + // Flag: "postgres-url" + PostgresURL string `mapstructure:"postgres_url"` + OAuth2Github OAuth2GithubConfig `mapstructure:"oauth2_github"` + OIDC OIDCConfig `mapstructure:"oidc"` + Telemetry TelemetryConfig `mapstructure:"telemetry"` + TLS TLSConfig `mapstructure:"tls_config"` + // Usage: Whether application tracing data is collected. + // Flag: "trace" + TraceEnable bool `mapstructure:"trace_enable"` + // Usage: Controls if the 'Secure' property is set on browser session cookies. + // Flag: "secure-auth-cookie" + SecureAuthCookie bool `mapstructure:"secure_auth_cookie"` + // Usage: The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". + // Flag: "ssh-keygen-algorithm" + // Default: "ed25519" + SSHKeygenAlgorithm string `mapstructure:"ssh_keygen_algorithm"` + // Usage: Templates to auto-import. Available auto-importable templates are: kubernetes + // Flag: "auto-import-template" + // Hidden: true + AutoImportTemplates []string `mapstructure:"auto_import_templates"` + // Usage: How frequently metrics are refreshed + // Flag: "metrics-cache-refresh-interval" + // Hidden: true + // Default: time.Hour + MetricsCacheRefreshInterval time.Duration `mapstructure:"metrics_cache_refresh_interval"` + // Usage: How frequently agent stats are recorded + // Flag: "agent-stats-refresh-interval" + // Hidden: true + // Default: 10 * time.Minute + AgentStatRefreshInterval time.Duration `mapstructure:"agent_stat_refresh_interval"` + // Usage: Enables verbose logging. + // Flag: "verbose" + // Shorthand: "v" + Verbose bool `mapstructure:"verbose"` + // Usage: Specifies whether audit logging is enabled. + // Flag: "audit-logging" + // Default: true + // Enterprise: true + AuditLogging bool `mapstructure:"audit_logging"` + // Usage: Whether Coder only allows connections to workspaces via the browser. + // Flag: "browser-only" + // Enterprise: true + BrowserOnly bool `mapstructure:"browser_only"` + // Usage: Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. + // Flag: "scim-auth-header" + // Enterprise: true + SCIMAuthHeader string `mapstructure:"scim_auth_header"` + // Usage: Enables and sets a limit on how many workspaces each user can create. + // Flag: "user-workspace-quota" + // Enterprise: true + UserWorkspaceQuota int `mapstructure:"user_workspace_quota"` } type DERPConfig struct { - Server DERPServerConfig `json:"server"` - Config DERPConfigConfig `json:"config"` + Server DERPServerConfig `mapstructure:"server"` + Config DERPConfigConfig `mapstructure:"config"` } type DERPServerConfig struct { - // Whether to enable or disable the embedded DERP relay server. - Enable bool `json:"enabled"` - // Region ID to use for the embedded DERP server. - RegionID int `json:"region_id"` - // Region code to use for the embedded DERP server. - RegionCode string `json:"region_code"` - // Region name that for the embedded DERP server. - RegionName string `json:"region_name"` - // Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections. - STUNAddresses []string `json:"stun_address"` + // Usage: Whether to enable or disable the embedded DERP relay server. + // Flag: "derp-server-enable" + // Default: true + Enable bool `mapstructure:"enabled"` + // Usage: Region ID to use for the embedded DERP server. + // Flag: "derp-server-region-id" + // Default: 999 + RegionID int `mapstructure:"region_id"` + // Usage: Region code to use for the embedded DERP server. + // Flag: "derp-server-region-code" + // Default: "coder" + RegionCode string `mapstructure:"region_code"` + // Usage: Region name that for the embedded DERP server. + // Flag: "derp-server-region-name" + // Default: "Coder Embedded Relay" + RegionName string `mapstructure:"region_name"` + // Usage: Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections. + // Flag: "derp-server-stun-addresses" + // Default: []string{"stun.l.google.com:19302"} + STUNAddresses []string `mapstructure:"stun_address"` } type DERPConfigConfig struct { - // URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/ - URL string `json:"url"` - // Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/ - Path string `json:"path"` + // Usage: URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/ + // Flag: "derp-config-url" + URL string `mapstructure:"url"` + // Usage: Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/ + // Flag: "derp-config-path" + Path string `mapstructure:"path"` } type PrometheusConfig struct { - // Serve prometheus metrics on the address defined by prometheus address. - Enable bool `json:"enabled"` - // The bind address to serve prometheus metrics. - Address string `json:"address"` + // Usage: Serve prometheus metrics on the address defined by prometheus address. + // Flag: "prometheus-enable" + Enable bool `mapstructure:"enabled"` + // Usage: The bind address to serve prometheus metrics. + // Flag: "prometheus-address" + // Default: "127.0.0.1:2112" + Address string `mapstructure:"address"` } type PprofConfig struct { - // Serve pprof metrics on the address defined by pprof address. - Enable bool `json:"enabled"` - // The bind address to serve pprof. - Address string `json:"address"` + // Usage: Serve pprof metrics on the address defined by pprof address. + // Flag: "pprof-enable" + Enable bool `mapstructure:"enabled"` + // Usage: The bind address to serve pprof. + // Flag: "pprof-address" + // Default: "127.0.0.1:6060" + Address string `mapstructure:"address"` } -type Oauth2GithubConfig struct { - // Client ID for Login with GitHub. - ClientID string `json:"client_id"` - // Client secret for Login with GitHub. - ClientSecret string `json:"client_secret"` - // Organizations the user must be a member of to Login with GitHub. - AllowedOrganizations []string `json:"allowed_organizations"` - // Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /. - AllowedTeams []string `json:"allowed_teams"` - // Whether new users can sign up with GitHub. - AllowSignups bool `json:"allow_signups"` - // Base URL of a GitHub Enterprise deployment to use for Login with GitHub. - EnterpriseBaseURL string `json:"enterprise_base_url"` +type OAuth2GithubConfig struct { + // Usage: Client ID for Login with GitHub. + // Flag: "oauth2-github-client-id" + ClientID string `mapstructure:"client_id"` + // Usage: Client secret for Login with GitHub. + // Flag: "oauth2-github-client-secret" + ClientSecret string `mapstructure:"client_secret"` + // Usage: Organizations the user must be a member of to Login with GitHub. + // Flag: "oauth2-github-allowed-orgs" + AllowedOrganizations []string `mapstructure:"allowed_organizations"` + // Usage: Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /. + // Flag: "oauth2-github-allowed-teams" + AllowedTeams []string `mapstructure:"allowed_teams"` + // Usage: Whether new users can sign up with GitHub. + // Flag: "oauth2-github-allow-signups" + AllowSignups bool `mapstructure:"allow_signups"` + // Usage: Base URL of a GitHub Enterprise deployment to use for Login with GitHub. + // Flag: "oauth2-github-enterprise-base-url" + EnterpriseBaseURL string `mapstructure:"enterprise_base_url"` } type OIDCConfig struct { - // Whether new users can sign up with OIDC. - AllowSignups bool `json:"allow_signups"` - // Client ID to use for Login with OIDC. - ClientID string `json:"client_id"` - // Client secret to use for Login with OIDC. - ClientSecret string `json:"cliet_secret"` - // Email domain that clients logging in with OIDC must match. - EmailDomain string `json:"email_domain"` - // Issuer URL to use for Login with OIDC. - IssuerURL string `json:"issuer_url"` - // Scopes to grant when authenticating with OIDC. - Scopes []string `json:"scopes"` + // Usage: Whether new users can sign up with OIDC. + // Flag: "oidc-allow-signups" + // Default: true + AllowSignups bool `mapstructure:"allow_signups"` + // Usage: Client ID to use for Login with OIDC. + // Flag: "oidc-client-id" + ClientID string `mapstructure:"client_id"` + // Usage: Client secret to use for Login with OIDC. + // Flag: "oidc-client-secret" + ClientSecret string `mapstructure:"cliet_secret"` + // Usage: Email domain that clients logging in with OIDC must match. + // Flag: "oidc-email-domain" + EmailDomain string `mapstructure:"email_domain"` + // Usage: Issuer URL to use for Login with OIDC. + // Flag: "oidc-issuer-url" + IssuerURL string `mapstructure:"issuer_url"` + // Usage: Scopes to grant when authenticating with OIDC. + // Flag: "oidc-scopes" + // Default: []string{oidc.ScopeOpenID, "profile", "email"} + Scopes []string `mapstructure:"scopes"` } type TelemetryConfig struct { - // Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product. - Enable bool `json:"enable"` - // Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option. - TraceEnable bool `json:"trace_enable"` - // URL to send telemetry. - URL string `json:"url"` + // Usage: Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product. + // Flag: "telemetry" + // Default: flag.Lookup("test.v") == nil + Enable bool `mapstructure:"enable"` + // Usage: Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option. + // Flag: "telemetry-trace" + // Default: flag.Lookup("test.v") == nil + TraceEnable bool `mapstructure:"trace_enable"` + // Usage: URL to send telemetry. + // Flag: "telemetry-url" + // Hidden: true + // Default: "https://telemetry.coder.com" + URL string `mapstructure:"url"` } type TLSConfig struct { - // Whether TLS will be enabled. - Enable bool `json:"tls_enable"` - // Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file. - CertFiles []string `json:"tls_cert_files"` - // PEM-encoded Certificate Authority file used for checking the authenticity of client - ClientCAFile string `json:"tls_client_ca_file"` - // Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". - ClientAuth string `json:"tls_client_auth"` - // Paths to the private keys for each of the certificates. It requires a PEM-encoded file. - KeyFiles []string `json:"tls_key_tiles"` - // Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" - MinVersion string `json:"tls_min_version"` + // Usage: Whether TLS will be enabled. + // Flag: "tls-enable" + Enable bool `mapstructure:"tls_enable"` + // Usage: Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file. + // Flag: "tls-cert-file" + CertFiles []string `mapstructure:"tls_cert_files"` + // Usage: PEM-encoded Certificate Authority file used for checking the authenticity of client + // Flag: "tls-client-ca-file" + ClientCAFile string `mapstructure:"tls_client_ca_file"` + // Usage: Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". + // Flag: "tls-client-auth" + ClientAuth string `mapstructure:"tls_client_auth"` + // Usage: Paths to the private keys for each of the certificates. It requires a PEM-encoded file. + // Flag: "tls-key-file" + KeyFiles []string `mapstructure:"tls_key_tiles"` + // Usage: Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" + // Flag: "tls-min-version" + // Default: "tls12" + MinVersion string `mapstructure:"tls_min_version"` } -type DeploymentFlags struct { - AccessURL *Flag[string] `json:"access_url"` - WildcardAccessURL *Flag[string] `json:"wildcard_access_url"` - Address *Flag[string] `json:"address"` - AutobuildPollInterval *Flag[time.Duration] `json:"autobuild_poll_interval"` - DerpServerEnable *Flag[bool] `json:"enabled"` - DerpServerRegionID *Flag[int] `json:"region_id"` - DerpServerRegionCode *Flag[string] `json:"region_code"` - DerpServerRegionName *Flag[string] `json:"region_name"` - DerpServerSTUNAddresses *Flag[[]string] `json:"stun_address"` - DerpConfigURL *Flag[string] `json:"derp_config_url"` - DerpConfigPath *Flag[string] `json:"derp_config_path"` - PromEnabled *Flag[bool] `json:"prom_enabled"` - PromAddress *Flag[string] `json:"prom_address"` - PprofEnabled *Flag[bool] `json:"pprof_enabled"` - PprofAddress *Flag[string] `json:"pprof_address"` - CacheDir *Flag[string] `json:"cache_dir"` - InMemoryDatabase *Flag[bool] `json:"in_memory_database"` - ProvisionerDaemonCount *Flag[int] `json:"provisioner_daemon_count"` - PostgresURL *Flag[string] `json:"postgres_url"` - OAuth2GithubClientID *Flag[string] `json:"client_id"` - OAuth2GithubClientSecret *Flag[string] `json:"client_secret"` - OAuth2GithubAllowedOrganizations *Flag[[]string] `json:"allowed_organizations"` - OAuth2GithubAllowedTeams *Flag[[]string] `json:"allowed_teams"` - OAuth2GithubAllowSignups *Flag[bool] `json:"allow_signups"` - OAuth2GithubEnterpriseBaseURL *Flag[string] `json:"enterprise_base_url"` - OIDCAllowSignups *Flag[bool] `json:"allow_signups"` - OIDCClientID *Flag[string] `json:"client_id"` - OIDCClientSecret *Flag[string] `json:"cliet_secret"` - OIDCEmailDomain *Flag[string] `json:"email_domain"` - OIDCIssuerURL *Flag[string] `json:"issuer_url"` - OIDCScopes *Flag[[]string] `json:"scopes"` - TelemetryEnable *Flag[bool] `json:"telemetry_enable"` - TelemetryTraceEnable *Flag[bool] `json:"telemetry_trace_enable"` - TelemetryURL *Flag[string] `json:"telemetry_url"` - TLSEnable *Flag[bool] `json:"tls_enable"` - TLSCertFiles *Flag[[]string] `json:"tls_cert_files"` - TLSClientCAFile *Flag[string] `json:"tls_client_ca_file"` - TLSClientAuth *Flag[string] `json:"tls_client_auth"` - TLSKeyFiles *Flag[[]string] `json:"tls_key_tiles"` - TLSMinVersion *Flag[string] `json:"tls_min_version"` - TraceEnable *Flag[bool] `json:"trace_enable"` - SecureAuthCookie *Flag[bool] `json:"secure_auth_cookie"` - SSHKeygenAlgorithm *Flag[string] `json:"ssh_keygen_algorithm"` - AutoImportTemplates *Flag[[]string] `json:"auto_import_templates"` - MetricsCacheRefreshInterval *Flag[time.Duration] `json:"metrics_cache_refresh_interval"` - AgentStatRefreshInterval *Flag[time.Duration] `json:"agent_stat_refresh_interval"` - Verbose *Flag[bool] `json:"verbose"` - AuditLogging *Flag[bool] `json:"audit_logging"` - BrowserOnly *Flag[bool] `json:"browser_only"` - SCIMAuthHeader *Flag[string] `json:"scim_auth_header"` - UserWorkspaceQuota *Flag[int] `json:"user_workspace_quota"` -} - -type Flaggable interface { - string | int | bool | time.Duration | []string -} - -type Flag[T Flaggable] struct { - Name string `json:"name"` - Flag string `json:"flag"` - EnvVar string `json:"env_var"` - Shorthand string `json:"shorthand"` - Description string `json:"description"` - Enterprise bool `json:"enterprise"` - Hidden bool `json:"hidden"` - Secret bool `json:"secret"` - Default T `json:"default"` - Value T `json:"value"` -} - -func (f *Flag[T]) IsEnterprise() bool { - return f.Enterprise -} - -// DeploymentFlags returns the deployment level flags for the coder server. -func (c *Client) DeploymentFlags(ctx context.Context) (DeploymentFlags, error) { +// DeploymentConfig returns the deployment config for the coder server. +func (c *Client) DeploymentConfig(ctx context.Context) (DeploymentConfig, error) { res, err := c.Request(ctx, http.MethodGet, "/api/v2/flags/deployment", nil) if err != nil { - return DeploymentFlags{}, xerrors.Errorf("execute request: %w", err) + return DeploymentConfig{}, xerrors.Errorf("execute request: %w", err) } defer res.Body.Close() if res.StatusCode != http.StatusOK { - return DeploymentFlags{}, readBodyAsError(res) + return DeploymentConfig{}, readBodyAsError(res) } - var df DeploymentFlags + var df DeploymentConfig return df, json.NewDecoder(res.Body).Decode(&df) } diff --git a/enterprise/cli/server.go b/enterprise/cli/server.go index 9459cc6906a84..cf5d572290441 100644 --- a/enterprise/cli/server.go +++ b/enterprise/cli/server.go @@ -24,7 +24,7 @@ import ( ) func server() *cobra.Command { - dflags := deployment.Flags() + vip := deployment.DefaultViper() cmd := agpl.Server(dflags, func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, io.Closer, error) { if dflags.DerpServerRelayAddress.Value != "" { _, err := url.Parse(dflags.DerpServerRelayAddress.Value) @@ -58,13 +58,13 @@ func server() *cobra.Command { } o := &coderd.Options{ - AuditLogging: dflags.AuditLogging.Value, - BrowserOnly: dflags.BrowserOnly.Value, - SCIMAPIKey: []byte(dflags.SCIMAuthHeader.Value), - UserWorkspaceQuota: dflags.UserWorkspaceQuota.Value, - RBAC: true, - DERPServerRelayAddress: dflags.DerpServerRelayAddress.Value, - DERPServerRegionID: dflags.DerpServerRegionID.Value, + AuditLogging: options.DeploymentConfig.AuditLogging, + BrowserOnly: options.DeploymentConfig.BrowserOnly, + SCIMAPIKey: []byte(options.DeploymentConfig.SCIMAuthHeader), + UserWorkspaceQuota: options.DeploymentConfig.UserWorkspaceQuota, + RBACEnabled: true, + DERPServerRelayAddress: options.DeploymentConfig.DerpServerRelayAddress, + DERPServerRegionID: options.DeploymentConfig.DerpServerRegionID, Options: options, } @@ -76,6 +76,7 @@ func server() *cobra.Command { return api.AGPL, api, nil }) - deployment.AttachFlags(cmd.Flags(), dflags, true) + deployment.AttachEnterpriseFlags(cmd.Flags(), vip) + return cmd } diff --git a/go.mod b/go.mod index 758f1d6a4fff9..c7ec45dd2a9b2 100644 --- a/go.mod +++ b/go.mod @@ -160,6 +160,15 @@ require github.com/jmoiron/sqlx v1.3.5 require github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 +require ( + github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/magiconair/properties v1.8.6 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/spf13/viper v1.13.0 // indirect + github.com/subosito/gotenv v1.4.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect +) + require ( filippo.io/edwards25519 v1.0.0-rc.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect @@ -246,7 +255,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect github.com/opencontainers/runc v1.1.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.4 // indirect + github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/pion/transport v0.13.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index 099a4d96cbd56..58de16d6865c3 100644 --- a/go.sum +++ b/go.sum @@ -594,6 +594,8 @@ github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= @@ -1230,6 +1232,8 @@ github.com/mafredri/udp v0.1.2-0.20220805105907-b2872e92e98d/go.mod h1:GUd681aT3 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -1483,8 +1487,12 @@ github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAv github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.4 h1:MHHO+ZUPwPZQ6BmnnT81iQg5cuurp78CRH7rNsguSMk= github.com/pelletier/go-toml/v2 v2.0.4/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= +github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= @@ -1680,6 +1688,8 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= +github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= +github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= @@ -1703,6 +1713,8 @@ github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= +github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/swaggest/assertjson v1.7.0 h1:SKw5Rn0LQs6UvmGrIdaKQbMR1R3ncXm5KNon+QJ7jtw= github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -2708,6 +2720,8 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= diff --git a/server.yaml b/server.yaml new file mode 100644 index 0000000000000..e69de29bb2d1d From 3794097cb4c54974dbcdef9481ba2d6bac3b9255 Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 17 Oct 2022 19:36:14 +0000 Subject: [PATCH 03/43] add script --- cli/deployment/config.go | 77 +++++++++-- codersdk/config.go | 108 +++++++-------- scripts/cligen/main.go | 268 ++++++++++++++++++++++++++++++++++++ scripts/cligen/main_test.go | 23 ++++ 4 files changed, 413 insertions(+), 63 deletions(-) create mode 100644 scripts/cligen/main.go create mode 100644 scripts/cligen/main_test.go diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 6ec3942bca08c..0ae5498d6bd86 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package deployment import ( @@ -7,24 +8,82 @@ import ( "github.com/coder/coder/codersdk" ) +func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { + cfg := codersdk.DeploymentConfig{} + return cfg, vip.Unmarshal(cfg) +} + func DefaultViper() *viper.Viper { v := viper.New() - v.SetDefault("access_url", "") + v.AutomaticEnv() + v.SetDefault("address", "127.0.0.1:3000") + v.SetDefault("cache_dir", defaultCacheDir()) + v.SetDefault("provisioner_daemon_count", 3) + v.SetDefault("ssh_keygen_algorithm", "ed25519") + v.SetDefault("audit_logging", true) + v.SetDefault("audit_logging", true) return v } func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { - _ = flagset.StringP("access-url", "", vip.GetString("access-url"), "usage") - _ = vip.BindPFlag("access-url", flagset.Lookup("access-url")) + _ = flagset.StringP("access-url", "", vip.GetString("access_url"), `External URL to access your deployment. This must be accessible by all provisioned workspaces.`) + _ = vip.BindPFlag("access_url", flagset.Lookup("access-url")) + _ = flagset.StringP("wildcard-access-url", "", vip.GetString("wildcard_access_url"), `Specifies the wildcard hostname to use for workspace applications in the form "*.example.com".`) + _ = vip.BindPFlag("wildcard_access_url", flagset.Lookup("wildcard-access-url")) + _ = flagset.StringP("address", "", vip.GetString("address"), `Bind address of the server.`) + _ = vip.BindPFlag("address", flagset.Lookup("address")) + _ = flagset.StringP("address", "a", vip.GetString("address"), `Bind address of the server.`) + _ = vip.BindPFlag("address", flagset.Lookup("address")) + _ = flagset.StringP("address", "a", vip.GetString("address"), `Bind address of the server.`) + _ = vip.BindPFlag("address", flagset.Lookup("address")) + _ = flagset.StringP("cache-dir", "", vip.GetString("cache_dir"), `The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.`) + _ = vip.BindPFlag("cache_dir", flagset.Lookup("cache-dir")) + _ = flagset.StringP("cache-dir", "", vip.GetString("cache_dir"), `The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.`) + _ = vip.BindPFlag("cache_dir", flagset.Lookup("cache-dir")) + _ = flagset.BoolP("in-memory", "", vip.GetBool("in_memory_database"), `Controls whether data will be stored in an in-memory database.`) + _ = vip.BindPFlag("in_memory_database", flagset.Lookup("in-memory")) + _ = flagset.BoolP("in-memory", "", vip.GetBool("in_memory_database"), `Controls whether data will be stored in an in-memory database.`) + _ = vip.BindPFlag("in_memory_database", flagset.Lookup("in-memory")) + _ = flagset.MarkHidden("in-memory") + _ = flagset.IntP("provisioner-daemons", "", vip.GetInt("provisioner_daemon_count"), `Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this.`) + _ = vip.BindPFlag("provisioner_daemon_count", flagset.Lookup("provisioner-daemons")) + _ = flagset.IntP("provisioner-daemons", "", vip.GetInt("provisioner_daemon_count"), `Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this.`) + _ = vip.BindPFlag("provisioner_daemon_count", flagset.Lookup("provisioner-daemons")) + _ = flagset.StringP("postgres-url", "", vip.GetString("postgres_url"), `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".`) + _ = vip.BindPFlag("postgres_url", flagset.Lookup("postgres-url")) + _ = flagset.BoolP("trace", "", vip.GetBool("trace_enable"), `Whether application tracing data is collected.`) + _ = vip.BindPFlag("trace_enable", flagset.Lookup("trace")) + _ = flagset.BoolP("secure-auth-cookie", "", vip.GetBool("secure_auth_cookie"), `Controls if the 'Secure' property is set on browser session cookies.`) + _ = vip.BindPFlag("secure_auth_cookie", flagset.Lookup("secure-auth-cookie")) + _ = flagset.StringP("ssh-keygen-algorithm", "", vip.GetString("ssh_keygen_algorithm"), `The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096".`) + _ = vip.BindPFlag("ssh_keygen_algorithm", flagset.Lookup("ssh-keygen-algorithm")) + _ = flagset.StringP("ssh-keygen-algorithm", "", vip.GetString("ssh_keygen_algorithm"), `The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096".`) + _ = vip.BindPFlag("ssh_keygen_algorithm", flagset.Lookup("ssh-keygen-algorithm")) + _ = flagset.BoolP("verbose", "", vip.GetBool("verbose"), `Enables verbose logging.`) + _ = vip.BindPFlag("verbose", flagset.Lookup("verbose")) + _ = flagset.BoolP("verbose", "v", vip.GetBool("verbose"), `Enables verbose logging.`) + _ = vip.BindPFlag("verbose", flagset.Lookup("verbose")) + _ = flagset.BoolP("audit-logging", "", vip.GetBool("audit_logging"), `Specifies whether audit logging is enabled.`) + _ = vip.BindPFlag("audit_logging", flagset.Lookup("audit-logging")) + _ = flagset.BoolP("audit-logging", "", vip.GetBool("audit_logging"), `Specifies whether audit logging is enabled.`) + _ = vip.BindPFlag("audit_logging", flagset.Lookup("audit-logging")) + _ = flagset.BoolP("browser-only", "", vip.GetBool("browser_only"), `Whether Coder only allows connections to workspaces via the browser.`) + _ = vip.BindPFlag("browser_only", flagset.Lookup("browser-only")) + _ = flagset.StringP("scim-auth-header", "", vip.GetString("scim_auth_header"), `Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication.`) + _ = vip.BindPFlag("scim_auth_header", flagset.Lookup("scim-auth-header")) + _ = flagset.IntP("user-workspace-quota", "", vip.GetInt("user_workspace_quota"), `Enables and sets a limit on how many workspaces each user can create.`) + _ = vip.BindPFlag("user_workspace_quota", flagset.Lookup("user-workspace-quota")) } func AttachEnterpriseFlags(flagset *pflag.FlagSet, vip *viper.Viper) { - _ = flagset.StringP("access-url", "", vip.GetString("access-url"), "usage") - _ = vip.BindPFlag("access-url", flagset.Lookup("access-url")) + _ = flagset.BoolP("audit-logging", "", vip.GetBool("audit_logging"), `Specifies whether audit logging is enabled.`) + _ = vip.BindPFlag("audit_logging", flagset.Lookup("audit-logging")) + _ = flagset.BoolP("browser-only", "", vip.GetBool("browser_only"), `Whether Coder only allows connections to workspaces via the browser.`) + _ = vip.BindPFlag("browser_only", flagset.Lookup("browser-only")) + _ = flagset.StringP("scim-auth-header", "", vip.GetString("scim_auth_header"), `Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication.`) + _ = vip.BindPFlag("scim_auth_header", flagset.Lookup("scim-auth-header")) + _ = flagset.IntP("user-workspace-quota", "", vip.GetInt("user_workspace_quota"), `Enables and sets a limit on how many workspaces each user can create.`) + _ = vip.BindPFlag("user_workspace_quota", flagset.Lookup("user-workspace-quota")) } -func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { - cfg := codersdk.DeploymentConfig{} - return cfg, vip.Unmarshal(cfg) -} diff --git a/codersdk/config.go b/codersdk/config.go index 2caddc438b2ef..c535440b4e34a 100644 --- a/codersdk/config.go +++ b/codersdk/config.go @@ -11,18 +11,18 @@ import ( type DeploymentConfig struct { // Usage: External URL to access your deployment. This must be accessible by all provisioned workspaces. - // Flag: "access-url" + // Flag: access-url AccessURL string `mapstructure:"access_url"` // Usage: Specifies the wildcard hostname to use for workspace applications in the form "*.example.com". - // Flag: "wildcard-access-url" + // Flag: wildcard-access-url WildcardAccessURL string `mapstructure:"wildcard_access_url"` // Usage: Bind address of the server. - // Flag: "address" - // Shorthand: "a" + // Flag: address + // Shorthand: a // Default: "127.0.0.1:3000" Address string `mapstructure:"address"` // Usage: Interval to poll for scheduled workspace builds. - // Flag: "autobuild-poll-interval" + // Flag: autobuild-poll-interval // Hidden: true // Default: time.Minute AutobuildPollInterval time.Duration `mapstructure:"autobuild_poll_interval"` @@ -30,67 +30,67 @@ type DeploymentConfig struct { Prometheus PrometheusConfig `mapstructure:"prometheus"` Pprof PprofConfig `mapstructure:"pprof"` // Usage: The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. - // Flag: "cache-dir" + // Flag: cache-dir // Default: defaultCacheDir() CacheDir string `mapstructure:"cache_dir"` // Usage: Controls whether data will be stored in an in-memory database. - // Flag: "in-memory" + // Flag: in-memory // Hidden: true InMemoryDatabase bool `mapstructure:"in_memory_database"` // Usage: Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. - // Flag: "provisioner-daemons" + // Flag: provisioner-daemons // Default: 3 ProvisionerDaemonCount int `mapstructure:"provisioner_daemon_count"` // Usage: 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". - // Flag: "postgres-url" + // Flag: postgres-url PostgresURL string `mapstructure:"postgres_url"` OAuth2Github OAuth2GithubConfig `mapstructure:"oauth2_github"` OIDC OIDCConfig `mapstructure:"oidc"` Telemetry TelemetryConfig `mapstructure:"telemetry"` TLS TLSConfig `mapstructure:"tls_config"` // Usage: Whether application tracing data is collected. - // Flag: "trace" + // Flag: trace TraceEnable bool `mapstructure:"trace_enable"` // Usage: Controls if the 'Secure' property is set on browser session cookies. - // Flag: "secure-auth-cookie" + // Flag: secure-auth-cookie SecureAuthCookie bool `mapstructure:"secure_auth_cookie"` // Usage: The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". - // Flag: "ssh-keygen-algorithm" + // Flag: ssh-keygen-algorithm // Default: "ed25519" SSHKeygenAlgorithm string `mapstructure:"ssh_keygen_algorithm"` // Usage: Templates to auto-import. Available auto-importable templates are: kubernetes - // Flag: "auto-import-template" + // Flag: auto-import-template // Hidden: true AutoImportTemplates []string `mapstructure:"auto_import_templates"` // Usage: How frequently metrics are refreshed - // Flag: "metrics-cache-refresh-interval" + // Flag: metrics-cache-refresh-interval // Hidden: true // Default: time.Hour MetricsCacheRefreshInterval time.Duration `mapstructure:"metrics_cache_refresh_interval"` // Usage: How frequently agent stats are recorded - // Flag: "agent-stats-refresh-interval" + // Flag: agent-stats-refresh-interval // Hidden: true // Default: 10 * time.Minute AgentStatRefreshInterval time.Duration `mapstructure:"agent_stat_refresh_interval"` // Usage: Enables verbose logging. - // Flag: "verbose" - // Shorthand: "v" + // Flag: verbose + // Shorthand: v Verbose bool `mapstructure:"verbose"` // Usage: Specifies whether audit logging is enabled. - // Flag: "audit-logging" + // Flag: audit-logging // Default: true // Enterprise: true AuditLogging bool `mapstructure:"audit_logging"` // Usage: Whether Coder only allows connections to workspaces via the browser. - // Flag: "browser-only" + // Flag: browser-only // Enterprise: true BrowserOnly bool `mapstructure:"browser_only"` // Usage: Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. - // Flag: "scim-auth-header" + // Flag: scim-auth-header // Enterprise: true SCIMAuthHeader string `mapstructure:"scim_auth_header"` // Usage: Enables and sets a limit on how many workspaces each user can create. - // Flag: "user-workspace-quota" + // Flag: user-workspace-quota // Enterprise: true UserWorkspaceQuota int `mapstructure:"user_workspace_quota"` } @@ -101,111 +101,111 @@ type DERPConfig struct { } type DERPServerConfig struct { // Usage: Whether to enable or disable the embedded DERP relay server. - // Flag: "derp-server-enable" + // Flag: derp-server-enable // Default: true Enable bool `mapstructure:"enabled"` // Usage: Region ID to use for the embedded DERP server. - // Flag: "derp-server-region-id" + // Flag: derp-server-region-id // Default: 999 RegionID int `mapstructure:"region_id"` // Usage: Region code to use for the embedded DERP server. - // Flag: "derp-server-region-code" + // Flag: derp-server-region-code // Default: "coder" RegionCode string `mapstructure:"region_code"` // Usage: Region name that for the embedded DERP server. - // Flag: "derp-server-region-name" + // Flag: derp-server-region-name // Default: "Coder Embedded Relay" RegionName string `mapstructure:"region_name"` // Usage: Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections. - // Flag: "derp-server-stun-addresses" + // Flag: derp-server-stun-addresses // Default: []string{"stun.l.google.com:19302"} STUNAddresses []string `mapstructure:"stun_address"` } type DERPConfigConfig struct { // Usage: URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/ - // Flag: "derp-config-url" + // Flag: derp-config-url URL string `mapstructure:"url"` // Usage: Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/ - // Flag: "derp-config-path" + // Flag: derp-config-path Path string `mapstructure:"path"` } type PrometheusConfig struct { // Usage: Serve prometheus metrics on the address defined by prometheus address. - // Flag: "prometheus-enable" + // Flag: prometheus-enable Enable bool `mapstructure:"enabled"` // Usage: The bind address to serve prometheus metrics. - // Flag: "prometheus-address" + // Flag: prometheus-address // Default: "127.0.0.1:2112" Address string `mapstructure:"address"` } type PprofConfig struct { // Usage: Serve pprof metrics on the address defined by pprof address. - // Flag: "pprof-enable" + // Flag: pprof-enable Enable bool `mapstructure:"enabled"` // Usage: The bind address to serve pprof. - // Flag: "pprof-address" + // Flag: pprof-address // Default: "127.0.0.1:6060" Address string `mapstructure:"address"` } type OAuth2GithubConfig struct { // Usage: Client ID for Login with GitHub. - // Flag: "oauth2-github-client-id" + // Flag: oauth2-github-client-id ClientID string `mapstructure:"client_id"` // Usage: Client secret for Login with GitHub. - // Flag: "oauth2-github-client-secret" + // Flag: oauth2-github-client-secret ClientSecret string `mapstructure:"client_secret"` // Usage: Organizations the user must be a member of to Login with GitHub. - // Flag: "oauth2-github-allowed-orgs" + // Flag: oauth2-github-allowed-orgs AllowedOrganizations []string `mapstructure:"allowed_organizations"` // Usage: Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /. - // Flag: "oauth2-github-allowed-teams" + // Flag: oauth2-github-allowed-teams AllowedTeams []string `mapstructure:"allowed_teams"` // Usage: Whether new users can sign up with GitHub. - // Flag: "oauth2-github-allow-signups" + // Flag: oauth2-github-allow-signups AllowSignups bool `mapstructure:"allow_signups"` // Usage: Base URL of a GitHub Enterprise deployment to use for Login with GitHub. - // Flag: "oauth2-github-enterprise-base-url" + // Flag: oauth2-github-enterprise-base-url EnterpriseBaseURL string `mapstructure:"enterprise_base_url"` } type OIDCConfig struct { // Usage: Whether new users can sign up with OIDC. - // Flag: "oidc-allow-signups" + // Flag: oidc-allow-signups // Default: true AllowSignups bool `mapstructure:"allow_signups"` // Usage: Client ID to use for Login with OIDC. - // Flag: "oidc-client-id" + // Flag: oidc-client-id ClientID string `mapstructure:"client_id"` // Usage: Client secret to use for Login with OIDC. - // Flag: "oidc-client-secret" + // Flag: oidc-client-secret ClientSecret string `mapstructure:"cliet_secret"` // Usage: Email domain that clients logging in with OIDC must match. - // Flag: "oidc-email-domain" + // Flag: oidc-email-domain EmailDomain string `mapstructure:"email_domain"` // Usage: Issuer URL to use for Login with OIDC. - // Flag: "oidc-issuer-url" + // Flag: oidc-issuer-url IssuerURL string `mapstructure:"issuer_url"` // Usage: Scopes to grant when authenticating with OIDC. - // Flag: "oidc-scopes" + // Flag: oidc-scopes // Default: []string{oidc.ScopeOpenID, "profile", "email"} Scopes []string `mapstructure:"scopes"` } type TelemetryConfig struct { // Usage: Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product. - // Flag: "telemetry" + // Flag: telemetry // Default: flag.Lookup("test.v") == nil Enable bool `mapstructure:"enable"` // Usage: Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option. - // Flag: "telemetry-trace" + // Flag: telemetry-trace // Default: flag.Lookup("test.v") == nil TraceEnable bool `mapstructure:"trace_enable"` // Usage: URL to send telemetry. - // Flag: "telemetry-url" + // Flag: telemetry-url // Hidden: true // Default: "https://telemetry.coder.com" URL string `mapstructure:"url"` @@ -213,23 +213,23 @@ type TelemetryConfig struct { type TLSConfig struct { // Usage: Whether TLS will be enabled. - // Flag: "tls-enable" + // Flag: tls-enable Enable bool `mapstructure:"tls_enable"` // Usage: Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file. - // Flag: "tls-cert-file" + // Flag: tls-cert-file CertFiles []string `mapstructure:"tls_cert_files"` // Usage: PEM-encoded Certificate Authority file used for checking the authenticity of client - // Flag: "tls-client-ca-file" + // Flag: tls-client-ca-file ClientCAFile string `mapstructure:"tls_client_ca_file"` // Usage: Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". - // Flag: "tls-client-auth" + // Flag: tls-client-auth ClientAuth string `mapstructure:"tls_client_auth"` // Usage: Paths to the private keys for each of the certificates. It requires a PEM-encoded file. - // Flag: "tls-key-file" + // Flag: tls-key-file KeyFiles []string `mapstructure:"tls_key_tiles"` // Usage: Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" - // Flag: "tls-min-version" - // Default: "tls12" + // Flag: tls-min-version + // Default: tls12 MinVersion string `mapstructure:"tls_min_version"` } diff --git a/scripts/cligen/main.go b/scripts/cligen/main.go new file mode 100644 index 0000000000000..c3da75d3d5de9 --- /dev/null +++ b/scripts/cligen/main.go @@ -0,0 +1,268 @@ +package main + +import ( + "bytes" + "context" + "fmt" + "go/ast" + "go/token" + "os" + "reflect" + "strconv" + "strings" + "text/template" + + "golang.org/x/tools/go/packages" + "golang.org/x/xerrors" + + "cdr.dev/slog" + "cdr.dev/slog/sloggers/sloghuman" +) + +const ( + PkgDir = "./codersdk" +) + +func main() { + ctx := context.Background() + log := slog.Make(sloghuman.Sink(os.Stderr)) + data, err := GenerateData(ctx, log, PkgDir) + if err != nil { + log.Fatal(ctx, err.Error()) + } + + // Just cat the output to a file to capture it + _, _ = fmt.Println(data.Render()) +} + +type Data struct { + Fields []Field +} + +type Field struct { + Key string + Usage string + Flag string + Shorthand string + Default string + Enterprise bool + Hidden bool + Type string +} + +func GenerateData(ctx context.Context, log slog.Logger, dir string) (*Data, error) { + g := Generator{ + log: log, + } + err := g.parsePackage(ctx, dir) + if err != nil { + return nil, xerrors.Errorf("parse package %q: %w", dir, err) + } + + codeBlocks, err := g.generateAll() + if err != nil { + return nil, xerrors.Errorf("parse package %q: %w", dir, err) + } + + return codeBlocks, nil +} + +type Generator struct { + // Package we are scanning. + pkg *packages.Package + log slog.Logger +} + +// parsePackage takes a list of patterns such as a directory, and parses them. +func (g *Generator) parsePackage(ctx context.Context, patterns ...string) error { + cfg := &packages.Config{ + // Just accept the fact we need these flags for what we want. Feel free to add + // more, it'll just increase the time it takes to parse. + Mode: packages.NeedTypes | packages.NeedName | packages.NeedTypesInfo | + packages.NeedTypesSizes | packages.NeedSyntax, + Tests: false, + Context: ctx, + } + + pkgs, err := packages.Load(cfg, patterns...) + if err != nil { + return xerrors.Errorf("load package: %w", err) + } + + // Only support 1 package for now. We can expand it if we need later, we + // just need to hook up multiple packages in the generator. + if len(pkgs) != 1 { + return xerrors.Errorf("expected 1 package, found %d", len(pkgs)) + } + + g.pkg = pkgs[0] + return nil +} + +func (g *Generator) generateAll() (*Data, error) { + cb := Data{} + for _, file := range g.pkg.Syntax { + for _, decl := range file.Decls { + decl, ok := decl.(*ast.GenDecl) + if !ok { + continue + } + if decl.Tok != token.TYPE { + continue + } + for _, speci := range decl.Specs { + spec, ok := speci.(*ast.TypeSpec) + if !ok { + continue + } + if spec.Name.Name != "DeploymentConfig" { + continue + } + t, ok := spec.Type.(*ast.StructType) + if !ok { + return nil, xerrors.Errorf("expected struct type, found %T", spec.Type) + } + for _, field := range t.Fields.List { + key := reflect.StructTag(strings.Trim(field.Tag.Value, "`")).Get("mapstructure") + if key == "" { + continue + } + f := Field{ + Key: key, + } + ft, ok := field.Type.(*ast.Ident) + if !ok { + // return nil, xerrors.Errorf("expected Ident type, found %T", field.Type) + continue + } + switch ft.Name { + case "string": + f.Type = "String" + case "int": + f.Type = "Int" + case "bool": + f.Type = "Bool" + case "[]string": + f.Type = "StringArray" + case "time.Duration": + f.Type = "Duration" + default: + continue + } + + for _, line := range field.Doc.List { + if strings.HasPrefix(line.Text, "// Usage:") { + v := strings.TrimPrefix(line.Text, "// Usage:") + v = strings.TrimSpace(v) + f.Usage = v + } + if strings.HasPrefix(line.Text, "// Flag:") { + v := strings.TrimPrefix(line.Text, "// Flag:") + v = strings.TrimSpace(v) + f.Flag = v + } + if strings.HasPrefix(line.Text, "// Shorthand:") { + v := strings.TrimPrefix(line.Text, "// Shorthand:") + v = strings.TrimSpace(v) + f.Shorthand = v + } + if strings.HasPrefix(line.Text, "// Default:") { + v := strings.TrimPrefix(line.Text, "// Default:") + v = strings.TrimSpace(v) + f.Default = v + } + if strings.HasPrefix(line.Text, "// Enterprise:") { + v := strings.TrimPrefix(line.Text, "// Enterprise:") + v = strings.TrimSpace(v) + b, err := strconv.ParseBool(v) + if err != nil { + return nil, xerrors.Errorf("parse enterprise: %w", err) + } + f.Enterprise = b + } + if strings.HasPrefix(line.Text, "// Hidden:") { + v := strings.TrimPrefix(line.Text, "// Hidden:") + v = strings.TrimSpace(v) + v = strings.TrimSpace(v) + b, err := strconv.ParseBool(v) + if err != nil { + return nil, xerrors.Errorf("parse hidden: %w", err) + } + f.Hidden = b + } + + cb.Fields = append(cb.Fields, f) + } + } + } + } + } + + return &cb, nil +} + +func (c Data) Render() string { + t, err := template.New("DeploymentConfig").Parse(deploymentConfigTemplate) + if err != nil { + panic(err) + } + var b bytes.Buffer + err = t.Execute(&b, c) + if err != nil { + panic(err) + } + + return b.String() +} + +const deploymentConfigTemplate = `// Code generated by go generate; DO NOT EDIT. +package deployment + +import ( + "github.com/spf13/pflag" + "github.com/spf13/viper" + + "github.com/coder/coder/codersdk" +) + +func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { + cfg := codersdk.DeploymentConfig{} + return cfg, vip.Unmarshal(cfg) +} + +func DefaultViper() *viper.Viper { + v := viper.New() + v.AutomaticEnv() + {{- range .Fields }} + {{- if .Default }} + v.SetDefault("{{ .Key }}", {{ .Default }}) + {{- end }} + {{- end }} + + return v +} + +func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { + {{- range .Fields }} + {{- if and (.Flag) (not .Enterprise) }} + _ = flagset.{{ .Type }}P("{{ .Flag }}", "{{ .Shorthand }}", vip.Get{{ .Type }}("{{ .Key }}"), ` + "`{{ .Usage }}`" + `) + _ = vip.BindPFlag("{{ .Key }}", flagset.Lookup("{{ .Flag }}")) + {{- if .Hidden }} + _ = flagset.MarkHidden("{{ .Flag }}") + {{- end }} + {{- end }} + {{- end }} +} + +func AttachEnterpriseFlags(flagset *pflag.FlagSet, vip *viper.Viper) { + {{- range .Fields }} + {{- if and .Flag .Enterprise }} + _ = flagset.{{ .Type }}P("{{ .Flag }}", "{{ .Shorthand }}", vip.Get{{ .Type }}("{{ .Key }}"), ` + "`{{ .Usage }}`" + `) + _ = vip.BindPFlag("{{ .Key }}", flagset.Lookup("{{ .Flag }}")) + {{- if .Hidden }} + _ = flagset.MarkHidden("{{ .Flag }}") + {{- end }} + {{- end }} + {{- end }} +} +` diff --git a/scripts/cligen/main_test.go b/scripts/cligen/main_test.go new file mode 100644 index 0000000000000..05bb4338b032b --- /dev/null +++ b/scripts/cligen/main_test.go @@ -0,0 +1,23 @@ +package main + +import ( + "context" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "cdr.dev/slog" + "cdr.dev/slog/sloggers/sloghuman" + "github.com/coder/coder/testutil" +) + +func TestCliGen(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + log := slog.Make(sloghuman.Sink(os.Stderr)) + cb, err := GenerateData(ctx, log, "../../codersdk") + require.NoError(t, err) + require.NotNil(t, cb) +} From d819ef66d679791891690446198dd2b035e6cd99 Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 17 Oct 2022 19:47:37 +0000 Subject: [PATCH 04/43] get working with normal fields --- cli/deployment/config.go | 41 ++++++++++++++++------------------------ scripts/cligen/main.go | 26 ++++++++++++++++++++----- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 0ae5498d6bd86..572392dfe6342 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -2,6 +2,9 @@ package deployment import ( + "os" + "path/filepath" + "github.com/spf13/pflag" "github.com/spf13/viper" @@ -21,7 +24,6 @@ func DefaultViper() *viper.Viper { v.SetDefault("provisioner_daemon_count", 3) v.SetDefault("ssh_keygen_algorithm", "ed25519") v.SetDefault("audit_logging", true) - v.SetDefault("audit_logging", true) return v } @@ -31,25 +33,15 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { _ = vip.BindPFlag("access_url", flagset.Lookup("access-url")) _ = flagset.StringP("wildcard-access-url", "", vip.GetString("wildcard_access_url"), `Specifies the wildcard hostname to use for workspace applications in the form "*.example.com".`) _ = vip.BindPFlag("wildcard_access_url", flagset.Lookup("wildcard-access-url")) - _ = flagset.StringP("address", "", vip.GetString("address"), `Bind address of the server.`) - _ = vip.BindPFlag("address", flagset.Lookup("address")) _ = flagset.StringP("address", "a", vip.GetString("address"), `Bind address of the server.`) _ = vip.BindPFlag("address", flagset.Lookup("address")) - _ = flagset.StringP("address", "a", vip.GetString("address"), `Bind address of the server.`) - _ = vip.BindPFlag("address", flagset.Lookup("address")) - _ = flagset.StringP("cache-dir", "", vip.GetString("cache_dir"), `The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.`) - _ = vip.BindPFlag("cache_dir", flagset.Lookup("cache-dir")) _ = flagset.StringP("cache-dir", "", vip.GetString("cache_dir"), `The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.`) _ = vip.BindPFlag("cache_dir", flagset.Lookup("cache-dir")) _ = flagset.BoolP("in-memory", "", vip.GetBool("in_memory_database"), `Controls whether data will be stored in an in-memory database.`) _ = vip.BindPFlag("in_memory_database", flagset.Lookup("in-memory")) - _ = flagset.BoolP("in-memory", "", vip.GetBool("in_memory_database"), `Controls whether data will be stored in an in-memory database.`) - _ = vip.BindPFlag("in_memory_database", flagset.Lookup("in-memory")) _ = flagset.MarkHidden("in-memory") _ = flagset.IntP("provisioner-daemons", "", vip.GetInt("provisioner_daemon_count"), `Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this.`) _ = vip.BindPFlag("provisioner_daemon_count", flagset.Lookup("provisioner-daemons")) - _ = flagset.IntP("provisioner-daemons", "", vip.GetInt("provisioner_daemon_count"), `Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this.`) - _ = vip.BindPFlag("provisioner_daemon_count", flagset.Lookup("provisioner-daemons")) _ = flagset.StringP("postgres-url", "", vip.GetString("postgres_url"), `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".`) _ = vip.BindPFlag("postgres_url", flagset.Lookup("postgres-url")) _ = flagset.BoolP("trace", "", vip.GetBool("trace_enable"), `Whether application tracing data is collected.`) @@ -58,22 +50,8 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { _ = vip.BindPFlag("secure_auth_cookie", flagset.Lookup("secure-auth-cookie")) _ = flagset.StringP("ssh-keygen-algorithm", "", vip.GetString("ssh_keygen_algorithm"), `The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096".`) _ = vip.BindPFlag("ssh_keygen_algorithm", flagset.Lookup("ssh-keygen-algorithm")) - _ = flagset.StringP("ssh-keygen-algorithm", "", vip.GetString("ssh_keygen_algorithm"), `The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096".`) - _ = vip.BindPFlag("ssh_keygen_algorithm", flagset.Lookup("ssh-keygen-algorithm")) - _ = flagset.BoolP("verbose", "", vip.GetBool("verbose"), `Enables verbose logging.`) - _ = vip.BindPFlag("verbose", flagset.Lookup("verbose")) _ = flagset.BoolP("verbose", "v", vip.GetBool("verbose"), `Enables verbose logging.`) _ = vip.BindPFlag("verbose", flagset.Lookup("verbose")) - _ = flagset.BoolP("audit-logging", "", vip.GetBool("audit_logging"), `Specifies whether audit logging is enabled.`) - _ = vip.BindPFlag("audit_logging", flagset.Lookup("audit-logging")) - _ = flagset.BoolP("audit-logging", "", vip.GetBool("audit_logging"), `Specifies whether audit logging is enabled.`) - _ = vip.BindPFlag("audit_logging", flagset.Lookup("audit-logging")) - _ = flagset.BoolP("browser-only", "", vip.GetBool("browser_only"), `Whether Coder only allows connections to workspaces via the browser.`) - _ = vip.BindPFlag("browser_only", flagset.Lookup("browser-only")) - _ = flagset.StringP("scim-auth-header", "", vip.GetString("scim_auth_header"), `Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication.`) - _ = vip.BindPFlag("scim_auth_header", flagset.Lookup("scim-auth-header")) - _ = flagset.IntP("user-workspace-quota", "", vip.GetInt("user_workspace_quota"), `Enables and sets a limit on how many workspaces each user can create.`) - _ = vip.BindPFlag("user_workspace_quota", flagset.Lookup("user-workspace-quota")) } func AttachEnterpriseFlags(flagset *pflag.FlagSet, vip *viper.Viper) { @@ -87,3 +65,16 @@ func AttachEnterpriseFlags(flagset *pflag.FlagSet, vip *viper.Viper) { _ = vip.BindPFlag("user_workspace_quota", flagset.Lookup("user-workspace-quota")) } +func defaultCacheDir() string { + defaultCacheDir, err := os.UserCacheDir() + if err != nil { + defaultCacheDir = os.TempDir() + } + if dir := os.Getenv("CACHE_DIRECTORY"); dir != "" { + // For compatibility with systemd. + defaultCacheDir = dir + } + + return filepath.Join(defaultCacheDir, "coder") +} + diff --git a/scripts/cligen/main.go b/scripts/cligen/main.go index c3da75d3d5de9..58bc9d5c45248 100644 --- a/scripts/cligen/main.go +++ b/scripts/cligen/main.go @@ -190,9 +190,9 @@ func (g *Generator) generateAll() (*Data, error) { } f.Hidden = b } - - cb.Fields = append(cb.Fields, f) } + + cb.Fields = append(cb.Fields, f) } } } @@ -219,6 +219,9 @@ const deploymentConfigTemplate = `// Code generated by go generate; DO NOT EDIT. package deployment import ( + "os" + "path/filepath" + "github.com/spf13/pflag" "github.com/spf13/viper" @@ -247,7 +250,7 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { {{- if and (.Flag) (not .Enterprise) }} _ = flagset.{{ .Type }}P("{{ .Flag }}", "{{ .Shorthand }}", vip.Get{{ .Type }}("{{ .Key }}"), ` + "`{{ .Usage }}`" + `) _ = vip.BindPFlag("{{ .Key }}", flagset.Lookup("{{ .Flag }}")) - {{- if .Hidden }} + {{- if and .Hidden }} _ = flagset.MarkHidden("{{ .Flag }}") {{- end }} {{- end }} @@ -256,13 +259,26 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { func AttachEnterpriseFlags(flagset *pflag.FlagSet, vip *viper.Viper) { {{- range .Fields }} - {{- if and .Flag .Enterprise }} + {{- if and (.Flag) (.Enterprise) }} _ = flagset.{{ .Type }}P("{{ .Flag }}", "{{ .Shorthand }}", vip.Get{{ .Type }}("{{ .Key }}"), ` + "`{{ .Usage }}`" + `) _ = vip.BindPFlag("{{ .Key }}", flagset.Lookup("{{ .Flag }}")) - {{- if .Hidden }} + {{- if and .Hidden }} _ = flagset.MarkHidden("{{ .Flag }}") {{- end }} {{- end }} {{- end }} } + +func defaultCacheDir() string { + defaultCacheDir, err := os.UserCacheDir() + if err != nil { + defaultCacheDir = os.TempDir() + } + if dir := os.Getenv("CACHE_DIRECTORY"); dir != "" { + // For compatibility with systemd. + defaultCacheDir = dir + } + + return filepath.Join(defaultCacheDir, "coder") +} ` From 14cdd5939140de9d20956fe78321b9155e165a5a Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 17 Oct 2022 20:29:52 +0000 Subject: [PATCH 05/43] get formatting right --- cli/deployment/config.go | 26 +++++++++++++++----------- scripts/cligen/main.go | 8 +++++++- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 572392dfe6342..7e8d16de6fd62 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -1,4 +1,6 @@ // Code generated by go generate; DO NOT EDIT. +// This file was generated by the script at scripts/cligen +// The data for populating this file is from the DeploymentConfig struct in codersdk. package deployment import ( @@ -9,6 +11,7 @@ import ( "github.com/spf13/viper" "github.com/coder/coder/codersdk" + "github.com/coder/coder/cli/cliui" ) func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { @@ -18,6 +21,7 @@ func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { func DefaultViper() *viper.Viper { v := viper.New() + v.SetEnvPrefix("coder") v.AutomaticEnv() v.SetDefault("address", "127.0.0.1:3000") v.SetDefault("cache_dir", defaultCacheDir()) @@ -29,28 +33,28 @@ func DefaultViper() *viper.Viper { } func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { - _ = flagset.StringP("access-url", "", vip.GetString("access_url"), `External URL to access your deployment. This must be accessible by all provisioned workspaces.`) + _ = flagset.StringP("access-url", "", vip.GetString("access_url"), `External URL to access your deployment. This must be accessible by all provisioned workspaces.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_ACCESS_URL")) _ = vip.BindPFlag("access_url", flagset.Lookup("access-url")) - _ = flagset.StringP("wildcard-access-url", "", vip.GetString("wildcard_access_url"), `Specifies the wildcard hostname to use for workspace applications in the form "*.example.com".`) + _ = flagset.StringP("wildcard-access-url", "", vip.GetString("wildcard_access_url"), `Specifies the wildcard hostname to use for workspace applications in the form "*.example.com".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_WILDCARD_ACCESS_URL")) _ = vip.BindPFlag("wildcard_access_url", flagset.Lookup("wildcard-access-url")) - _ = flagset.StringP("address", "a", vip.GetString("address"), `Bind address of the server.`) + _ = flagset.StringP("address", "a", vip.GetString("address"), `Bind address of the server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_ADDRESS")) _ = vip.BindPFlag("address", flagset.Lookup("address")) - _ = flagset.StringP("cache-dir", "", vip.GetString("cache_dir"), `The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.`) + _ = flagset.StringP("cache-dir", "", vip.GetString("cache_dir"), `The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_CACHE_DIR")) _ = vip.BindPFlag("cache_dir", flagset.Lookup("cache-dir")) - _ = flagset.BoolP("in-memory", "", vip.GetBool("in_memory_database"), `Controls whether data will be stored in an in-memory database.`) + _ = flagset.BoolP("in-memory", "", vip.GetBool("in_memory_database"), `Controls whether data will be stored in an in-memory database.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_IN_MEMORY_DATABASE")) _ = vip.BindPFlag("in_memory_database", flagset.Lookup("in-memory")) _ = flagset.MarkHidden("in-memory") - _ = flagset.IntP("provisioner-daemons", "", vip.GetInt("provisioner_daemon_count"), `Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this.`) + _ = flagset.IntP("provisioner-daemons", "", vip.GetInt("provisioner_daemon_count"), `Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PROVISIONER_DAEMON_COUNT")) _ = vip.BindPFlag("provisioner_daemon_count", flagset.Lookup("provisioner-daemons")) - _ = flagset.StringP("postgres-url", "", vip.GetString("postgres_url"), `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".`) + _ = flagset.StringP("postgres-url", "", vip.GetString("postgres_url"), `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".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_POSTGRES_URL")) _ = vip.BindPFlag("postgres_url", flagset.Lookup("postgres-url")) - _ = flagset.BoolP("trace", "", vip.GetBool("trace_enable"), `Whether application tracing data is collected.`) + _ = flagset.BoolP("trace", "", vip.GetBool("trace_enable"), `Whether application tracing data is collected.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TRACE_ENABLE")) _ = vip.BindPFlag("trace_enable", flagset.Lookup("trace")) - _ = flagset.BoolP("secure-auth-cookie", "", vip.GetBool("secure_auth_cookie"), `Controls if the 'Secure' property is set on browser session cookies.`) + _ = flagset.BoolP("secure-auth-cookie", "", vip.GetBool("secure_auth_cookie"), `Controls if the 'Secure' property is set on browser session cookies.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_SECURE_AUTH_COOKIE")) _ = vip.BindPFlag("secure_auth_cookie", flagset.Lookup("secure-auth-cookie")) - _ = flagset.StringP("ssh-keygen-algorithm", "", vip.GetString("ssh_keygen_algorithm"), `The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096".`) + _ = flagset.StringP("ssh-keygen-algorithm", "", vip.GetString("ssh_keygen_algorithm"), `The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_SSH_KEYGEN_ALGORITHM")) _ = vip.BindPFlag("ssh_keygen_algorithm", flagset.Lookup("ssh-keygen-algorithm")) - _ = flagset.BoolP("verbose", "v", vip.GetBool("verbose"), `Enables verbose logging.`) + _ = flagset.BoolP("verbose", "v", vip.GetBool("verbose"), `Enables verbose logging.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_VERBOSE")) _ = vip.BindPFlag("verbose", flagset.Lookup("verbose")) } diff --git a/scripts/cligen/main.go b/scripts/cligen/main.go index 58bc9d5c45248..a624b6e08c4f0 100644 --- a/scripts/cligen/main.go +++ b/scripts/cligen/main.go @@ -41,6 +41,7 @@ type Data struct { type Field struct { Key string + Env string Usage string Flag string Shorthand string @@ -129,6 +130,7 @@ func (g *Generator) generateAll() (*Data, error) { } f := Field{ Key: key, + Env: "CODER_" + strings.ReplaceAll(strings.ToUpper(key), "-", "_"), } ft, ok := field.Type.(*ast.Ident) if !ok { @@ -216,6 +218,8 @@ func (c Data) Render() string { } const deploymentConfigTemplate = `// Code generated by go generate; DO NOT EDIT. +// This file was generated by the script at scripts/cligen +// The data for populating this file is from the DeploymentConfig struct in codersdk. package deployment import ( @@ -226,6 +230,7 @@ import ( "github.com/spf13/viper" "github.com/coder/coder/codersdk" + "github.com/coder/coder/cli/cliui" ) func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { @@ -235,6 +240,7 @@ func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { func DefaultViper() *viper.Viper { v := viper.New() + v.SetEnvPrefix("coder") v.AutomaticEnv() {{- range .Fields }} {{- if .Default }} @@ -248,7 +254,7 @@ func DefaultViper() *viper.Viper { func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { {{- range .Fields }} {{- if and (.Flag) (not .Enterprise) }} - _ = flagset.{{ .Type }}P("{{ .Flag }}", "{{ .Shorthand }}", vip.Get{{ .Type }}("{{ .Key }}"), ` + "`{{ .Usage }}`" + `) + _ = flagset.{{ .Type }}P("{{ .Flag }}", "{{ .Shorthand }}", vip.Get{{ .Type }}("{{ .Key }}"), ` + "`{{ .Usage }}`" + `+"\n"+cliui.Styles.Placeholder.Render("Consumes ${{ .Env }}")) _ = vip.BindPFlag("{{ .Key }}", flagset.Lookup("{{ .Flag }}")) {{- if and .Hidden }} _ = flagset.MarkHidden("{{ .Flag }}") From a819afdd61f0d32c22b5e27a688b044f913be087 Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 17 Oct 2022 21:26:59 +0000 Subject: [PATCH 06/43] make arrays work --- cli/deployment/config.go | 16 ++++++++++++ scripts/cligen/main.go | 54 ++++++++++++++++++++++++++-------------- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 7e8d16de6fd62..80c52bb6323d9 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -6,6 +6,7 @@ package deployment import ( "os" "path/filepath" + "time" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -24,9 +25,12 @@ func DefaultViper() *viper.Viper { v.SetEnvPrefix("coder") v.AutomaticEnv() v.SetDefault("address", "127.0.0.1:3000") + v.SetDefault("autobuild_poll_interval", time.Minute) v.SetDefault("cache_dir", defaultCacheDir()) v.SetDefault("provisioner_daemon_count", 3) v.SetDefault("ssh_keygen_algorithm", "ed25519") + v.SetDefault("metrics_cache_refresh_interval", time.Hour) + v.SetDefault("agent_stat_refresh_interval", 10 * time.Minute) v.SetDefault("audit_logging", true) return v @@ -39,6 +43,9 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { _ = vip.BindPFlag("wildcard_access_url", flagset.Lookup("wildcard-access-url")) _ = flagset.StringP("address", "a", vip.GetString("address"), `Bind address of the server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_ADDRESS")) _ = vip.BindPFlag("address", flagset.Lookup("address")) + _ = flagset.DurationP("autobuild-poll-interval", "", vip.GetDuration("autobuild_poll_interval"), `Interval to poll for scheduled workspace builds.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_AUTOBUILD_POLL_INTERVAL")) + _ = vip.BindPFlag("autobuild_poll_interval", flagset.Lookup("autobuild-poll-interval")) + _ = flagset.MarkHidden("autobuild-poll-interval") _ = flagset.StringP("cache-dir", "", vip.GetString("cache_dir"), `The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_CACHE_DIR")) _ = vip.BindPFlag("cache_dir", flagset.Lookup("cache-dir")) _ = flagset.BoolP("in-memory", "", vip.GetBool("in_memory_database"), `Controls whether data will be stored in an in-memory database.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_IN_MEMORY_DATABASE")) @@ -54,6 +61,15 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { _ = vip.BindPFlag("secure_auth_cookie", flagset.Lookup("secure-auth-cookie")) _ = flagset.StringP("ssh-keygen-algorithm", "", vip.GetString("ssh_keygen_algorithm"), `The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_SSH_KEYGEN_ALGORITHM")) _ = vip.BindPFlag("ssh_keygen_algorithm", flagset.Lookup("ssh-keygen-algorithm")) + _ = flagset.StringArrayP("auto-import-template", "", vip.GetStringSlice("auto_import_templates"), `Templates to auto-import. Available auto-importable templates are: kubernetes`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_AUTO_IMPORT_TEMPLATES")) + _ = vip.BindPFlag("auto_import_templates", flagset.Lookup("auto-import-template")) + _ = flagset.MarkHidden("auto-import-template") + _ = flagset.DurationP("metrics-cache-refresh-interval", "", vip.GetDuration("metrics_cache_refresh_interval"), `How frequently metrics are refreshed`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_METRICS_CACHE_REFRESH_INTERVAL")) + _ = vip.BindPFlag("metrics_cache_refresh_interval", flagset.Lookup("metrics-cache-refresh-interval")) + _ = flagset.MarkHidden("metrics-cache-refresh-interval") + _ = flagset.DurationP("agent-stats-refresh-interval", "", vip.GetDuration("agent_stat_refresh_interval"), `How frequently agent stats are recorded`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_AGENT_STAT_REFRESH_INTERVAL")) + _ = vip.BindPFlag("agent_stat_refresh_interval", flagset.Lookup("agent-stats-refresh-interval")) + _ = flagset.MarkHidden("agent-stats-refresh-interval") _ = flagset.BoolP("verbose", "v", vip.GetBool("verbose"), `Enables verbose logging.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_VERBOSE")) _ = vip.BindPFlag("verbose", flagset.Lookup("verbose")) } diff --git a/scripts/cligen/main.go b/scripts/cligen/main.go index a624b6e08c4f0..a58010f3987fb 100644 --- a/scripts/cligen/main.go +++ b/scripts/cligen/main.go @@ -49,6 +49,7 @@ type Field struct { Enterprise bool Hidden bool Type string + ViperType string } func GenerateData(ctx context.Context, log slog.Logger, dir string) (*Data, error) { @@ -123,6 +124,7 @@ func (g *Generator) generateAll() (*Data, error) { if !ok { return nil, xerrors.Errorf("expected struct type, found %T", spec.Type) } + for _, field := range t.Fields.List { key := reflect.StructTag(strings.Trim(field.Tag.Value, "`")).Get("mapstructure") if key == "" { @@ -132,24 +134,37 @@ func (g *Generator) generateAll() (*Data, error) { Key: key, Env: "CODER_" + strings.ReplaceAll(strings.ToUpper(key), "-", "_"), } - ft, ok := field.Type.(*ast.Ident) - if !ok { - // return nil, xerrors.Errorf("expected Ident type, found %T", field.Type) - continue - } - switch ft.Name { - case "string": - f.Type = "String" - case "int": - f.Type = "Int" - case "bool": - f.Type = "Bool" - case "[]string": - f.Type = "StringArray" - case "time.Duration": + switch ft := field.Type.(type) { + case *ast.Ident: + switch ft.Name { + case "string": + f.Type = "String" + f.ViperType = "String" + case "int": + f.Type = "Int" + f.ViperType = "Int" + case "bool": + f.Type = "Bool" + f.ViperType = "Bool" + default: + continue + } + case *ast.SelectorExpr: + if ft.Sel.Name != "Duration" { + continue + } f.Type = "Duration" - default: - continue + f.ViperType = "Duration" + case *ast.ArrayType: + i, ok := ft.Elt.(*ast.Ident) + if !ok { + continue + } + if i.Name != "string" { + continue + } + f.Type = "StringArray" + f.ViperType = "StringSlice" } for _, line := range field.Doc.List { @@ -225,6 +240,7 @@ package deployment import ( "os" "path/filepath" + "time" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -254,7 +270,7 @@ func DefaultViper() *viper.Viper { func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { {{- range .Fields }} {{- if and (.Flag) (not .Enterprise) }} - _ = flagset.{{ .Type }}P("{{ .Flag }}", "{{ .Shorthand }}", vip.Get{{ .Type }}("{{ .Key }}"), ` + "`{{ .Usage }}`" + `+"\n"+cliui.Styles.Placeholder.Render("Consumes ${{ .Env }}")) + _ = flagset.{{ .Type }}P("{{ .Flag }}", "{{ .Shorthand }}", vip.Get{{ .ViperType }}("{{ .Key }}"), ` + "`{{ .Usage }}`" + `+"\n"+cliui.Styles.Placeholder.Render("Consumes ${{ .Env }}")) _ = vip.BindPFlag("{{ .Key }}", flagset.Lookup("{{ .Flag }}")) {{- if and .Hidden }} _ = flagset.MarkHidden("{{ .Flag }}") @@ -266,7 +282,7 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { func AttachEnterpriseFlags(flagset *pflag.FlagSet, vip *viper.Viper) { {{- range .Fields }} {{- if and (.Flag) (.Enterprise) }} - _ = flagset.{{ .Type }}P("{{ .Flag }}", "{{ .Shorthand }}", vip.Get{{ .Type }}("{{ .Key }}"), ` + "`{{ .Usage }}`" + `) + _ = flagset.{{ .Type }}P("{{ .Flag }}", "{{ .Shorthand }}", vip.Get{{ .ViperType }}("{{ .Key }}"), ` + "`{{ .Usage }}`" + `) _ = vip.BindPFlag("{{ .Key }}", flagset.Lookup("{{ .Flag }}")) {{- if and .Hidden }} _ = flagset.MarkHidden("{{ .Flag }}") From 976be19bffff04119e2bb76c81373c2f9820b214 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 00:50:23 +0000 Subject: [PATCH 07/43] support structs --- cli/deployment/config.go | 82 ++++++++++++++- codersdk/config.go | 4 +- scripts/cligen/main.go | 208 ++++++++++++++++++++++----------------- 3 files changed, 200 insertions(+), 94 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 80c52bb6323d9..e96333b78945b 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -4,15 +4,17 @@ package deployment import ( + "flag" "os" "path/filepath" "time" + "github.com/coreos/go-oidc/v3/oidc" "github.com/spf13/pflag" "github.com/spf13/viper" - "github.com/coder/coder/codersdk" "github.com/coder/coder/cli/cliui" + "github.com/coder/coder/codersdk" ) func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { @@ -26,8 +28,21 @@ func DefaultViper() *viper.Viper { v.AutomaticEnv() v.SetDefault("address", "127.0.0.1:3000") v.SetDefault("autobuild_poll_interval", time.Minute) + v.SetDefault("derp.server.enabled", true) + v.SetDefault("derp.server.region_id", 999) + v.SetDefault("derp.server.region_code", "coder") + v.SetDefault("derp.server.region_name", "Coder Embedded Relay") + v.SetDefault("derp.server.stun_address", []string{"stun.l.google.com:19302"}) + v.SetDefault("prometheus.address", "127.0.0.1:2112") + v.SetDefault("pprof.address", "127.0.0.1:6060") v.SetDefault("cache_dir", defaultCacheDir()) v.SetDefault("provisioner_daemon_count", 3) + v.SetDefault("oidc.allow_signups", true) + v.SetDefault("oidc.scopes", []string{oidc.ScopeOpenID, "profile", "email"}) + v.SetDefault("telemetry.enable", flag.Lookup("test.v") == nil) + v.SetDefault("telemetry.trace_enable", flag.Lookup("test.v") == nil) + v.SetDefault("telemetry.url", "https://telemetry.coder.com") + v.SetDefault("tls_config.tls_min_version", "tls12") v.SetDefault("ssh_keygen_algorithm", "ed25519") v.SetDefault("metrics_cache_refresh_interval", time.Hour) v.SetDefault("agent_stat_refresh_interval", 10 * time.Minute) @@ -46,6 +61,28 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { _ = flagset.DurationP("autobuild-poll-interval", "", vip.GetDuration("autobuild_poll_interval"), `Interval to poll for scheduled workspace builds.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_AUTOBUILD_POLL_INTERVAL")) _ = vip.BindPFlag("autobuild_poll_interval", flagset.Lookup("autobuild-poll-interval")) _ = flagset.MarkHidden("autobuild-poll-interval") + _ = flagset.BoolP("derp-server-enable", "", vip.GetBool("derp.server.enabled"), `Whether to enable or disable the embedded DERP relay server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.SERVER.ENABLED")) + _ = vip.BindPFlag("derp.server.enabled", flagset.Lookup("derp-server-enable")) + _ = flagset.IntP("derp-server-region-id", "", vip.GetInt("derp.server.region_id"), `Region ID to use for the embedded DERP server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.SERVER.REGION_ID")) + _ = vip.BindPFlag("derp.server.region_id", flagset.Lookup("derp-server-region-id")) + _ = flagset.StringP("derp-server-region-code", "", vip.GetString("derp.server.region_code"), `Region code to use for the embedded DERP server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.SERVER.REGION_CODE")) + _ = vip.BindPFlag("derp.server.region_code", flagset.Lookup("derp-server-region-code")) + _ = flagset.StringP("derp-server-region-name", "", vip.GetString("derp.server.region_name"), `Region name that for the embedded DERP server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.SERVER.REGION_NAME")) + _ = vip.BindPFlag("derp.server.region_name", flagset.Lookup("derp-server-region-name")) + _ = flagset.StringArrayP("derp-server-stun-addresses", "", vip.GetStringSlice("derp.server.stun_address"), `Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.SERVER.STUN_ADDRESS")) + _ = vip.BindPFlag("derp.server.stun_address", flagset.Lookup("derp-server-stun-addresses")) + _ = flagset.StringP("derp-config-url", "", vip.GetString("derp.config.url"), `URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.CONFIG.URL")) + _ = vip.BindPFlag("derp.config.url", flagset.Lookup("derp-config-url")) + _ = flagset.StringP("derp-config-path", "", vip.GetString("derp.config.path"), `Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.CONFIG.PATH")) + _ = vip.BindPFlag("derp.config.path", flagset.Lookup("derp-config-path")) + _ = flagset.BoolP("prometheus-enable", "", vip.GetBool("prometheus.enabled"), `Serve prometheus metrics on the address defined by prometheus address.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PROMETHEUS.ENABLED")) + _ = vip.BindPFlag("prometheus.enabled", flagset.Lookup("prometheus-enable")) + _ = flagset.StringP("prometheus-address", "", vip.GetString("prometheus.address"), `The bind address to serve prometheus metrics.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PROMETHEUS.ADDRESS")) + _ = vip.BindPFlag("prometheus.address", flagset.Lookup("prometheus-address")) + _ = flagset.BoolP("pprof-enable", "", vip.GetBool("pprof.enabled"), `Serve pprof metrics on the address defined by pprof address.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PPROF.ENABLED")) + _ = vip.BindPFlag("pprof.enabled", flagset.Lookup("pprof-enable")) + _ = flagset.StringP("pprof-address", "", vip.GetString("pprof.address"), `The bind address to serve pprof.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PPROF.ADDRESS")) + _ = vip.BindPFlag("pprof.address", flagset.Lookup("pprof-address")) _ = flagset.StringP("cache-dir", "", vip.GetString("cache_dir"), `The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_CACHE_DIR")) _ = vip.BindPFlag("cache_dir", flagset.Lookup("cache-dir")) _ = flagset.BoolP("in-memory", "", vip.GetBool("in_memory_database"), `Controls whether data will be stored in an in-memory database.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_IN_MEMORY_DATABASE")) @@ -55,6 +92,49 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { _ = vip.BindPFlag("provisioner_daemon_count", flagset.Lookup("provisioner-daemons")) _ = flagset.StringP("postgres-url", "", vip.GetString("postgres_url"), `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".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_POSTGRES_URL")) _ = vip.BindPFlag("postgres_url", flagset.Lookup("postgres-url")) + _ = flagset.StringP("oauth2-github-client-id", "", vip.GetString("oauth2_github.client_id"), `Client ID for Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB.CLIENT_ID")) + _ = vip.BindPFlag("oauth2_github.client_id", flagset.Lookup("oauth2-github-client-id")) + _ = flagset.StringP("oauth2-github-client-secret", "", vip.GetString("oauth2_github.client_secret"), `Client secret for Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB.CLIENT_SECRET")) + _ = vip.BindPFlag("oauth2_github.client_secret", flagset.Lookup("oauth2-github-client-secret")) + _ = flagset.StringArrayP("oauth2-github-allowed-orgs", "", vip.GetStringSlice("oauth2_github.allowed_organizations"), `Organizations the user must be a member of to Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB.ALLOWED_ORGANIZATIONS")) + _ = vip.BindPFlag("oauth2_github.allowed_organizations", flagset.Lookup("oauth2-github-allowed-orgs")) + _ = flagset.StringArrayP("oauth2-github-allowed-teams", "", vip.GetStringSlice("oauth2_github.allowed_teams"), `Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB.ALLOWED_TEAMS")) + _ = vip.BindPFlag("oauth2_github.allowed_teams", flagset.Lookup("oauth2-github-allowed-teams")) + _ = flagset.BoolP("oauth2-github-allow-signups", "", vip.GetBool("oauth2_github.allow_signups"), `Whether new users can sign up with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB.ALLOW_SIGNUPS")) + _ = vip.BindPFlag("oauth2_github.allow_signups", flagset.Lookup("oauth2-github-allow-signups")) + _ = flagset.StringP("oauth2-github-enterprise-base-url", "", vip.GetString("oauth2_github.enterprise_base_url"), `Base URL of a GitHub Enterprise deployment to use for Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB.ENTERPRISE_BASE_URL")) + _ = vip.BindPFlag("oauth2_github.enterprise_base_url", flagset.Lookup("oauth2-github-enterprise-base-url")) + _ = flagset.BoolP("oidc-allow-signups", "", vip.GetBool("oidc.allow_signups"), `Whether new users can sign up with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC.ALLOW_SIGNUPS")) + _ = vip.BindPFlag("oidc.allow_signups", flagset.Lookup("oidc-allow-signups")) + _ = flagset.StringP("oidc-client-id", "", vip.GetString("oidc.client_id"), `Client ID to use for Login with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC.CLIENT_ID")) + _ = vip.BindPFlag("oidc.client_id", flagset.Lookup("oidc-client-id")) + _ = flagset.StringP("oidc-client-secret", "", vip.GetString("oidc.cliet_secret"), `Client secret to use for Login with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC.CLIET_SECRET")) + _ = vip.BindPFlag("oidc.cliet_secret", flagset.Lookup("oidc-client-secret")) + _ = flagset.StringP("oidc-email-domain", "", vip.GetString("oidc.email_domain"), `Email domain that clients logging in with OIDC must match.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC.EMAIL_DOMAIN")) + _ = vip.BindPFlag("oidc.email_domain", flagset.Lookup("oidc-email-domain")) + _ = flagset.StringP("oidc-issuer-url", "", vip.GetString("oidc.issuer_url"), `Issuer URL to use for Login with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC.ISSUER_URL")) + _ = vip.BindPFlag("oidc.issuer_url", flagset.Lookup("oidc-issuer-url")) + _ = flagset.StringArrayP("oidc-scopes", "", vip.GetStringSlice("oidc.scopes"), `Scopes to grant when authenticating with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC.SCOPES")) + _ = vip.BindPFlag("oidc.scopes", flagset.Lookup("oidc-scopes")) + _ = flagset.BoolP("telemetry", "", vip.GetBool("telemetry.enable"), `Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TELEMETRY.ENABLE")) + _ = vip.BindPFlag("telemetry.enable", flagset.Lookup("telemetry")) + _ = flagset.BoolP("telemetry-trace", "", vip.GetBool("telemetry.trace_enable"), `Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TELEMETRY.TRACE_ENABLE")) + _ = vip.BindPFlag("telemetry.trace_enable", flagset.Lookup("telemetry-trace")) + _ = flagset.StringP("telemetry-url", "", vip.GetString("telemetry.url"), `URL to send telemetry.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TELEMETRY.URL")) + _ = vip.BindPFlag("telemetry.url", flagset.Lookup("telemetry-url")) + _ = flagset.MarkHidden("telemetry-url") + _ = flagset.BoolP("tls-enable", "", vip.GetBool("tls_config.tls_enable"), `Whether TLS will be enabled.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG.TLS_ENABLE")) + _ = vip.BindPFlag("tls_config.tls_enable", flagset.Lookup("tls-enable")) + _ = flagset.StringArrayP("tls-cert-file", "", vip.GetStringSlice("tls_config.tls_cert_files"), `Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG.TLS_CERT_FILES")) + _ = vip.BindPFlag("tls_config.tls_cert_files", flagset.Lookup("tls-cert-file")) + _ = flagset.StringP("tls-client-ca-file", "", vip.GetString("tls_config.tls_client_ca_file"), `PEM-encoded Certificate Authority file used for checking the authenticity of client`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG.TLS_CLIENT_CA_FILE")) + _ = vip.BindPFlag("tls_config.tls_client_ca_file", flagset.Lookup("tls-client-ca-file")) + _ = flagset.StringP("tls-client-auth", "", vip.GetString("tls_config.tls_client_auth"), `Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG.TLS_CLIENT_AUTH")) + _ = vip.BindPFlag("tls_config.tls_client_auth", flagset.Lookup("tls-client-auth")) + _ = flagset.StringArrayP("tls-key-file", "", vip.GetStringSlice("tls_config.tls_key_tiles"), `Paths to the private keys for each of the certificates. It requires a PEM-encoded file.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG.TLS_KEY_TILES")) + _ = vip.BindPFlag("tls_config.tls_key_tiles", flagset.Lookup("tls-key-file")) + _ = flagset.StringP("tls-min-version", "", vip.GetString("tls_config.tls_min_version"), `Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13"`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG.TLS_MIN_VERSION")) + _ = vip.BindPFlag("tls_config.tls_min_version", flagset.Lookup("tls-min-version")) _ = flagset.BoolP("trace", "", vip.GetBool("trace_enable"), `Whether application tracing data is collected.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TRACE_ENABLE")) _ = vip.BindPFlag("trace_enable", flagset.Lookup("trace")) _ = flagset.BoolP("secure-auth-cookie", "", vip.GetBool("secure_auth_cookie"), `Controls if the 'Secure' property is set on browser session cookies.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_SECURE_AUTH_COOKIE")) diff --git a/codersdk/config.go b/codersdk/config.go index c535440b4e34a..97b3bfed84967 100644 --- a/codersdk/config.go +++ b/codersdk/config.go @@ -99,6 +99,7 @@ type DERPConfig struct { Server DERPServerConfig `mapstructure:"server"` Config DERPConfigConfig `mapstructure:"config"` } + type DERPServerConfig struct { // Usage: Whether to enable or disable the embedded DERP relay server. // Flag: derp-server-enable @@ -210,7 +211,6 @@ type TelemetryConfig struct { // Default: "https://telemetry.coder.com" URL string `mapstructure:"url"` } - type TLSConfig struct { // Usage: Whether TLS will be enabled. // Flag: tls-enable @@ -229,7 +229,7 @@ type TLSConfig struct { KeyFiles []string `mapstructure:"tls_key_tiles"` // Usage: Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" // Flag: tls-min-version - // Default: tls12 + // Default: "tls12" MinVersion string `mapstructure:"tls_min_version"` } diff --git a/scripts/cligen/main.go b/scripts/cligen/main.go index a58010f3987fb..b9080c285999e 100644 --- a/scripts/cligen/main.go +++ b/scripts/cligen/main.go @@ -103,6 +103,7 @@ func (g *Generator) parsePackage(ctx context.Context, patterns ...string) error func (g *Generator) generateAll() (*Data, error) { cb := Data{} + structs := make(map[string]*ast.StructType) for _, file := range g.pkg.Syntax { for _, decl := range file.Decls { decl, ok := decl.(*ast.GenDecl) @@ -117,105 +118,128 @@ func (g *Generator) generateAll() (*Data, error) { if !ok { continue } - if spec.Name.Name != "DeploymentConfig" { + t, ok := spec.Type.(*ast.StructType) + if !ok { continue } - t, ok := spec.Type.(*ast.StructType) + structs[spec.Name.Name] = t + } + } + } + + cb.Fields = handleStruct("", "DeploymentConfig", structs, cb.Fields) + + return &cb, nil +} + +func handleStruct(prefix string, target string, structs map[string]*ast.StructType, fields []Field) []Field { + var dc *ast.StructType + for name, t := range structs { + if name == target { + dc = t + break + } + } + if dc == nil { + return fields + } + for _, field := range dc.Fields.List { + key := reflect.StructTag(strings.Trim(field.Tag.Value, "`")).Get("mapstructure") + if key == "" { + continue + } + if prefix != "" { + key = fmt.Sprintf("%s.%s", prefix, key) + } + f := Field{ + Key: key, + Env: "CODER_" + strings.ReplaceAll(strings.ToUpper(key), "-", "_"), + } + switch ft := field.Type.(type) { + case *ast.Ident: + switch ft.Name { + case "string": + f.Type = "String" + f.ViperType = "String" + case "int": + f.Type = "Int" + f.ViperType = "Int" + case "bool": + f.Type = "Bool" + f.ViperType = "Bool" + default: + _, ok := structs[ft.Name] if !ok { - return nil, xerrors.Errorf("expected struct type, found %T", spec.Type) + continue } + fields = handleStruct(key, ft.Name, structs, fields) + continue + } + case *ast.SelectorExpr: + if ft.Sel.Name != "Duration" { + continue + } + f.Type = "Duration" + f.ViperType = "Duration" + case *ast.ArrayType: + i, ok := ft.Elt.(*ast.Ident) + if !ok { + continue + } + if i.Name != "string" { + continue + } + f.Type = "StringArray" + f.ViperType = "StringSlice" + default: + continue + } - for _, field := range t.Fields.List { - key := reflect.StructTag(strings.Trim(field.Tag.Value, "`")).Get("mapstructure") - if key == "" { - continue - } - f := Field{ - Key: key, - Env: "CODER_" + strings.ReplaceAll(strings.ToUpper(key), "-", "_"), - } - switch ft := field.Type.(type) { - case *ast.Ident: - switch ft.Name { - case "string": - f.Type = "String" - f.ViperType = "String" - case "int": - f.Type = "Int" - f.ViperType = "Int" - case "bool": - f.Type = "Bool" - f.ViperType = "Bool" - default: - continue - } - case *ast.SelectorExpr: - if ft.Sel.Name != "Duration" { - continue - } - f.Type = "Duration" - f.ViperType = "Duration" - case *ast.ArrayType: - i, ok := ft.Elt.(*ast.Ident) - if !ok { - continue - } - if i.Name != "string" { - continue - } - f.Type = "StringArray" - f.ViperType = "StringSlice" - } - - for _, line := range field.Doc.List { - if strings.HasPrefix(line.Text, "// Usage:") { - v := strings.TrimPrefix(line.Text, "// Usage:") - v = strings.TrimSpace(v) - f.Usage = v - } - if strings.HasPrefix(line.Text, "// Flag:") { - v := strings.TrimPrefix(line.Text, "// Flag:") - v = strings.TrimSpace(v) - f.Flag = v - } - if strings.HasPrefix(line.Text, "// Shorthand:") { - v := strings.TrimPrefix(line.Text, "// Shorthand:") - v = strings.TrimSpace(v) - f.Shorthand = v - } - if strings.HasPrefix(line.Text, "// Default:") { - v := strings.TrimPrefix(line.Text, "// Default:") - v = strings.TrimSpace(v) - f.Default = v - } - if strings.HasPrefix(line.Text, "// Enterprise:") { - v := strings.TrimPrefix(line.Text, "// Enterprise:") - v = strings.TrimSpace(v) - b, err := strconv.ParseBool(v) - if err != nil { - return nil, xerrors.Errorf("parse enterprise: %w", err) - } - f.Enterprise = b - } - if strings.HasPrefix(line.Text, "// Hidden:") { - v := strings.TrimPrefix(line.Text, "// Hidden:") - v = strings.TrimSpace(v) - v = strings.TrimSpace(v) - b, err := strconv.ParseBool(v) - if err != nil { - return nil, xerrors.Errorf("parse hidden: %w", err) - } - f.Hidden = b - } - } - - cb.Fields = append(cb.Fields, f) + for _, line := range field.Doc.List { + if strings.HasPrefix(line.Text, "// Usage:") { + v := strings.TrimPrefix(line.Text, "// Usage:") + v = strings.TrimSpace(v) + f.Usage = v + } + if strings.HasPrefix(line.Text, "// Flag:") { + v := strings.TrimPrefix(line.Text, "// Flag:") + v = strings.TrimSpace(v) + f.Flag = v + } + if strings.HasPrefix(line.Text, "// Shorthand:") { + v := strings.TrimPrefix(line.Text, "// Shorthand:") + v = strings.TrimSpace(v) + f.Shorthand = v + } + if strings.HasPrefix(line.Text, "// Default:") { + v := strings.TrimPrefix(line.Text, "// Default:") + v = strings.TrimSpace(v) + f.Default = v + } + if strings.HasPrefix(line.Text, "// Enterprise:") { + v := strings.TrimPrefix(line.Text, "// Enterprise:") + v = strings.TrimSpace(v) + b, err := strconv.ParseBool(v) + if err != nil { + continue + } + f.Enterprise = b + } + if strings.HasPrefix(line.Text, "// Hidden:") { + v := strings.TrimPrefix(line.Text, "// Hidden:") + v = strings.TrimSpace(v) + v = strings.TrimSpace(v) + b, err := strconv.ParseBool(v) + if err != nil { + continue } + f.Hidden = b } } - } - return &cb, nil + fields = append(fields, f) + } + return fields } func (c Data) Render() string { @@ -238,15 +262,17 @@ const deploymentConfigTemplate = `// Code generated by go generate; DO NOT EDIT. package deployment import ( + "flag" "os" "path/filepath" "time" + "github.com/coreos/go-oidc/v3/oidc" "github.com/spf13/pflag" "github.com/spf13/viper" - "github.com/coder/coder/codersdk" "github.com/coder/coder/cli/cliui" + "github.com/coder/coder/codersdk" ) func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { From ddfd59fe54d2aa0d479973182e668c284d7f40c0 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 01:30:25 +0000 Subject: [PATCH 08/43] rename --- codersdk/config.go | 216 +++++++++--------- scripts/{cligen => deploymentconfig}/main.go | 60 ++--- .../{cligen => deploymentconfig}/main_test.go | 6 +- 3 files changed, 132 insertions(+), 150 deletions(-) rename scripts/{cligen => deploymentconfig}/main.go (82%) rename scripts/{cligen => deploymentconfig}/main_test.go (66%) diff --git a/codersdk/config.go b/codersdk/config.go index 97b3bfed84967..790fb38d58565 100644 --- a/codersdk/config.go +++ b/codersdk/config.go @@ -16,82 +16,82 @@ type DeploymentConfig struct { // Usage: Specifies the wildcard hostname to use for workspace applications in the form "*.example.com". // Flag: wildcard-access-url WildcardAccessURL string `mapstructure:"wildcard_access_url"` - // Usage: Bind address of the server. - // Flag: address - // Shorthand: a - // Default: "127.0.0.1:3000" + // Usage: Bind address of the server. + // Flag: address + // Shorthand: a + // Default: "127.0.0.1:3000" Address string `mapstructure:"address"` - // Usage: Interval to poll for scheduled workspace builds. - // Flag: autobuild-poll-interval - // Hidden: true - // Default: time.Minute + // Usage: Interval to poll for scheduled workspace builds. + // Flag: autobuild-poll-interval + // Hidden: true + // Default: time.Minute AutobuildPollInterval time.Duration `mapstructure:"autobuild_poll_interval"` DERP DERPConfig `mapstructure:"derp"` Prometheus PrometheusConfig `mapstructure:"prometheus"` Pprof PprofConfig `mapstructure:"pprof"` - // Usage: The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. - // Flag: cache-dir - // Default: defaultCacheDir() + // Usage: The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. + // Flag: cache-dir + // Default: defaultCacheDir() CacheDir string `mapstructure:"cache_dir"` - // Usage: Controls whether data will be stored in an in-memory database. - // Flag: in-memory - // Hidden: true + // Usage: Controls whether data will be stored in an in-memory database. + // Flag: in-memory + // Hidden: true InMemoryDatabase bool `mapstructure:"in_memory_database"` - // Usage: Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. - // Flag: provisioner-daemons - // Default: 3 + // Usage: Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. + // Flag: provisioner-daemons + // Default: 3 ProvisionerDaemonCount int `mapstructure:"provisioner_daemon_count"` // Usage: 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". - // Flag: postgres-url + // Flag: postgres-url PostgresURL string `mapstructure:"postgres_url"` OAuth2Github OAuth2GithubConfig `mapstructure:"oauth2_github"` OIDC OIDCConfig `mapstructure:"oidc"` Telemetry TelemetryConfig `mapstructure:"telemetry"` TLS TLSConfig `mapstructure:"tls_config"` // Usage: Whether application tracing data is collected. - // Flag: trace + // Flag: trace TraceEnable bool `mapstructure:"trace_enable"` // Usage: Controls if the 'Secure' property is set on browser session cookies. - // Flag: secure-auth-cookie + // Flag: secure-auth-cookie SecureAuthCookie bool `mapstructure:"secure_auth_cookie"` - // Usage: The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". - // Flag: ssh-keygen-algorithm + // Usage: The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". + // Flag: ssh-keygen-algorithm // Default: "ed25519" SSHKeygenAlgorithm string `mapstructure:"ssh_keygen_algorithm"` - // Usage: Templates to auto-import. Available auto-importable templates are: kubernetes - // Flag: auto-import-template - // Hidden: true + // Usage: Templates to auto-import. Available auto-importable templates are: kubernetes + // Flag: auto-import-template + // Hidden: true AutoImportTemplates []string `mapstructure:"auto_import_templates"` - // Usage: How frequently metrics are refreshed - // Flag: metrics-cache-refresh-interval - // Hidden: true - // Default: time.Hour + // Usage: How frequently metrics are refreshed + // Flag: metrics-cache-refresh-interval + // Hidden: true + // Default: time.Hour MetricsCacheRefreshInterval time.Duration `mapstructure:"metrics_cache_refresh_interval"` - // Usage: How frequently agent stats are recorded - // Flag: agent-stats-refresh-interval - // Hidden: true - // Default: 10 * time.Minute + // Usage: How frequently agent stats are recorded + // Flag: agent-stats-refresh-interval + // Hidden: true + // Default: 10 * time.Minute AgentStatRefreshInterval time.Duration `mapstructure:"agent_stat_refresh_interval"` - // Usage: Enables verbose logging. - // Flag: verbose - // Shorthand: v + // Usage: Enables verbose logging. + // Flag: verbose + // Shorthand: v Verbose bool `mapstructure:"verbose"` - // Usage: Specifies whether audit logging is enabled. - // Flag: audit-logging - // Default: true - // Enterprise: true + // Usage: Specifies whether audit logging is enabled. + // Flag: audit-logging + // Default: true + // Enterprise: true AuditLogging bool `mapstructure:"audit_logging"` - // Usage: Whether Coder only allows connections to workspaces via the browser. - // Flag: browser-only - // Enterprise: true + // Usage: Whether Coder only allows connections to workspaces via the browser. + // Flag: browser-only + // Enterprise: true BrowserOnly bool `mapstructure:"browser_only"` - // Usage: Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. - // Flag: scim-auth-header - // Enterprise: true + // Usage: Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. + // Flag: scim-auth-header + // Enterprise: true SCIMAuthHeader string `mapstructure:"scim_auth_header"` - // Usage: Enables and sets a limit on how many workspaces each user can create. - // Flag: user-workspace-quota - // Enterprise: true + // Usage: Enables and sets a limit on how many workspaces each user can create. + // Flag: user-workspace-quota + // Enterprise: true UserWorkspaceQuota int `mapstructure:"user_workspace_quota"` } @@ -101,135 +101,135 @@ type DERPConfig struct { } type DERPServerConfig struct { - // Usage: Whether to enable or disable the embedded DERP relay server. - // Flag: derp-server-enable - // Default: true + // Usage: Whether to enable or disable the embedded DERP relay server. + // Flag: derp-server-enable + // Default: true Enable bool `mapstructure:"enabled"` - // Usage: Region ID to use for the embedded DERP server. - // Flag: derp-server-region-id - // Default: 999 + // Usage: Region ID to use for the embedded DERP server. + // Flag: derp-server-region-id + // Default: 999 RegionID int `mapstructure:"region_id"` - // Usage: Region code to use for the embedded DERP server. - // Flag: derp-server-region-code - // Default: "coder" + // Usage: Region code to use for the embedded DERP server. + // Flag: derp-server-region-code + // Default: "coder" RegionCode string `mapstructure:"region_code"` - // Usage: Region name that for the embedded DERP server. - // Flag: derp-server-region-name - // Default: "Coder Embedded Relay" + // Usage: Region name that for the embedded DERP server. + // Flag: derp-server-region-name + // Default: "Coder Embedded Relay" RegionName string `mapstructure:"region_name"` - // Usage: Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections. - // Flag: derp-server-stun-addresses - // Default: []string{"stun.l.google.com:19302"} + // Usage: Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections. + // Flag: derp-server-stun-addresses + // Default: []string{"stun.l.google.com:19302"} STUNAddresses []string `mapstructure:"stun_address"` } type DERPConfigConfig struct { // Usage: URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/ - // Flag: derp-config-url + // Flag: derp-config-url URL string `mapstructure:"url"` // Usage: Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/ - // Flag: derp-config-path + // Flag: derp-config-path Path string `mapstructure:"path"` } type PrometheusConfig struct { // Usage: Serve prometheus metrics on the address defined by prometheus address. - // Flag: prometheus-enable + // Flag: prometheus-enable Enable bool `mapstructure:"enabled"` - // Usage: The bind address to serve prometheus metrics. - // Flag: prometheus-address - // Default: "127.0.0.1:2112" + // Usage: The bind address to serve prometheus metrics. + // Flag: prometheus-address + // Default: "127.0.0.1:2112" Address string `mapstructure:"address"` } type PprofConfig struct { // Usage: Serve pprof metrics on the address defined by pprof address. - // Flag: pprof-enable + // Flag: pprof-enable Enable bool `mapstructure:"enabled"` - // Usage: The bind address to serve pprof. - // Flag: pprof-address - // Default: "127.0.0.1:6060" + // Usage: The bind address to serve pprof. + // Flag: pprof-address + // Default: "127.0.0.1:6060" Address string `mapstructure:"address"` } type OAuth2GithubConfig struct { // Usage: Client ID for Login with GitHub. - // Flag: oauth2-github-client-id + // Flag: oauth2-github-client-id ClientID string `mapstructure:"client_id"` // Usage: Client secret for Login with GitHub. - // Flag: oauth2-github-client-secret + // Flag: oauth2-github-client-secret ClientSecret string `mapstructure:"client_secret"` // Usage: Organizations the user must be a member of to Login with GitHub. - // Flag: oauth2-github-allowed-orgs + // Flag: oauth2-github-allowed-orgs AllowedOrganizations []string `mapstructure:"allowed_organizations"` // Usage: Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /. - // Flag: oauth2-github-allowed-teams + // Flag: oauth2-github-allowed-teams AllowedTeams []string `mapstructure:"allowed_teams"` // Usage: Whether new users can sign up with GitHub. - // Flag: oauth2-github-allow-signups + // Flag: oauth2-github-allow-signups AllowSignups bool `mapstructure:"allow_signups"` // Usage: Base URL of a GitHub Enterprise deployment to use for Login with GitHub. - // Flag: oauth2-github-enterprise-base-url + // Flag: oauth2-github-enterprise-base-url EnterpriseBaseURL string `mapstructure:"enterprise_base_url"` } type OIDCConfig struct { - // Usage: Whether new users can sign up with OIDC. - // Flag: oidc-allow-signups - // Default: true + // Usage: Whether new users can sign up with OIDC. + // Flag: oidc-allow-signups + // Default: true AllowSignups bool `mapstructure:"allow_signups"` // Usage: Client ID to use for Login with OIDC. - // Flag: oidc-client-id + // Flag: oidc-client-id ClientID string `mapstructure:"client_id"` // Usage: Client secret to use for Login with OIDC. - // Flag: oidc-client-secret + // Flag: oidc-client-secret ClientSecret string `mapstructure:"cliet_secret"` // Usage: Email domain that clients logging in with OIDC must match. - // Flag: oidc-email-domain + // Flag: oidc-email-domain EmailDomain string `mapstructure:"email_domain"` // Usage: Issuer URL to use for Login with OIDC. - // Flag: oidc-issuer-url + // Flag: oidc-issuer-url IssuerURL string `mapstructure:"issuer_url"` - // Usage: Scopes to grant when authenticating with OIDC. - // Flag: oidc-scopes - // Default: []string{oidc.ScopeOpenID, "profile", "email"} + // Usage: Scopes to grant when authenticating with OIDC. + // Flag: oidc-scopes + // Default: []string{oidc.ScopeOpenID, "profile", "email"} Scopes []string `mapstructure:"scopes"` } type TelemetryConfig struct { - // Usage: Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product. - // Flag: telemetry - // Default: flag.Lookup("test.v") == nil + // Usage: Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product. + // Flag: telemetry + // Default: flag.Lookup("test.v") == nil Enable bool `mapstructure:"enable"` - // Usage: Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option. - // Flag: telemetry-trace - // Default: flag.Lookup("test.v") == nil + // Usage: Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option. + // Flag: telemetry-trace + // Default: flag.Lookup("test.v") == nil TraceEnable bool `mapstructure:"trace_enable"` - // Usage: URL to send telemetry. - // Flag: telemetry-url - // Hidden: true - // Default: "https://telemetry.coder.com" + // Usage: URL to send telemetry. + // Flag: telemetry-url + // Hidden: true + // Default: "https://telemetry.coder.com" URL string `mapstructure:"url"` } type TLSConfig struct { // Usage: Whether TLS will be enabled. - // Flag: tls-enable + // Flag: tls-enable Enable bool `mapstructure:"tls_enable"` // Usage: Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file. - // Flag: tls-cert-file + // Flag: tls-cert-file CertFiles []string `mapstructure:"tls_cert_files"` // Usage: PEM-encoded Certificate Authority file used for checking the authenticity of client - // Flag: tls-client-ca-file + // Flag: tls-client-ca-file ClientCAFile string `mapstructure:"tls_client_ca_file"` // Usage: Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". - // Flag: tls-client-auth + // Flag: tls-client-auth ClientAuth string `mapstructure:"tls_client_auth"` // Usage: Paths to the private keys for each of the certificates. It requires a PEM-encoded file. - // Flag: tls-key-file + // Flag: tls-key-file KeyFiles []string `mapstructure:"tls_key_tiles"` - // Usage: Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" - // Flag: tls-min-version - // Default: "tls12" + // Usage: Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" + // Flag: tls-min-version + // Default: "tls12" MinVersion string `mapstructure:"tls_min_version"` } diff --git a/scripts/cligen/main.go b/scripts/deploymentconfig/main.go similarity index 82% rename from scripts/cligen/main.go rename to scripts/deploymentconfig/main.go index b9080c285999e..d294ce164d172 100644 --- a/scripts/cligen/main.go +++ b/scripts/deploymentconfig/main.go @@ -6,7 +6,6 @@ import ( "fmt" "go/ast" "go/token" - "os" "reflect" "strconv" "strings" @@ -14,29 +13,26 @@ import ( "golang.org/x/tools/go/packages" "golang.org/x/xerrors" - - "cdr.dev/slog" - "cdr.dev/slog/sloggers/sloghuman" ) const ( - PkgDir = "./codersdk" + StructTarget = "DeploymentConfig" + EnvPrefix = "CODER_" ) func main() { - ctx := context.Background() - log := slog.Make(sloghuman.Sink(os.Stderr)) - data, err := GenerateData(ctx, log, PkgDir) + data, err := GenerateData(context.Background(), "./codersdk") if err != nil { - log.Fatal(ctx, err.Error()) + panic(err.Error()) } // Just cat the output to a file to capture it - _, _ = fmt.Println(data.Render()) + _, _ = fmt.Println(Render(data)) } type Data struct { - Fields []Field + StructTarget string + Fields []Field } type Field struct { @@ -52,16 +48,13 @@ type Field struct { ViperType string } -func GenerateData(ctx context.Context, log slog.Logger, dir string) (*Data, error) { - g := Generator{ - log: log, - } - err := g.parsePackage(ctx, dir) +func GenerateData(ctx context.Context, dir string) (*Data, error) { + pkg, err := parsePackage(ctx, dir) if err != nil { return nil, xerrors.Errorf("parse package %q: %w", dir, err) } - codeBlocks, err := g.generateAll() + codeBlocks, err := generateAll(pkg) if err != nil { return nil, xerrors.Errorf("parse package %q: %w", dir, err) } @@ -69,14 +62,8 @@ func GenerateData(ctx context.Context, log slog.Logger, dir string) (*Data, erro return codeBlocks, nil } -type Generator struct { - // Package we are scanning. - pkg *packages.Package - log slog.Logger -} - // parsePackage takes a list of patterns such as a directory, and parses them. -func (g *Generator) parsePackage(ctx context.Context, patterns ...string) error { +func parsePackage(ctx context.Context, patterns ...string) (*packages.Package, error) { cfg := &packages.Config{ // Just accept the fact we need these flags for what we want. Feel free to add // more, it'll just increase the time it takes to parse. @@ -88,23 +75,22 @@ func (g *Generator) parsePackage(ctx context.Context, patterns ...string) error pkgs, err := packages.Load(cfg, patterns...) if err != nil { - return xerrors.Errorf("load package: %w", err) + return nil, xerrors.Errorf("load package: %w", err) } // Only support 1 package for now. We can expand it if we need later, we // just need to hook up multiple packages in the generator. if len(pkgs) != 1 { - return xerrors.Errorf("expected 1 package, found %d", len(pkgs)) + return nil, xerrors.Errorf("expected 1 package, found %d", len(pkgs)) } - g.pkg = pkgs[0] - return nil + return pkgs[0], nil } -func (g *Generator) generateAll() (*Data, error) { +func generateAll(pkg *packages.Package) (*Data, error) { cb := Data{} structs := make(map[string]*ast.StructType) - for _, file := range g.pkg.Syntax { + for _, file := range pkg.Syntax { for _, decl := range file.Decls { decl, ok := decl.(*ast.GenDecl) if !ok { @@ -127,7 +113,7 @@ func (g *Generator) generateAll() (*Data, error) { } } - cb.Fields = handleStruct("", "DeploymentConfig", structs, cb.Fields) + cb.Fields = handleStruct("", StructTarget, structs, cb.Fields) return &cb, nil } @@ -153,7 +139,7 @@ func handleStruct(prefix string, target string, structs map[string]*ast.StructTy } f := Field{ Key: key, - Env: "CODER_" + strings.ReplaceAll(strings.ToUpper(key), "-", "_"), + Env: EnvPrefix + strings.ReplaceAll(strings.ToUpper(key), "-", "_"), } switch ft := field.Type.(type) { case *ast.Ident: @@ -242,13 +228,13 @@ func handleStruct(prefix string, target string, structs map[string]*ast.StructTy return fields } -func (c Data) Render() string { - t, err := template.New("DeploymentConfig").Parse(deploymentConfigTemplate) +func Render(d *Data) string { + t, err := template.New(StructTarget).Parse(deploymentConfigTemplate) if err != nil { panic(err) } var b bytes.Buffer - err = t.Execute(&b, c) + err = t.Execute(&b, d) if err != nil { panic(err) } @@ -257,8 +243,8 @@ func (c Data) Render() string { } const deploymentConfigTemplate = `// Code generated by go generate; DO NOT EDIT. -// This file was generated by the script at scripts/cligen -// The data for populating this file is from the DeploymentConfig struct in codersdk. +// This file was generated by the script at scripts/deploymentconfig/main.go +// The data for populating this file is from codersdk.{{ .StructTarget }}. package deployment import ( diff --git a/scripts/cligen/main_test.go b/scripts/deploymentconfig/main_test.go similarity index 66% rename from scripts/cligen/main_test.go rename to scripts/deploymentconfig/main_test.go index 05bb4338b032b..c3eeea0028326 100644 --- a/scripts/cligen/main_test.go +++ b/scripts/deploymentconfig/main_test.go @@ -2,13 +2,10 @@ package main import ( "context" - "os" "testing" "github.com/stretchr/testify/require" - "cdr.dev/slog" - "cdr.dev/slog/sloggers/sloghuman" "github.com/coder/coder/testutil" ) @@ -16,8 +13,7 @@ func TestCliGen(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() - log := slog.Make(sloghuman.Sink(os.Stderr)) - cb, err := GenerateData(ctx, log, "../../codersdk") + cb, err := GenerateData(ctx, "../../codersdk") require.NoError(t, err) require.NotNil(t, cb) } From 817704cec3f12f64ac3f96e331bff1eedfe3bfe5 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 01:43:09 +0000 Subject: [PATCH 09/43] fix --- cli/deployment/flags.go | 511 -------------------------------- cli/server.go | 1 + coderd/coderdtest/coderdtest.go | 11 +- 3 files changed, 2 insertions(+), 521 deletions(-) delete mode 100644 cli/deployment/flags.go diff --git a/cli/deployment/flags.go b/cli/deployment/flags.go deleted file mode 100644 index 0b5baf3028552..0000000000000 --- a/cli/deployment/flags.go +++ /dev/null @@ -1,511 +0,0 @@ -package deployment - -import ( - "flag" - "fmt" - "os" - "path/filepath" - "reflect" - "time" - - "github.com/coreos/go-oidc/v3/oidc" - "github.com/spf13/pflag" - - "github.com/coder/coder/cli/cliflag" - "github.com/coder/coder/cli/cliui" - "github.com/coder/coder/codersdk" -) - -const ( - secretValue = "********" -) - -func Flags() *codersdk.DeploymentFlags { - return &codersdk.DeploymentFlags{ - AccessURL: &codersdk.Flag[string]{ - 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.Flag[string]{ - 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" or "*-suffix.example.com". Ports or schemes should not be included. The scheme will be copied from the access URL.`, - }, - Address: &codersdk.Flag[string]{ - Name: "Bind Address", - Flag: "address", - EnvVar: "CODER_ADDRESS", - Shorthand: "a", - Description: "Bind address of the server.", - Default: "127.0.0.1:3000", - }, - AutobuildPollInterval: &codersdk.Flag[time.Duration]{ - 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.Flag[bool]{ - 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.Flag[int]{ - 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.Flag[string]{ - 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.Flag[string]{ - 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.Flag[[]string]{ - 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"}, - }, - DerpServerRelayAddress: &codersdk.StringFlag{ - Name: "DERP Server Relay Address", - Flag: "derp-server-relay-address", - EnvVar: "CODER_DERP_SERVER_RELAY_URL", - Description: "An HTTP address that is accessible by other replicas to relay DERP traffic. Required for high availability.", - Enterprise: true, - }, - 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.Flag[string]{ - 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.Flag[bool]{ - Name: "Prometheus Enabled", - Flag: "prometheus-enable", - EnvVar: "CODER_PROMETHEUS_ENABLE", - Description: "Serve prometheus metrics on the address defined by `prometheus-address`.", - }, - PromAddress: &codersdk.Flag[string]{ - 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.Flag[bool]{ - Name: "pprof Enabled", - Flag: "pprof-enable", - EnvVar: "CODER_PPROF_ENABLE", - Description: "Serve pprof metrics on the address defined by `pprof-address`.", - }, - PprofAddress: &codersdk.Flag[string]{ - 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.Flag[string]{ - 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.Flag[bool]{ - 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.Flag[int]{ - 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.Flag[string]{ - 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.Flag[string]{ - 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.Flag[string]{ - 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.Flag[[]string]{ - 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.Flag[[]string]{ - 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.Flag[bool]{ - 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.Flag[string]{ - 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.Flag[bool]{ - 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.Flag[string]{ - Name: "OIDC Client ID", - Flag: "oidc-client-id", - EnvVar: "CODER_OIDC_CLIENT_ID", - Description: "Client ID to use for Login with OIDC.", - }, - OIDCClientSecret: &codersdk.Flag[string]{ - 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.Flag[string]{ - 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.Flag[string]{ - Name: "OIDC Issuer URL", - Flag: "oidc-issuer-url", - EnvVar: "CODER_OIDC_ISSUER_URL", - Description: "Issuer URL to use for Login with OIDC.", - }, - OIDCScopes: &codersdk.Flag[[]string]{ - 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.Flag[bool]{ - 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.Flag[bool]{ - Name: "Trace Telemetry Enabled", - Flag: "telemetry-trace", - EnvVar: "CODER_TELEMETRY_TRACE", - Shorthand: "", - 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.Flag[string]{ - Name: "Telemetry URL", - Flag: "telemetry-url", - EnvVar: "CODER_TELEMETRY_URL", - Description: "URL to send telemetry.", - Hidden: true, - Default: "https://telemetry.coder.com", - }, - TLSEnable: &codersdk.Flag[bool]{ - Name: "TLS Enabled", - Flag: "tls-enable", - EnvVar: "CODER_TLS_ENABLE", - Description: "Whether TLS will be enabled.", - }, - TLSCertFiles: &codersdk.Flag[[]string]{ - Name: "TLS Cert Files", - Flag: "tls-cert-file", - EnvVar: "CODER_TLS_CERT_FILE", - Description: "Path to each certificate for TLS. It requires a PEM-encoded file. " + - "To configure the listener to use a CA certificate, concatenate the primary certificate " + - "and the CA certificate together. The primary certificate should appear first in the combined file.", - Default: []string{}, - }, - TLSClientCAFile: &codersdk.Flag[string]{ - 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.Flag[string]{ - Name: "TLS Client Auth", - Flag: "tls-client-auth", - EnvVar: "CODER_TLS_CLIENT_AUTH", - Description: `Policy the server will follow for TLS Client Authentication. ` + - `Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify"`, - Default: "request", - }, - TLSKeyFiles: &codersdk.Flag[[]string]{ - 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.Flag[string]{ - 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.Flag[bool]{ - Name: "Trace Enabled", - Flag: "trace", - EnvVar: "CODER_TRACE", - Description: "Whether application tracing data is collected.", - }, - SecureAuthCookie: &codersdk.Flag[bool]{ - 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.Flag[string]{ - Name: "SSH Keygen Algorithm", - Flag: "ssh-keygen-algorithm", - EnvVar: "CODER_SSH_KEYGEN_ALGORITHM", - Description: "The algorithm to use for generating ssh keys. " + - `Accepted values are "ed25519", "ecdsa", or "rsa4096"`, - Default: "ed25519", - }, - AutoImportTemplates: &codersdk.Flag[[]string]{ - 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.Flag[time.Duration]{ - 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.Flag[time.Duration]{ - 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.Flag[bool]{ - Name: "Verbose Logging", - Flag: "verbose", - EnvVar: "CODER_VERBOSE", - Shorthand: "v", - Description: "Enables verbose logging.", - }, - AuditLogging: &codersdk.Flag[bool]{ - Name: "Audit Logging", - Flag: "audit-logging", - EnvVar: "CODER_AUDIT_LOGGING", - Description: "Specifies whether audit logging is enabled.", - Default: true, - Enterprise: true, - }, - BrowserOnly: &codersdk.Flag[bool]{ - 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.Flag[string]{ - Name: "SCIM Authentication Header", - Flag: "scim-auth-header", - EnvVar: "CODER_SCIM_API_KEY", - Description: "Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication.", - Secret: true, - Enterprise: true, - }, - UserWorkspaceQuota: &codersdk.Flag[int]{ - Name: "User Workspace Quota", - Flag: "user-workspace-quota", - EnvVar: "CODER_USER_WORKSPACE_QUOTA", - Description: "Enables and sets a limit on how many workspaces each user can create.", - Default: 0, - Enterprise: true, - }, - } -} - -func RemoveSensitiveValues(df codersdk.DeploymentFlags) codersdk.DeploymentFlags { - v := reflect.ValueOf(&df).Elem() - t := v.Type() - for i := 0; i < t.NumField(); i++ { - fv := v.Field(i) - if vp, ok := fv.Interface().(*codersdk.Flag[string]); ok { - if vp.Secret && vp.Value != "" { - // Make a copy and remove the value. - v := *vp - v.Value = secretValue - fv.Set(reflect.ValueOf(&v)) - } - } - } - - 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.Flag[string]: - StringFlag(flagset, v) - case *codersdk.Flag[[]string]: - StringArrayFlag(flagset, v) - case *codersdk.Flag[int]: - IntFlag(flagset, v) - case *codersdk.Flag[bool]: - BoolFlag(flagset, v) - case *codersdk.Flag[time.Duration]: - 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.Flag[string]) { - cliflag.StringVarP(flagset, - &fl.Value, - fl.Flag, - fl.Shorthand, - fl.EnvVar, - fl.Default, - fl.Description, - ) -} - -func BoolFlag(flagset *pflag.FlagSet, fl *codersdk.Flag[bool]) { - cliflag.BoolVarP(flagset, - &fl.Value, - fl.Flag, - fl.Shorthand, - fl.EnvVar, - fl.Default, - fl.Description, - ) -} - -func IntFlag(flagset *pflag.FlagSet, fl *codersdk.Flag[int]) { - cliflag.IntVarP(flagset, - &fl.Value, - fl.Flag, - fl.Shorthand, - fl.EnvVar, - fl.Default, - fl.Description, - ) -} - -func DurationFlag(flagset *pflag.FlagSet, fl *codersdk.Flag[time.Duration]) { - cliflag.DurationVarP(flagset, - &fl.Value, - fl.Flag, - fl.Shorthand, - fl.EnvVar, - fl.Default, - fl.Description, - ) -} - -func StringArrayFlag(flagset *pflag.FlagSet, fl *codersdk.Flag[[]string]) { - cliflag.StringArrayVarP(flagset, - &fl.Value, - fl.Flag, - fl.Shorthand, - fl.EnvVar, - fl.Default, - fl.Description, - ) -} - -func defaultCacheDir() string { - defaultCacheDir, err := os.UserCacheDir() - if err != nil { - defaultCacheDir = os.TempDir() - } - if dir := os.Getenv("CACHE_DIRECTORY"); dir != "" { - // For compatibility with systemd. - defaultCacheDir = dir - } - - return filepath.Join(defaultCacheDir, "coder") -} diff --git a/cli/server.go b/cli/server.go index 9c1f38af7b23b..0030e2c6ff4f4 100644 --- a/cli/server.go +++ b/cli/server.go @@ -172,6 +172,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co } defer listener.Close() + var tlsConfig *tls.Config if cfg.TLS.Enable { tlsConfig, err = configureTLS( cfg.TLS.MinVersion, diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index 62d63c9154466..4a4198e43c45b 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -265,20 +265,11 @@ func NewOptions(t *testing.T, options *Options) (func(http.Handler), context.Can }, }, }, -<<<<<<< HEAD AutoImportTemplates: options.AutoImportTemplates, MetricsCacheRefreshInterval: options.MetricsCacheRefreshInterval, AgentStatsRefreshInterval: options.AgentStatsRefreshInterval, - DeploymentFlags: options.DeploymentFlags, + DeploymentConfig: options.DeploymentConfig, } -======= - }, - AutoImportTemplates: options.AutoImportTemplates, - MetricsCacheRefreshInterval: options.MetricsCacheRefreshInterval, - AgentStatsRefreshInterval: options.AgentStatsRefreshInterval, - DeploymentConfig: options.DeploymentConfig, - } ->>>>>>> idk } // NewWithAPI constructs an in-memory API instance and returns a client to talk to it. From 451b7c4bea00810e6f91ed5aee4125ec8fd7348d Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 01:46:30 +0000 Subject: [PATCH 10/43] more fix --- enterprise/cli/server.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/enterprise/cli/server.go b/enterprise/cli/server.go index cf5d572290441..36ec3c44b919f 100644 --- a/enterprise/cli/server.go +++ b/enterprise/cli/server.go @@ -25,9 +25,14 @@ import ( func server() *cobra.Command { vip := deployment.DefaultViper() - cmd := agpl.Server(dflags, func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, io.Closer, error) { - if dflags.DerpServerRelayAddress.Value != "" { - _, err := url.Parse(dflags.DerpServerRelayAddress.Value) + cmd := agpl.Server(vip, func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, io.Closer, error) { + cfg, err := deployment.Config(vip) + if err != nil { + return nil, nil, xerrors.Errorf("failed to read config: %w", err) + } + + if cfg.DerpServerRelayAddress != "" { + _, err := url.Parse(cfg.DerpServerRelayAddress) if err != nil { return nil, nil, xerrors.Errorf("derp-server-relay-address must be a valid HTTP URL: %w", err) } @@ -62,7 +67,7 @@ func server() *cobra.Command { BrowserOnly: options.DeploymentConfig.BrowserOnly, SCIMAPIKey: []byte(options.DeploymentConfig.SCIMAuthHeader), UserWorkspaceQuota: options.DeploymentConfig.UserWorkspaceQuota, - RBACEnabled: true, + RBAC: true, DERPServerRelayAddress: options.DeploymentConfig.DerpServerRelayAddress, DERPServerRegionID: options.DeploymentConfig.DerpServerRegionID, From 8928789b26e84013c1d24b80bacb4b2467222eaf Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 01:50:07 +0000 Subject: [PATCH 11/43] relay --- cli/deployment/config.go | 6 ++++-- codersdk/config.go | 4 ++++ enterprise/cli/server.go | 8 ++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index e96333b78945b..b80db4a7b7d05 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -1,6 +1,6 @@ // Code generated by go generate; DO NOT EDIT. -// This file was generated by the script at scripts/cligen -// The data for populating this file is from the DeploymentConfig struct in codersdk. +// This file was generated by the script at scripts/deploymentconfig/main.go +// The data for populating this file is from codersdk.. package deployment import ( @@ -155,6 +155,8 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { } func AttachEnterpriseFlags(flagset *pflag.FlagSet, vip *viper.Viper) { + _ = flagset.StringP("derp-server-relay-address", "", vip.GetString("derp.server.relay_address"), `An HTTP address that is accessible by other replicas to relay DERP traffic. Required for high availability.`) + _ = vip.BindPFlag("derp.server.relay_address", flagset.Lookup("derp-server-relay-address")) _ = flagset.BoolP("audit-logging", "", vip.GetBool("audit_logging"), `Specifies whether audit logging is enabled.`) _ = vip.BindPFlag("audit_logging", flagset.Lookup("audit-logging")) _ = flagset.BoolP("browser-only", "", vip.GetBool("browser_only"), `Whether Coder only allows connections to workspaces via the browser.`) diff --git a/codersdk/config.go b/codersdk/config.go index 790fb38d58565..6001ad924e06e 100644 --- a/codersdk/config.go +++ b/codersdk/config.go @@ -121,6 +121,10 @@ type DERPServerConfig struct { // Flag: derp-server-stun-addresses // Default: []string{"stun.l.google.com:19302"} STUNAddresses []string `mapstructure:"stun_address"` + // Usage: An HTTP address that is accessible by other replicas to relay DERP traffic. Required for high availability. + // Flag: derp-server-relay-address + // Enterprise: true + RelayAddress string `mapstructure:"relay_address"` } type DERPConfigConfig struct { diff --git a/enterprise/cli/server.go b/enterprise/cli/server.go index 36ec3c44b919f..c261eac497c90 100644 --- a/enterprise/cli/server.go +++ b/enterprise/cli/server.go @@ -31,8 +31,8 @@ func server() *cobra.Command { return nil, nil, xerrors.Errorf("failed to read config: %w", err) } - if cfg.DerpServerRelayAddress != "" { - _, err := url.Parse(cfg.DerpServerRelayAddress) + if cfg.DERP.Server.RelayAddress != "" { + _, err := url.Parse(cfg.DERP.Server.RelayAddress) if err != nil { return nil, nil, xerrors.Errorf("derp-server-relay-address must be a valid HTTP URL: %w", err) } @@ -68,8 +68,8 @@ func server() *cobra.Command { SCIMAPIKey: []byte(options.DeploymentConfig.SCIMAuthHeader), UserWorkspaceQuota: options.DeploymentConfig.UserWorkspaceQuota, RBAC: true, - DERPServerRelayAddress: options.DeploymentConfig.DerpServerRelayAddress, - DERPServerRegionID: options.DeploymentConfig.DerpServerRegionID, + DERPServerRelayAddress: options.DeploymentConfig.DERP.Server.RelayAddress, + DERPServerRegionID: options.DeploymentConfig.DERP.Server.RegionID, Options: options, } From bc53e5ebbeae923fc410e11d99212a26b10c2410 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 16:31:32 +0000 Subject: [PATCH 12/43] fix env --- cli/deployment/config.go | 66 ++++++++++++++++---------------- cli/root.go | 2 +- enterprise/cli/server.go | 2 +- scripts/deploymentconfig/main.go | 4 +- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index b80db4a7b7d05..8c6479ef09511 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -22,7 +22,7 @@ func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { return cfg, vip.Unmarshal(cfg) } -func DefaultViper() *viper.Viper { +func NewViper() *viper.Viper { v := viper.New() v.SetEnvPrefix("coder") v.AutomaticEnv() @@ -61,27 +61,27 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { _ = flagset.DurationP("autobuild-poll-interval", "", vip.GetDuration("autobuild_poll_interval"), `Interval to poll for scheduled workspace builds.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_AUTOBUILD_POLL_INTERVAL")) _ = vip.BindPFlag("autobuild_poll_interval", flagset.Lookup("autobuild-poll-interval")) _ = flagset.MarkHidden("autobuild-poll-interval") - _ = flagset.BoolP("derp-server-enable", "", vip.GetBool("derp.server.enabled"), `Whether to enable or disable the embedded DERP relay server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.SERVER.ENABLED")) + _ = flagset.BoolP("derp-server-enable", "", vip.GetBool("derp.server.enabled"), `Whether to enable or disable the embedded DERP relay server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_SERVER_ENABLED")) _ = vip.BindPFlag("derp.server.enabled", flagset.Lookup("derp-server-enable")) - _ = flagset.IntP("derp-server-region-id", "", vip.GetInt("derp.server.region_id"), `Region ID to use for the embedded DERP server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.SERVER.REGION_ID")) + _ = flagset.IntP("derp-server-region-id", "", vip.GetInt("derp.server.region_id"), `Region ID to use for the embedded DERP server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_SERVER_REGION_ID")) _ = vip.BindPFlag("derp.server.region_id", flagset.Lookup("derp-server-region-id")) - _ = flagset.StringP("derp-server-region-code", "", vip.GetString("derp.server.region_code"), `Region code to use for the embedded DERP server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.SERVER.REGION_CODE")) + _ = flagset.StringP("derp-server-region-code", "", vip.GetString("derp.server.region_code"), `Region code to use for the embedded DERP server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_SERVER_REGION_CODE")) _ = vip.BindPFlag("derp.server.region_code", flagset.Lookup("derp-server-region-code")) - _ = flagset.StringP("derp-server-region-name", "", vip.GetString("derp.server.region_name"), `Region name that for the embedded DERP server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.SERVER.REGION_NAME")) + _ = flagset.StringP("derp-server-region-name", "", vip.GetString("derp.server.region_name"), `Region name that for the embedded DERP server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_SERVER_REGION_NAME")) _ = vip.BindPFlag("derp.server.region_name", flagset.Lookup("derp-server-region-name")) - _ = flagset.StringArrayP("derp-server-stun-addresses", "", vip.GetStringSlice("derp.server.stun_address"), `Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.SERVER.STUN_ADDRESS")) + _ = flagset.StringArrayP("derp-server-stun-addresses", "", vip.GetStringSlice("derp.server.stun_address"), `Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_SERVER_STUN_ADDRESS")) _ = vip.BindPFlag("derp.server.stun_address", flagset.Lookup("derp-server-stun-addresses")) - _ = flagset.StringP("derp-config-url", "", vip.GetString("derp.config.url"), `URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.CONFIG.URL")) + _ = flagset.StringP("derp-config-url", "", vip.GetString("derp.config.url"), `URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_CONFIG_URL")) _ = vip.BindPFlag("derp.config.url", flagset.Lookup("derp-config-url")) - _ = flagset.StringP("derp-config-path", "", vip.GetString("derp.config.path"), `Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP.CONFIG.PATH")) + _ = flagset.StringP("derp-config-path", "", vip.GetString("derp.config.path"), `Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_CONFIG_PATH")) _ = vip.BindPFlag("derp.config.path", flagset.Lookup("derp-config-path")) - _ = flagset.BoolP("prometheus-enable", "", vip.GetBool("prometheus.enabled"), `Serve prometheus metrics on the address defined by prometheus address.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PROMETHEUS.ENABLED")) + _ = flagset.BoolP("prometheus-enable", "", vip.GetBool("prometheus.enabled"), `Serve prometheus metrics on the address defined by prometheus address.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PROMETHEUS_ENABLED")) _ = vip.BindPFlag("prometheus.enabled", flagset.Lookup("prometheus-enable")) - _ = flagset.StringP("prometheus-address", "", vip.GetString("prometheus.address"), `The bind address to serve prometheus metrics.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PROMETHEUS.ADDRESS")) + _ = flagset.StringP("prometheus-address", "", vip.GetString("prometheus.address"), `The bind address to serve prometheus metrics.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PROMETHEUS_ADDRESS")) _ = vip.BindPFlag("prometheus.address", flagset.Lookup("prometheus-address")) - _ = flagset.BoolP("pprof-enable", "", vip.GetBool("pprof.enabled"), `Serve pprof metrics on the address defined by pprof address.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PPROF.ENABLED")) + _ = flagset.BoolP("pprof-enable", "", vip.GetBool("pprof.enabled"), `Serve pprof metrics on the address defined by pprof address.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PPROF_ENABLED")) _ = vip.BindPFlag("pprof.enabled", flagset.Lookup("pprof-enable")) - _ = flagset.StringP("pprof-address", "", vip.GetString("pprof.address"), `The bind address to serve pprof.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PPROF.ADDRESS")) + _ = flagset.StringP("pprof-address", "", vip.GetString("pprof.address"), `The bind address to serve pprof.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PPROF_ADDRESS")) _ = vip.BindPFlag("pprof.address", flagset.Lookup("pprof-address")) _ = flagset.StringP("cache-dir", "", vip.GetString("cache_dir"), `The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_CACHE_DIR")) _ = vip.BindPFlag("cache_dir", flagset.Lookup("cache-dir")) @@ -92,48 +92,48 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { _ = vip.BindPFlag("provisioner_daemon_count", flagset.Lookup("provisioner-daemons")) _ = flagset.StringP("postgres-url", "", vip.GetString("postgres_url"), `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".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_POSTGRES_URL")) _ = vip.BindPFlag("postgres_url", flagset.Lookup("postgres-url")) - _ = flagset.StringP("oauth2-github-client-id", "", vip.GetString("oauth2_github.client_id"), `Client ID for Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB.CLIENT_ID")) + _ = flagset.StringP("oauth2-github-client-id", "", vip.GetString("oauth2_github.client_id"), `Client ID for Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB_CLIENT_ID")) _ = vip.BindPFlag("oauth2_github.client_id", flagset.Lookup("oauth2-github-client-id")) - _ = flagset.StringP("oauth2-github-client-secret", "", vip.GetString("oauth2_github.client_secret"), `Client secret for Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB.CLIENT_SECRET")) + _ = flagset.StringP("oauth2-github-client-secret", "", vip.GetString("oauth2_github.client_secret"), `Client secret for Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB_CLIENT_SECRET")) _ = vip.BindPFlag("oauth2_github.client_secret", flagset.Lookup("oauth2-github-client-secret")) - _ = flagset.StringArrayP("oauth2-github-allowed-orgs", "", vip.GetStringSlice("oauth2_github.allowed_organizations"), `Organizations the user must be a member of to Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB.ALLOWED_ORGANIZATIONS")) + _ = flagset.StringArrayP("oauth2-github-allowed-orgs", "", vip.GetStringSlice("oauth2_github.allowed_organizations"), `Organizations the user must be a member of to Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB_ALLOWED_ORGANIZATIONS")) _ = vip.BindPFlag("oauth2_github.allowed_organizations", flagset.Lookup("oauth2-github-allowed-orgs")) - _ = flagset.StringArrayP("oauth2-github-allowed-teams", "", vip.GetStringSlice("oauth2_github.allowed_teams"), `Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB.ALLOWED_TEAMS")) + _ = flagset.StringArrayP("oauth2-github-allowed-teams", "", vip.GetStringSlice("oauth2_github.allowed_teams"), `Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB_ALLOWED_TEAMS")) _ = vip.BindPFlag("oauth2_github.allowed_teams", flagset.Lookup("oauth2-github-allowed-teams")) - _ = flagset.BoolP("oauth2-github-allow-signups", "", vip.GetBool("oauth2_github.allow_signups"), `Whether new users can sign up with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB.ALLOW_SIGNUPS")) + _ = flagset.BoolP("oauth2-github-allow-signups", "", vip.GetBool("oauth2_github.allow_signups"), `Whether new users can sign up with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS")) _ = vip.BindPFlag("oauth2_github.allow_signups", flagset.Lookup("oauth2-github-allow-signups")) - _ = flagset.StringP("oauth2-github-enterprise-base-url", "", vip.GetString("oauth2_github.enterprise_base_url"), `Base URL of a GitHub Enterprise deployment to use for Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB.ENTERPRISE_BASE_URL")) + _ = flagset.StringP("oauth2-github-enterprise-base-url", "", vip.GetString("oauth2_github.enterprise_base_url"), `Base URL of a GitHub Enterprise deployment to use for Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB_ENTERPRISE_BASE_URL")) _ = vip.BindPFlag("oauth2_github.enterprise_base_url", flagset.Lookup("oauth2-github-enterprise-base-url")) - _ = flagset.BoolP("oidc-allow-signups", "", vip.GetBool("oidc.allow_signups"), `Whether new users can sign up with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC.ALLOW_SIGNUPS")) + _ = flagset.BoolP("oidc-allow-signups", "", vip.GetBool("oidc.allow_signups"), `Whether new users can sign up with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC_ALLOW_SIGNUPS")) _ = vip.BindPFlag("oidc.allow_signups", flagset.Lookup("oidc-allow-signups")) - _ = flagset.StringP("oidc-client-id", "", vip.GetString("oidc.client_id"), `Client ID to use for Login with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC.CLIENT_ID")) + _ = flagset.StringP("oidc-client-id", "", vip.GetString("oidc.client_id"), `Client ID to use for Login with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC_CLIENT_ID")) _ = vip.BindPFlag("oidc.client_id", flagset.Lookup("oidc-client-id")) - _ = flagset.StringP("oidc-client-secret", "", vip.GetString("oidc.cliet_secret"), `Client secret to use for Login with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC.CLIET_SECRET")) + _ = flagset.StringP("oidc-client-secret", "", vip.GetString("oidc.cliet_secret"), `Client secret to use for Login with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC_CLIET_SECRET")) _ = vip.BindPFlag("oidc.cliet_secret", flagset.Lookup("oidc-client-secret")) - _ = flagset.StringP("oidc-email-domain", "", vip.GetString("oidc.email_domain"), `Email domain that clients logging in with OIDC must match.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC.EMAIL_DOMAIN")) + _ = flagset.StringP("oidc-email-domain", "", vip.GetString("oidc.email_domain"), `Email domain that clients logging in with OIDC must match.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC_EMAIL_DOMAIN")) _ = vip.BindPFlag("oidc.email_domain", flagset.Lookup("oidc-email-domain")) - _ = flagset.StringP("oidc-issuer-url", "", vip.GetString("oidc.issuer_url"), `Issuer URL to use for Login with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC.ISSUER_URL")) + _ = flagset.StringP("oidc-issuer-url", "", vip.GetString("oidc.issuer_url"), `Issuer URL to use for Login with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC_ISSUER_URL")) _ = vip.BindPFlag("oidc.issuer_url", flagset.Lookup("oidc-issuer-url")) - _ = flagset.StringArrayP("oidc-scopes", "", vip.GetStringSlice("oidc.scopes"), `Scopes to grant when authenticating with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC.SCOPES")) + _ = flagset.StringArrayP("oidc-scopes", "", vip.GetStringSlice("oidc.scopes"), `Scopes to grant when authenticating with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC_SCOPES")) _ = vip.BindPFlag("oidc.scopes", flagset.Lookup("oidc-scopes")) - _ = flagset.BoolP("telemetry", "", vip.GetBool("telemetry.enable"), `Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TELEMETRY.ENABLE")) + _ = flagset.BoolP("telemetry", "", vip.GetBool("telemetry.enable"), `Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TELEMETRY_ENABLE")) _ = vip.BindPFlag("telemetry.enable", flagset.Lookup("telemetry")) - _ = flagset.BoolP("telemetry-trace", "", vip.GetBool("telemetry.trace_enable"), `Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TELEMETRY.TRACE_ENABLE")) + _ = flagset.BoolP("telemetry-trace", "", vip.GetBool("telemetry.trace_enable"), `Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TELEMETRY_TRACE_ENABLE")) _ = vip.BindPFlag("telemetry.trace_enable", flagset.Lookup("telemetry-trace")) - _ = flagset.StringP("telemetry-url", "", vip.GetString("telemetry.url"), `URL to send telemetry.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TELEMETRY.URL")) + _ = flagset.StringP("telemetry-url", "", vip.GetString("telemetry.url"), `URL to send telemetry.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TELEMETRY_URL")) _ = vip.BindPFlag("telemetry.url", flagset.Lookup("telemetry-url")) _ = flagset.MarkHidden("telemetry-url") - _ = flagset.BoolP("tls-enable", "", vip.GetBool("tls_config.tls_enable"), `Whether TLS will be enabled.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG.TLS_ENABLE")) + _ = flagset.BoolP("tls-enable", "", vip.GetBool("tls_config.tls_enable"), `Whether TLS will be enabled.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_ENABLE")) _ = vip.BindPFlag("tls_config.tls_enable", flagset.Lookup("tls-enable")) - _ = flagset.StringArrayP("tls-cert-file", "", vip.GetStringSlice("tls_config.tls_cert_files"), `Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG.TLS_CERT_FILES")) + _ = flagset.StringArrayP("tls-cert-file", "", vip.GetStringSlice("tls_config.tls_cert_files"), `Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_CERT_FILES")) _ = vip.BindPFlag("tls_config.tls_cert_files", flagset.Lookup("tls-cert-file")) - _ = flagset.StringP("tls-client-ca-file", "", vip.GetString("tls_config.tls_client_ca_file"), `PEM-encoded Certificate Authority file used for checking the authenticity of client`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG.TLS_CLIENT_CA_FILE")) + _ = flagset.StringP("tls-client-ca-file", "", vip.GetString("tls_config.tls_client_ca_file"), `PEM-encoded Certificate Authority file used for checking the authenticity of client`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_CLIENT_CA_FILE")) _ = vip.BindPFlag("tls_config.tls_client_ca_file", flagset.Lookup("tls-client-ca-file")) - _ = flagset.StringP("tls-client-auth", "", vip.GetString("tls_config.tls_client_auth"), `Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG.TLS_CLIENT_AUTH")) + _ = flagset.StringP("tls-client-auth", "", vip.GetString("tls_config.tls_client_auth"), `Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_CLIENT_AUTH")) _ = vip.BindPFlag("tls_config.tls_client_auth", flagset.Lookup("tls-client-auth")) - _ = flagset.StringArrayP("tls-key-file", "", vip.GetStringSlice("tls_config.tls_key_tiles"), `Paths to the private keys for each of the certificates. It requires a PEM-encoded file.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG.TLS_KEY_TILES")) + _ = flagset.StringArrayP("tls-key-file", "", vip.GetStringSlice("tls_config.tls_key_tiles"), `Paths to the private keys for each of the certificates. It requires a PEM-encoded file.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_KEY_TILES")) _ = vip.BindPFlag("tls_config.tls_key_tiles", flagset.Lookup("tls-key-file")) - _ = flagset.StringP("tls-min-version", "", vip.GetString("tls_config.tls_min_version"), `Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13"`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG.TLS_MIN_VERSION")) + _ = flagset.StringP("tls-min-version", "", vip.GetString("tls_config.tls_min_version"), `Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13"`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_MIN_VERSION")) _ = vip.BindPFlag("tls_config.tls_min_version", flagset.Lookup("tls-min-version")) _ = flagset.BoolP("trace", "", vip.GetBool("trace_enable"), `Whether application tracing data is collected.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TRACE_ENABLE")) _ = vip.BindPFlag("trace_enable", flagset.Lookup("trace")) diff --git a/cli/root.go b/cli/root.go index 8bddf8c4bd6fd..f86d44161e46f 100644 --- a/cli/root.go +++ b/cli/root.go @@ -101,7 +101,7 @@ func Core() []*cobra.Command { } func AGPL() []*cobra.Command { - all := append(Core(), Server(deployment.DefaultViper(), func(_ context.Context, o *coderd.Options) (*coderd.API, io.Closer, error) { + all := append(Core(), Server(deployment.NewViper(), func(_ context.Context, o *coderd.Options) (*coderd.API, io.Closer, error) { api := coderd.New(o) return api, api, nil })) diff --git a/enterprise/cli/server.go b/enterprise/cli/server.go index c261eac497c90..bc391997985d4 100644 --- a/enterprise/cli/server.go +++ b/enterprise/cli/server.go @@ -24,7 +24,7 @@ import ( ) func server() *cobra.Command { - vip := deployment.DefaultViper() + vip := deployment.NewViper() cmd := agpl.Server(vip, func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, io.Closer, error) { cfg, err := deployment.Config(vip) if err != nil { diff --git a/scripts/deploymentconfig/main.go b/scripts/deploymentconfig/main.go index d294ce164d172..c3368ef3164bc 100644 --- a/scripts/deploymentconfig/main.go +++ b/scripts/deploymentconfig/main.go @@ -139,7 +139,7 @@ func handleStruct(prefix string, target string, structs map[string]*ast.StructTy } f := Field{ Key: key, - Env: EnvPrefix + strings.ReplaceAll(strings.ToUpper(key), "-", "_"), + Env: EnvPrefix + strings.ToUpper(strings.NewReplacer("-", "_", ".", "_").Replace(key)), } switch ft := field.Type.(type) { case *ast.Ident: @@ -266,7 +266,7 @@ func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { return cfg, vip.Unmarshal(cfg) } -func DefaultViper() *viper.Viper { +func NewViper() *viper.Viper { v := viper.New() v.SetEnvPrefix("coder") v.AutomaticEnv() From 387293eba1262772aa5b080b5b0e001f9c467fbd Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 17:03:59 +0000 Subject: [PATCH 13/43] pass tests --- cli/deployment/config.go | 2 +- coderd/coderd.go | 4 +- coderd/flags.go | 23 +++-- coderd/flags_test.go | 69 ++++++++------- coderd/rbac/object.go | 6 +- codersdk/config.go | 124 +++++++++++++-------------- codersdk/flags.go | 142 ------------------------------- scripts/deploymentconfig/main.go | 2 +- 8 files changed, 122 insertions(+), 250 deletions(-) delete mode 100644 codersdk/flags.go diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 8c6479ef09511..55c29221ab381 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -19,7 +19,7 @@ import ( func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { cfg := codersdk.DeploymentConfig{} - return cfg, vip.Unmarshal(cfg) + return cfg, vip.Unmarshal(&cfg) } func NewViper() *viper.Viper { diff --git a/coderd/coderd.go b/coderd/coderd.go index e2aa6f8d581a7..1aed8417b0ba8 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -286,9 +286,9 @@ func New(options *Options) *API { }) }) }) - r.Route("/flags", func(r chi.Router) { + r.Route("/config", func(r chi.Router) { r.Use(apiKeyMiddleware) - // r.Get("/deployment", api.deploymentFlags) + r.Get("/deployment", api.deploymentConfig) }) r.Route("/audit", func(r chi.Router) { r.Use( diff --git a/coderd/flags.go b/coderd/flags.go index 944828f041223..d68332c9089f7 100644 --- a/coderd/flags.go +++ b/coderd/flags.go @@ -1,10 +1,17 @@ package coderd -// func (api *API) deploymentFlags(rw http.ResponseWriter, r *http.Request) { -// if !api.Authorize(r, rbac.ActionRead, rbac.ResourceDeploymentFlags) { -// httpapi.Forbidden(rw) -// return -// } - -// httpapi.Write(r.Context(), rw, http.StatusOK, deployment.RemoveSensitiveValues(*api.DeploymentFlags)) -// } +import ( + "net/http" + + "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/coderd/rbac" +) + +func (api *API) deploymentConfig(rw http.ResponseWriter, r *http.Request) { + if !api.Authorize(r, rbac.ActionRead, rbac.ResourceDeploymentConfig) { + httpapi.Forbidden(rw) + return + } + + httpapi.Write(r.Context(), rw, http.StatusOK, api.DeploymentConfig) +} diff --git a/coderd/flags_test.go b/coderd/flags_test.go index a5ae3d916a6ef..106db53f85d21 100644 --- a/coderd/flags_test.go +++ b/coderd/flags_test.go @@ -1,36 +1,43 @@ package coderd_test -const ( - secretValue = "********" +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/coder/coder/cli/deployment" + "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/testutil" ) -// func TestDeploymentFlagSecrets(t *testing.T) { -// t.Parallel() -// hi := "hi" -// ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) -// defer cancel() -// df := deployment.Flags() -// // check if copy works for non-secret values -// df.AccessURL.Value = hi -// // check if secrets are removed -// df.OAuth2GithubClientSecret.Value = hi -// df.OIDCClientSecret.Value = hi -// df.PostgresURL.Value = hi -// df.SCIMAuthHeader.Value = hi +func TestDeploymentConfig(t *testing.T) { + t.Parallel() + hi := "hi" + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + vip := deployment.NewViper() + cfg, err := deployment.Config(vip) + require.NoError(t, err) + // values should be returned + cfg.AccessURL = hi + // values should not be returned + cfg.OAuth2Github.ClientSecret = hi + cfg.OIDC.ClientSecret = hi + cfg.PostgresURL = hi + cfg.SCIMAuthHeader = hi -// client := coderdtest.New(t, &coderdtest.Options{ -// DeploymentFlags: df, -// }) -// _ = coderdtest.CreateFirstUser(t, client) -// scrubbed, err := client.DeploymentFlags(ctx) -// require.NoError(t, err) -// // ensure df is unchanged -// require.EqualValues(t, hi, df.OAuth2GithubClientSecret.Value) -// // ensure normal values pass through -// require.EqualValues(t, hi, scrubbed.AccessURL.Value) -// // ensure secrets are removed -// require.EqualValues(t, secretValue, scrubbed.OAuth2GithubClientSecret.Value) -// require.EqualValues(t, secretValue, scrubbed.OIDCClientSecret.Value) -// require.EqualValues(t, secretValue, scrubbed.PostgresURL.Value) -// require.EqualValues(t, secretValue, scrubbed.SCIMAuthHeader.Value) -// } + client := coderdtest.New(t, &coderdtest.Options{ + DeploymentConfig: &cfg, + }) + _ = coderdtest.CreateFirstUser(t, client) + scrubbed, err := client.DeploymentConfig(ctx) + require.NoError(t, err) + // ensure normal values pass through + require.EqualValues(t, hi, scrubbed.AccessURL) + // ensure secrets are removed + require.Empty(t, scrubbed.OAuth2Github.ClientSecret) + require.Empty(t, scrubbed.OIDC.ClientSecret) + require.Empty(t, scrubbed.PostgresURL) + require.Empty(t, scrubbed.SCIMAuthHeader) +} diff --git a/coderd/rbac/object.go b/coderd/rbac/object.go index 1a8861c984ce9..4852a4c269638 100644 --- a/coderd/rbac/object.go +++ b/coderd/rbac/object.go @@ -142,9 +142,9 @@ var ( Type: "license", } - // ResourceDeploymentFlags - ResourceDeploymentFlags = Object{ - Type: "deployment_flags", + // ResourceDeploymentConfig + ResourceDeploymentConfig = Object{ + Type: "deployment_config", } ResourceReplicas = Object{ diff --git a/codersdk/config.go b/codersdk/config.go index 6001ad924e06e..2a8ffdeb6206a 100644 --- a/codersdk/config.go +++ b/codersdk/config.go @@ -12,234 +12,234 @@ import ( type DeploymentConfig struct { // Usage: External URL to access your deployment. This must be accessible by all provisioned workspaces. // Flag: access-url - AccessURL string `mapstructure:"access_url"` + AccessURL string `mapstructure:"access_url" json:"access_url"` // Usage: Specifies the wildcard hostname to use for workspace applications in the form "*.example.com". // Flag: wildcard-access-url - WildcardAccessURL string `mapstructure:"wildcard_access_url"` + WildcardAccessURL string `mapstructure:"wildcard_access_url" json:"wildcard_access_url"` // Usage: Bind address of the server. // Flag: address // Shorthand: a // Default: "127.0.0.1:3000" - Address string `mapstructure:"address"` + Address string `mapstructure:"address" json:"address"` // Usage: Interval to poll for scheduled workspace builds. // Flag: autobuild-poll-interval // Hidden: true // Default: time.Minute - AutobuildPollInterval time.Duration `mapstructure:"autobuild_poll_interval"` - DERP DERPConfig `mapstructure:"derp"` - Prometheus PrometheusConfig `mapstructure:"prometheus"` - Pprof PprofConfig `mapstructure:"pprof"` + AutobuildPollInterval time.Duration `mapstructure:"autobuild_poll_interval" json:"autobuild_poll_interval"` + DERP DERPConfig `mapstructure:"derp" json:"derp"` + Prometheus PrometheusConfig `mapstructure:"prometheus" json:"prometheus"` + Pprof PprofConfig `mapstructure:"pprof" json:"pprof"` // Usage: The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. // Flag: cache-dir // Default: defaultCacheDir() - CacheDir string `mapstructure:"cache_dir"` + CacheDir string `mapstructure:"cache_dir" json:"cache_dir"` // Usage: Controls whether data will be stored in an in-memory database. // Flag: in-memory // Hidden: true - InMemoryDatabase bool `mapstructure:"in_memory_database"` + InMemoryDatabase bool `mapstructure:"in_memory_database" json:"in_memory_database"` // Usage: Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. // Flag: provisioner-daemons // Default: 3 - ProvisionerDaemonCount int `mapstructure:"provisioner_daemon_count"` + ProvisionerDaemonCount int `mapstructure:"provisioner_daemon_count" json:"provisioner_daemon_count"` // Usage: 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". // Flag: postgres-url - PostgresURL string `mapstructure:"postgres_url"` - OAuth2Github OAuth2GithubConfig `mapstructure:"oauth2_github"` - OIDC OIDCConfig `mapstructure:"oidc"` - Telemetry TelemetryConfig `mapstructure:"telemetry"` - TLS TLSConfig `mapstructure:"tls_config"` + PostgresURL string `mapstructure:"postgres_url" json:"-"` + OAuth2Github OAuth2GithubConfig `mapstructure:"oauth2_github" json:"oauth2_github"` + OIDC OIDCConfig `mapstructure:"oidc" json:"oidc"` + Telemetry TelemetryConfig `mapstructure:"telemetry" json:"telemetry"` + TLS TLSConfig `mapstructure:"tls_config" json:"tls_config"` // Usage: Whether application tracing data is collected. // Flag: trace - TraceEnable bool `mapstructure:"trace_enable"` + TraceEnable bool `mapstructure:"trace_enable" json:"trace_enable"` // Usage: Controls if the 'Secure' property is set on browser session cookies. // Flag: secure-auth-cookie - SecureAuthCookie bool `mapstructure:"secure_auth_cookie"` + SecureAuthCookie bool `mapstructure:"secure_auth_cookie" json:"secure_auth_cookie"` // Usage: The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". // Flag: ssh-keygen-algorithm // Default: "ed25519" - SSHKeygenAlgorithm string `mapstructure:"ssh_keygen_algorithm"` + SSHKeygenAlgorithm string `mapstructure:"ssh_keygen_algorithm" json:"ssh_keygen_algorithm"` // Usage: Templates to auto-import. Available auto-importable templates are: kubernetes // Flag: auto-import-template // Hidden: true - AutoImportTemplates []string `mapstructure:"auto_import_templates"` + AutoImportTemplates []string `mapstructure:"auto_import_templates" json:"auto_import_templates"` // Usage: How frequently metrics are refreshed // Flag: metrics-cache-refresh-interval // Hidden: true // Default: time.Hour - MetricsCacheRefreshInterval time.Duration `mapstructure:"metrics_cache_refresh_interval"` + MetricsCacheRefreshInterval time.Duration `mapstructure:"metrics_cache_refresh_interval" json:"metrics_cache_refresh_interval"` // Usage: How frequently agent stats are recorded // Flag: agent-stats-refresh-interval // Hidden: true // Default: 10 * time.Minute - AgentStatRefreshInterval time.Duration `mapstructure:"agent_stat_refresh_interval"` + AgentStatRefreshInterval time.Duration `mapstructure:"agent_stat_refresh_interval" json:"agent_stat_refresh_interval"` // Usage: Enables verbose logging. // Flag: verbose // Shorthand: v - Verbose bool `mapstructure:"verbose"` + Verbose bool `mapstructure:"verbose" json:"verbose"` // Usage: Specifies whether audit logging is enabled. // Flag: audit-logging // Default: true // Enterprise: true - AuditLogging bool `mapstructure:"audit_logging"` + AuditLogging bool `mapstructure:"audit_logging" json:"audit_logging"` // Usage: Whether Coder only allows connections to workspaces via the browser. // Flag: browser-only // Enterprise: true - BrowserOnly bool `mapstructure:"browser_only"` + BrowserOnly bool `mapstructure:"browser_only" json:"browser_only"` // Usage: Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. // Flag: scim-auth-header // Enterprise: true - SCIMAuthHeader string `mapstructure:"scim_auth_header"` + SCIMAuthHeader string `mapstructure:"scim_auth_header" json:"-"` // Usage: Enables and sets a limit on how many workspaces each user can create. // Flag: user-workspace-quota // Enterprise: true - UserWorkspaceQuota int `mapstructure:"user_workspace_quota"` + UserWorkspaceQuota int `mapstructure:"user_workspace_quota" json:"user_workspace_quota"` } type DERPConfig struct { - Server DERPServerConfig `mapstructure:"server"` - Config DERPConfigConfig `mapstructure:"config"` + Server DERPServerConfig `mapstructure:"server" json:"server"` + Config DERPConfigConfig `mapstructure:"config" json:"config"` } type DERPServerConfig struct { // Usage: Whether to enable or disable the embedded DERP relay server. // Flag: derp-server-enable // Default: true - Enable bool `mapstructure:"enabled"` + Enable bool `mapstructure:"enabled" json:"enabled"` // Usage: Region ID to use for the embedded DERP server. // Flag: derp-server-region-id // Default: 999 - RegionID int `mapstructure:"region_id"` + RegionID int `mapstructure:"region_id" json:"region_id"` // Usage: Region code to use for the embedded DERP server. // Flag: derp-server-region-code // Default: "coder" - RegionCode string `mapstructure:"region_code"` + RegionCode string `mapstructure:"region_code" json:"region_code"` // Usage: Region name that for the embedded DERP server. // Flag: derp-server-region-name // Default: "Coder Embedded Relay" - RegionName string `mapstructure:"region_name"` + RegionName string `mapstructure:"region_name" json:"region_name"` // Usage: Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections. // Flag: derp-server-stun-addresses // Default: []string{"stun.l.google.com:19302"} - STUNAddresses []string `mapstructure:"stun_address"` + STUNAddresses []string `mapstructure:"stun_address" json:"stun_address"` // Usage: An HTTP address that is accessible by other replicas to relay DERP traffic. Required for high availability. // Flag: derp-server-relay-address // Enterprise: true - RelayAddress string `mapstructure:"relay_address"` + RelayAddress string `mapstructure:"relay_address" json:"relay_address"` } type DERPConfigConfig struct { // Usage: URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/ // Flag: derp-config-url - URL string `mapstructure:"url"` + URL string `mapstructure:"url" json:"url"` // Usage: Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/ // Flag: derp-config-path - Path string `mapstructure:"path"` + Path string `mapstructure:"path" json:"path"` } type PrometheusConfig struct { // Usage: Serve prometheus metrics on the address defined by prometheus address. // Flag: prometheus-enable - Enable bool `mapstructure:"enabled"` + Enable bool `mapstructure:"enabled" json:"enabled"` // Usage: The bind address to serve prometheus metrics. // Flag: prometheus-address // Default: "127.0.0.1:2112" - Address string `mapstructure:"address"` + Address string `mapstructure:"address" json:"address"` } type PprofConfig struct { // Usage: Serve pprof metrics on the address defined by pprof address. // Flag: pprof-enable - Enable bool `mapstructure:"enabled"` + Enable bool `mapstructure:"enabled" json:"enabled"` // Usage: The bind address to serve pprof. // Flag: pprof-address // Default: "127.0.0.1:6060" - Address string `mapstructure:"address"` + Address string `mapstructure:"address" json:"address"` } type OAuth2GithubConfig struct { // Usage: Client ID for Login with GitHub. // Flag: oauth2-github-client-id - ClientID string `mapstructure:"client_id"` + ClientID string `mapstructure:"client_id" json:"client_id"` // Usage: Client secret for Login with GitHub. // Flag: oauth2-github-client-secret - ClientSecret string `mapstructure:"client_secret"` + ClientSecret string `mapstructure:"client_secret" json:"-"` // Usage: Organizations the user must be a member of to Login with GitHub. // Flag: oauth2-github-allowed-orgs - AllowedOrganizations []string `mapstructure:"allowed_organizations"` + AllowedOrganizations []string `mapstructure:"allowed_organizations" json:"allowed_organizations"` // Usage: Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /. // Flag: oauth2-github-allowed-teams - AllowedTeams []string `mapstructure:"allowed_teams"` + AllowedTeams []string `mapstructure:"allowed_teams" json:"allowed_teams"` // Usage: Whether new users can sign up with GitHub. // Flag: oauth2-github-allow-signups - AllowSignups bool `mapstructure:"allow_signups"` + AllowSignups bool `mapstructure:"allow_signups" json:"allow_signups"` // Usage: Base URL of a GitHub Enterprise deployment to use for Login with GitHub. // Flag: oauth2-github-enterprise-base-url - EnterpriseBaseURL string `mapstructure:"enterprise_base_url"` + EnterpriseBaseURL string `mapstructure:"enterprise_base_url" json:"enterprise_base_url"` } type OIDCConfig struct { // Usage: Whether new users can sign up with OIDC. // Flag: oidc-allow-signups // Default: true - AllowSignups bool `mapstructure:"allow_signups"` + AllowSignups bool `mapstructure:"allow_signups" json:"allow_signups"` // Usage: Client ID to use for Login with OIDC. // Flag: oidc-client-id - ClientID string `mapstructure:"client_id"` + ClientID string `mapstructure:"client_id" json:"client_id"` // Usage: Client secret to use for Login with OIDC. // Flag: oidc-client-secret - ClientSecret string `mapstructure:"cliet_secret"` + ClientSecret string `mapstructure:"cliet_secret" json:"-"` // Usage: Email domain that clients logging in with OIDC must match. // Flag: oidc-email-domain - EmailDomain string `mapstructure:"email_domain"` + EmailDomain string `mapstructure:"email_domain" json:"email_domain"` // Usage: Issuer URL to use for Login with OIDC. // Flag: oidc-issuer-url - IssuerURL string `mapstructure:"issuer_url"` + IssuerURL string `mapstructure:"issuer_url" json:"issuer_url"` // Usage: Scopes to grant when authenticating with OIDC. // Flag: oidc-scopes // Default: []string{oidc.ScopeOpenID, "profile", "email"} - Scopes []string `mapstructure:"scopes"` + Scopes []string `mapstructure:"scopes" json:"scopes"` } type TelemetryConfig struct { // Usage: Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product. // Flag: telemetry // Default: flag.Lookup("test.v") == nil - Enable bool `mapstructure:"enable"` + Enable bool `mapstructure:"enable" json:"enable"` // Usage: Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option. // Flag: telemetry-trace // Default: flag.Lookup("test.v") == nil - TraceEnable bool `mapstructure:"trace_enable"` + TraceEnable bool `mapstructure:"trace_enable" json:"trace_enable"` // Usage: URL to send telemetry. // Flag: telemetry-url // Hidden: true // Default: "https://telemetry.coder.com" - URL string `mapstructure:"url"` + URL string `mapstructure:"url" json:"url"` } type TLSConfig struct { // Usage: Whether TLS will be enabled. // Flag: tls-enable - Enable bool `mapstructure:"tls_enable"` + Enable bool `mapstructure:"tls_enable" json:"tls_enable"` // Usage: Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file. // Flag: tls-cert-file - CertFiles []string `mapstructure:"tls_cert_files"` + CertFiles []string `mapstructure:"tls_cert_files" json:"tls_cert_files"` // Usage: PEM-encoded Certificate Authority file used for checking the authenticity of client // Flag: tls-client-ca-file - ClientCAFile string `mapstructure:"tls_client_ca_file"` + ClientCAFile string `mapstructure:"tls_client_ca_file" json:"tls_client_ca_file"` // Usage: Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". // Flag: tls-client-auth - ClientAuth string `mapstructure:"tls_client_auth"` + ClientAuth string `mapstructure:"tls_client_auth" json:"tls_client_auth"` // Usage: Paths to the private keys for each of the certificates. It requires a PEM-encoded file. // Flag: tls-key-file - KeyFiles []string `mapstructure:"tls_key_tiles"` + KeyFiles []string `mapstructure:"tls_key_tiles" json:"tls_key_tiles"` // Usage: Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" // Flag: tls-min-version // Default: "tls12" - MinVersion string `mapstructure:"tls_min_version"` + MinVersion string `mapstructure:"tls_min_version" json:"tls_min_version"` } // DeploymentConfig returns the deployment config for the coder server. func (c *Client) DeploymentConfig(ctx context.Context) (DeploymentConfig, error) { - res, err := c.Request(ctx, http.MethodGet, "/api/v2/flags/deployment", nil) + res, err := c.Request(ctx, http.MethodGet, "/api/v2/config/deployment", nil) if err != nil { return DeploymentConfig{}, xerrors.Errorf("execute request: %w", err) } diff --git a/codersdk/flags.go b/codersdk/flags.go deleted file mode 100644 index bf407760bbfb8..0000000000000 --- a/codersdk/flags.go +++ /dev/null @@ -1,142 +0,0 @@ -package codersdk - -import ( - "context" - "encoding/json" - "net/http" - "time" - - "golang.org/x/xerrors" -) - -type DeploymentFlags struct { - 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"` - DerpServerRelayAddress *StringFlag `json:"derp_server_relay_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_client_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_files" 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 { - Name string `json:"name"` - Flag string `json:"flag"` - EnvVar string `json:"env_var"` - Shorthand string `json:"shorthand"` - Description string `json:"description"` - Enterprise bool `json:"enterprise"` - Secret bool `json:"secret"` - Hidden bool `json:"hidden"` - Default string `json:"default"` - Value string `json:"value"` -} - -type BoolFlag struct { - Name string `json:"name"` - Flag string `json:"flag"` - EnvVar string `json:"env_var"` - Shorthand string `json:"shorthand"` - Description string `json:"description"` - Enterprise bool `json:"enterprise"` - Hidden bool `json:"hidden"` - Default bool `json:"default"` - Value bool `json:"value"` -} - -type IntFlag struct { - Name string `json:"name"` - Flag string `json:"flag"` - EnvVar string `json:"env_var"` - Shorthand string `json:"shorthand"` - Description string `json:"description"` - Enterprise bool `json:"enterprise"` - Hidden bool `json:"hidden"` - Default int `json:"default"` - Value int `json:"value"` -} - -type DurationFlag struct { - Name string `json:"name"` - Flag string `json:"flag"` - EnvVar string `json:"env_var"` - 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"` -} - -type StringArrayFlag struct { - Name string `json:"name"` - Flag string `json:"flag"` - EnvVar string `json:"env_var"` - Shorthand string `json:"shorthand"` - Description string `json:"description"` - Enterprise bool `json:"enterprise"` - Hidden bool `json:"hidden"` - Default []string `json:"default"` - Value []string `json:"value"` -} - -// DeploymentFlags returns the deployment level flags for the coder server. -func (c *Client) DeploymentFlags(ctx context.Context) (DeploymentFlags, error) { - res, err := c.Request(ctx, http.MethodGet, "/api/v2/flags/deployment", nil) - if err != nil { - return DeploymentFlags{}, xerrors.Errorf("execute request: %w", err) - } - defer res.Body.Close() - - if res.StatusCode != http.StatusOK { - return DeploymentFlags{}, readBodyAsError(res) - } - - var df DeploymentFlags - return df, json.NewDecoder(res.Body).Decode(&df) -} diff --git a/scripts/deploymentconfig/main.go b/scripts/deploymentconfig/main.go index c3368ef3164bc..c508fd1d1e805 100644 --- a/scripts/deploymentconfig/main.go +++ b/scripts/deploymentconfig/main.go @@ -263,7 +263,7 @@ import ( func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { cfg := codersdk.DeploymentConfig{} - return cfg, vip.Unmarshal(cfg) + return cfg, vip.Unmarshal(&cfg) } func NewViper() *viper.Viper { From 65d73b425d3a421898a6feb95609e71269c9790d Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 17:17:46 +0000 Subject: [PATCH 14/43] fix tests --- cli/deployment/config.go | 5 +++-- codersdk/config.go | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 55c29221ab381..55fdbc28c263f 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -42,6 +42,7 @@ func NewViper() *viper.Viper { v.SetDefault("telemetry.enable", flag.Lookup("test.v") == nil) v.SetDefault("telemetry.trace_enable", flag.Lookup("test.v") == nil) v.SetDefault("telemetry.url", "https://telemetry.coder.com") + v.SetDefault("tls_config.tls_client_auth", "request") v.SetDefault("tls_config.tls_min_version", "tls12") v.SetDefault("ssh_keygen_algorithm", "ed25519") v.SetDefault("metrics_cache_refresh_interval", time.Hour) @@ -131,8 +132,8 @@ func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { _ = vip.BindPFlag("tls_config.tls_client_ca_file", flagset.Lookup("tls-client-ca-file")) _ = flagset.StringP("tls-client-auth", "", vip.GetString("tls_config.tls_client_auth"), `Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_CLIENT_AUTH")) _ = vip.BindPFlag("tls_config.tls_client_auth", flagset.Lookup("tls-client-auth")) - _ = flagset.StringArrayP("tls-key-file", "", vip.GetStringSlice("tls_config.tls_key_tiles"), `Paths to the private keys for each of the certificates. It requires a PEM-encoded file.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_KEY_TILES")) - _ = vip.BindPFlag("tls_config.tls_key_tiles", flagset.Lookup("tls-key-file")) + _ = flagset.StringArrayP("tls-key-file", "", vip.GetStringSlice("tls_config.tls_key_files"), `Paths to the private keys for each of the certificates. It requires a PEM-encoded file.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_KEY_FILES")) + _ = vip.BindPFlag("tls_config.tls_key_files", flagset.Lookup("tls-key-file")) _ = flagset.StringP("tls-min-version", "", vip.GetString("tls_config.tls_min_version"), `Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13"`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_MIN_VERSION")) _ = vip.BindPFlag("tls_config.tls_min_version", flagset.Lookup("tls-min-version")) _ = flagset.BoolP("trace", "", vip.GetBool("trace_enable"), `Whether application tracing data is collected.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TRACE_ENABLE")) diff --git a/codersdk/config.go b/codersdk/config.go index 2a8ffdeb6206a..79b757dab678a 100644 --- a/codersdk/config.go +++ b/codersdk/config.go @@ -225,12 +225,13 @@ type TLSConfig struct { // Usage: PEM-encoded Certificate Authority file used for checking the authenticity of client // Flag: tls-client-ca-file ClientCAFile string `mapstructure:"tls_client_ca_file" json:"tls_client_ca_file"` - // Usage: Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". - // Flag: tls-client-auth + // Usage: Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". + // Flag: tls-client-auth + // Default: "request" ClientAuth string `mapstructure:"tls_client_auth" json:"tls_client_auth"` // Usage: Paths to the private keys for each of the certificates. It requires a PEM-encoded file. // Flag: tls-key-file - KeyFiles []string `mapstructure:"tls_key_tiles" json:"tls_key_tiles"` + KeyFiles []string `mapstructure:"tls_key_files" json:"tls_key_files"` // Usage: Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" // Flag: tls-min-version // Default: "tls12" From c098307483ad54702b8cf69aa3c2a1234b1c05c4 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 17:26:16 +0000 Subject: [PATCH 15/43] docs --- codersdk/config.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/codersdk/config.go b/codersdk/config.go index 79b757dab678a..162d584dd2aa0 100644 --- a/codersdk/config.go +++ b/codersdk/config.go @@ -9,6 +9,16 @@ import ( "golang.org/x/xerrors" ) +// DeploymentConfig is the central configuration for the coder server. +// Secret values should specify `json:"-"` to prevent them from being returned by the API. +// All config values can be set via environment variables in the form of `CODER_` with `.` and `-` replaced by `_`. +// Optional doc comments above fields will generate CLI commands with the following options: +// Usage: - Describe what the setting field does (required) +// Flag: - Long flag name (required) +// Shorthand: - Single character shorthand flag name (optional) +// Default: - Default value for the field as you would write in go code (ex. "string", int, time.Minute, []string{"one", "two"}) (optional) +// Enterprise - Whether or not the field is only available in enterprise (optional) +// Hidden - Whether or not the field should be hidden from the CLI (optional) type DeploymentConfig struct { // Usage: External URL to access your deployment. This must be accessible by all provisioned workspaces. // Flag: access-url From de46a070ad6e1417ab1b418dd2920a2767fd42f1 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 17:27:09 +0000 Subject: [PATCH 16/43] remove script test --- scripts/deploymentconfig/main_test.go | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 scripts/deploymentconfig/main_test.go diff --git a/scripts/deploymentconfig/main_test.go b/scripts/deploymentconfig/main_test.go deleted file mode 100644 index c3eeea0028326..0000000000000 --- a/scripts/deploymentconfig/main_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/coder/coder/testutil" -) - -func TestCliGen(t *testing.T) { - t.Parallel() - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - cb, err := GenerateData(ctx, "../../codersdk") - require.NoError(t, err) - require.NotNil(t, cb) -} From 65d7d5687a7d44dce432f58a3ae6b9f5f8b2892f Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 17:29:22 +0000 Subject: [PATCH 17/43] fix gen --- cli/deployment/config.go | 2 +- scripts/deploymentconfig/main.go | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 55fdbc28c263f..8fc17606f8c2a 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -1,6 +1,6 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by the script at scripts/deploymentconfig/main.go -// The data for populating this file is from codersdk.. +// The data for populating this file is from codersdk.DeploymentConfig. package deployment import ( diff --git a/scripts/deploymentconfig/main.go b/scripts/deploymentconfig/main.go index c508fd1d1e805..abf6f6d9a384d 100644 --- a/scripts/deploymentconfig/main.go +++ b/scripts/deploymentconfig/main.go @@ -54,12 +54,12 @@ func GenerateData(ctx context.Context, dir string) (*Data, error) { return nil, xerrors.Errorf("parse package %q: %w", dir, err) } - codeBlocks, err := generateAll(pkg) + data, err := generateAll(pkg) if err != nil { return nil, xerrors.Errorf("parse package %q: %w", dir, err) } - return codeBlocks, nil + return data, nil } // parsePackage takes a list of patterns such as a directory, and parses them. @@ -88,7 +88,9 @@ func parsePackage(ctx context.Context, patterns ...string) (*packages.Package, e } func generateAll(pkg *packages.Package) (*Data, error) { - cb := Data{} + cb := Data{ + StructTarget: StructTarget, + } structs := make(map[string]*ast.StructType) for _, file := range pkg.Syntax { for _, decl := range file.Decls { From 3826228ac0c9eb1cccf78026bdb08beccca76527 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 17:33:21 +0000 Subject: [PATCH 18/43] fix gen --- scripts/apitypings/main.go | 4 + site/src/api/typesGenerated.ts | 208 +++++++++++++++------------------ 2 files changed, 96 insertions(+), 116 deletions(-) diff --git a/scripts/apitypings/main.go b/scripts/apitypings/main.go index 720a828f100bf..20ff1ba1f8250 100644 --- a/scripts/apitypings/main.go +++ b/scripts/apitypings/main.go @@ -441,6 +441,10 @@ func (g *Generator) buildStruct(obj types.Object, st *types.Struct) (string, err jsonOptional bool ) if err == nil { + if jsonTag.Name == "-" { + // Completely ignore this field. + continue + } jsonName = jsonTag.Name if len(jsonTag.Options) > 0 && jsonTag.Options[0] == "omitempty" { jsonOptional = true diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index c8540c31888a9..2907a96108da2 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -126,19 +126,6 @@ export interface AzureInstanceIdentityToken { readonly encoding: string } -// From codersdk/flags.go -export interface BoolFlag { - readonly name: string - readonly flag: string - readonly env_var: string - readonly shorthand: string - readonly description: string - readonly enterprise: boolean - readonly hidden: boolean - readonly default: boolean - readonly value: boolean -} - // From codersdk/buildinfo.go export interface BuildInfoResponse { readonly external_url: string @@ -258,81 +245,63 @@ export interface DAUEntry { readonly amount: number } +// From codersdk/config.go +export interface DERPConfig { + readonly server: DERPServerConfig + readonly config: DERPConfigConfig +} + +// From codersdk/config.go +export interface DERPConfigConfig { + readonly url: string + readonly path: string +} + // From codersdk/workspaceagents.go export interface DERPRegion { readonly preferred: boolean readonly latency_ms: number } -// From codersdk/flags.go -export interface DeploymentFlags { - readonly access_url: StringFlag - readonly wildcard_access_url: StringFlag - readonly address: StringFlag - readonly autobuild_poll_interval: DurationFlag - readonly derp_server_enabled: BoolFlag - readonly derp_server_region_id: IntFlag - readonly derp_server_region_code: StringFlag - readonly derp_server_region_name: StringFlag - readonly derp_server_stun_address: StringArrayFlag - readonly derp_server_relay_address: StringFlag - readonly derp_config_url: StringFlag - readonly derp_config_path: StringFlag - readonly prom_enabled: BoolFlag - readonly prom_address: StringFlag - readonly pprof_enabled: BoolFlag - readonly pprof_address: StringFlag - readonly cache_dir: StringFlag - readonly in_memory_database: BoolFlag - readonly provisioner_daemon_count: IntFlag - readonly postgres_url: StringFlag - readonly oauth2_github_client_id: StringFlag - readonly oauth2_github_client_secret: StringFlag - readonly oauth2_github_allowed_organizations: StringArrayFlag - readonly oauth2_github_allowed_teams: StringArrayFlag - readonly oauth2_github_allow_signups: BoolFlag - readonly oauth2_github_enterprise_base_url: StringFlag - readonly oidc_allow_signups: BoolFlag - readonly oidc_client_id: StringFlag - readonly oidc_client_secret: StringFlag - readonly oidc_email_domain: StringFlag - readonly oidc_issuer_url: StringFlag - readonly oidc_scopes: StringArrayFlag - readonly telemetry_enable: BoolFlag - readonly telemetry_trace_enable: BoolFlag - readonly telemetry_url: StringFlag - readonly tls_enable: BoolFlag - readonly tls_cert_files: StringArrayFlag - readonly tls_client_ca_file: StringFlag - readonly tls_client_auth: StringFlag - readonly tls_key_files: StringArrayFlag - readonly tls_min_version: StringFlag - readonly trace_enable: BoolFlag - readonly secure_auth_cookie: BoolFlag - readonly ssh_keygen_algorithm: StringFlag - readonly auto_import_templates: StringArrayFlag - readonly metrics_cache_refresh_interval: DurationFlag - readonly agent_stat_refresh_interval: DurationFlag - readonly verbose: BoolFlag - readonly audit_logging: BoolFlag - readonly browser_only: BoolFlag - readonly scim_auth_header: StringFlag - readonly user_workspace_quota: IntFlag -} - -// From codersdk/flags.go -export interface DurationFlag { - readonly name: string - readonly flag: string - readonly env_var: string - readonly shorthand: string - readonly description: string - readonly enterprise: boolean - readonly hidden: boolean +// From codersdk/config.go +export interface DERPServerConfig { + readonly enabled: boolean + readonly region_id: number + readonly region_code: string + readonly region_name: string + readonly stun_address: string[] + readonly relay_address: string +} + +// From codersdk/config.go +export interface DeploymentConfig { + readonly access_url: string + readonly wildcard_access_url: string + readonly address: string + // This is likely an enum in an external package ("time.Duration") + readonly autobuild_poll_interval: number + readonly derp: DERPConfig + readonly prometheus: PrometheusConfig + readonly pprof: PprofConfig + readonly cache_dir: string + readonly in_memory_database: boolean + readonly provisioner_daemon_count: number + readonly oauth2_github: OAuth2GithubConfig + readonly oidc: OIDCConfig + readonly telemetry: TelemetryConfig + readonly tls_config: TLSConfig + readonly trace_enable: boolean + readonly secure_auth_cookie: boolean + readonly ssh_keygen_algorithm: string + readonly auto_import_templates: string[] // This is likely an enum in an external package ("time.Duration") - readonly default: number + readonly metrics_cache_refresh_interval: number // This is likely an enum in an external package ("time.Duration") - readonly value: number + readonly agent_stat_refresh_interval: number + readonly verbose: boolean + readonly audit_logging: boolean + readonly browser_only: boolean + readonly user_workspace_quota: number } // From codersdk/features.go @@ -387,19 +356,6 @@ export interface Healthcheck { readonly threshold: number } -// From codersdk/flags.go -export interface IntFlag { - readonly name: string - readonly flag: string - readonly env_var: string - readonly shorthand: string - readonly description: string - readonly enterprise: boolean - readonly hidden: boolean - readonly default: number - readonly value: number -} - // From codersdk/licenses.go export interface License { readonly id: number @@ -431,6 +387,24 @@ export interface LoginWithPasswordResponse { readonly session_token: string } +// From codersdk/config.go +export interface OAuth2GithubConfig { + readonly client_id: string + readonly allowed_organizations: string[] + readonly allowed_teams: string[] + readonly allow_signups: boolean + readonly enterprise_base_url: string +} + +// From codersdk/config.go +export interface OIDCConfig { + readonly allow_signups: boolean + readonly client_id: string + readonly email_domain: string + readonly issuer_url: string + readonly scopes: string[] +} + // From codersdk/organizations.go export interface Organization { readonly id: string @@ -496,6 +470,18 @@ export interface PatchGroupRequest { readonly avatar_url?: string } +// From codersdk/config.go +export interface PprofConfig { + readonly enabled: boolean + readonly address: string +} + +// From codersdk/config.go +export interface PrometheusConfig { + readonly enabled: boolean + readonly address: string +} + // From codersdk/provisionerdaemons.go export interface ProvisionerDaemon { readonly id: string @@ -564,31 +550,21 @@ export interface ServerSentEvent { readonly data: any } -// From codersdk/flags.go -export interface StringArrayFlag { - readonly name: string - readonly flag: string - readonly env_var: string - readonly shorthand: string - readonly description: string - readonly enterprise: boolean - readonly hidden: boolean - readonly default: string[] - readonly value: string[] +// From codersdk/config.go +export interface TLSConfig { + readonly tls_enable: boolean + readonly tls_cert_files: string[] + readonly tls_client_ca_file: string + readonly tls_client_auth: string + readonly tls_key_files: string[] + readonly tls_min_version: string } -// From codersdk/flags.go -export interface StringFlag { - readonly name: string - readonly flag: string - readonly env_var: string - readonly shorthand: string - readonly description: string - readonly enterprise: boolean - readonly secret: boolean - readonly hidden: boolean - readonly default: string - readonly value: string +// From codersdk/config.go +export interface TelemetryConfig { + readonly enable: boolean + readonly trace_enable: boolean + readonly url: string } // From codersdk/templates.go From c14f58e15375757cac2bf024e22e0b865d95058b Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 18:58:42 +0000 Subject: [PATCH 19/43] frontend refactor --- site/src/api/typesGenerated.ts | 208 ++++++++++++++++++--------------- 1 file changed, 116 insertions(+), 92 deletions(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 2907a96108da2..c8540c31888a9 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -126,6 +126,19 @@ export interface AzureInstanceIdentityToken { readonly encoding: string } +// From codersdk/flags.go +export interface BoolFlag { + readonly name: string + readonly flag: string + readonly env_var: string + readonly shorthand: string + readonly description: string + readonly enterprise: boolean + readonly hidden: boolean + readonly default: boolean + readonly value: boolean +} + // From codersdk/buildinfo.go export interface BuildInfoResponse { readonly external_url: string @@ -245,63 +258,81 @@ export interface DAUEntry { readonly amount: number } -// From codersdk/config.go -export interface DERPConfig { - readonly server: DERPServerConfig - readonly config: DERPConfigConfig -} - -// From codersdk/config.go -export interface DERPConfigConfig { - readonly url: string - readonly path: string -} - // From codersdk/workspaceagents.go export interface DERPRegion { readonly preferred: boolean readonly latency_ms: number } -// From codersdk/config.go -export interface DERPServerConfig { - readonly enabled: boolean - readonly region_id: number - readonly region_code: string - readonly region_name: string - readonly stun_address: string[] - readonly relay_address: string -} - -// From codersdk/config.go -export interface DeploymentConfig { - readonly access_url: string - readonly wildcard_access_url: string - readonly address: string - // This is likely an enum in an external package ("time.Duration") - readonly autobuild_poll_interval: number - readonly derp: DERPConfig - readonly prometheus: PrometheusConfig - readonly pprof: PprofConfig - readonly cache_dir: string - readonly in_memory_database: boolean - readonly provisioner_daemon_count: number - readonly oauth2_github: OAuth2GithubConfig - readonly oidc: OIDCConfig - readonly telemetry: TelemetryConfig - readonly tls_config: TLSConfig - readonly trace_enable: boolean - readonly secure_auth_cookie: boolean - readonly ssh_keygen_algorithm: string - readonly auto_import_templates: string[] +// From codersdk/flags.go +export interface DeploymentFlags { + readonly access_url: StringFlag + readonly wildcard_access_url: StringFlag + readonly address: StringFlag + readonly autobuild_poll_interval: DurationFlag + readonly derp_server_enabled: BoolFlag + readonly derp_server_region_id: IntFlag + readonly derp_server_region_code: StringFlag + readonly derp_server_region_name: StringFlag + readonly derp_server_stun_address: StringArrayFlag + readonly derp_server_relay_address: StringFlag + readonly derp_config_url: StringFlag + readonly derp_config_path: StringFlag + readonly prom_enabled: BoolFlag + readonly prom_address: StringFlag + readonly pprof_enabled: BoolFlag + readonly pprof_address: StringFlag + readonly cache_dir: StringFlag + readonly in_memory_database: BoolFlag + readonly provisioner_daemon_count: IntFlag + readonly postgres_url: StringFlag + readonly oauth2_github_client_id: StringFlag + readonly oauth2_github_client_secret: StringFlag + readonly oauth2_github_allowed_organizations: StringArrayFlag + readonly oauth2_github_allowed_teams: StringArrayFlag + readonly oauth2_github_allow_signups: BoolFlag + readonly oauth2_github_enterprise_base_url: StringFlag + readonly oidc_allow_signups: BoolFlag + readonly oidc_client_id: StringFlag + readonly oidc_client_secret: StringFlag + readonly oidc_email_domain: StringFlag + readonly oidc_issuer_url: StringFlag + readonly oidc_scopes: StringArrayFlag + readonly telemetry_enable: BoolFlag + readonly telemetry_trace_enable: BoolFlag + readonly telemetry_url: StringFlag + readonly tls_enable: BoolFlag + readonly tls_cert_files: StringArrayFlag + readonly tls_client_ca_file: StringFlag + readonly tls_client_auth: StringFlag + readonly tls_key_files: StringArrayFlag + readonly tls_min_version: StringFlag + readonly trace_enable: BoolFlag + readonly secure_auth_cookie: BoolFlag + readonly ssh_keygen_algorithm: StringFlag + readonly auto_import_templates: StringArrayFlag + readonly metrics_cache_refresh_interval: DurationFlag + readonly agent_stat_refresh_interval: DurationFlag + readonly verbose: BoolFlag + readonly audit_logging: BoolFlag + readonly browser_only: BoolFlag + readonly scim_auth_header: StringFlag + readonly user_workspace_quota: IntFlag +} + +// From codersdk/flags.go +export interface DurationFlag { + readonly name: string + readonly flag: string + readonly env_var: string + readonly shorthand: string + readonly description: string + readonly enterprise: boolean + readonly hidden: boolean // This is likely an enum in an external package ("time.Duration") - readonly metrics_cache_refresh_interval: number + readonly default: number // This is likely an enum in an external package ("time.Duration") - readonly agent_stat_refresh_interval: number - readonly verbose: boolean - readonly audit_logging: boolean - readonly browser_only: boolean - readonly user_workspace_quota: number + readonly value: number } // From codersdk/features.go @@ -356,6 +387,19 @@ export interface Healthcheck { readonly threshold: number } +// From codersdk/flags.go +export interface IntFlag { + readonly name: string + readonly flag: string + readonly env_var: string + readonly shorthand: string + readonly description: string + readonly enterprise: boolean + readonly hidden: boolean + readonly default: number + readonly value: number +} + // From codersdk/licenses.go export interface License { readonly id: number @@ -387,24 +431,6 @@ export interface LoginWithPasswordResponse { readonly session_token: string } -// From codersdk/config.go -export interface OAuth2GithubConfig { - readonly client_id: string - readonly allowed_organizations: string[] - readonly allowed_teams: string[] - readonly allow_signups: boolean - readonly enterprise_base_url: string -} - -// From codersdk/config.go -export interface OIDCConfig { - readonly allow_signups: boolean - readonly client_id: string - readonly email_domain: string - readonly issuer_url: string - readonly scopes: string[] -} - // From codersdk/organizations.go export interface Organization { readonly id: string @@ -470,18 +496,6 @@ export interface PatchGroupRequest { readonly avatar_url?: string } -// From codersdk/config.go -export interface PprofConfig { - readonly enabled: boolean - readonly address: string -} - -// From codersdk/config.go -export interface PrometheusConfig { - readonly enabled: boolean - readonly address: string -} - // From codersdk/provisionerdaemons.go export interface ProvisionerDaemon { readonly id: string @@ -550,21 +564,31 @@ export interface ServerSentEvent { readonly data: any } -// From codersdk/config.go -export interface TLSConfig { - readonly tls_enable: boolean - readonly tls_cert_files: string[] - readonly tls_client_ca_file: string - readonly tls_client_auth: string - readonly tls_key_files: string[] - readonly tls_min_version: string +// From codersdk/flags.go +export interface StringArrayFlag { + readonly name: string + readonly flag: string + readonly env_var: string + readonly shorthand: string + readonly description: string + readonly enterprise: boolean + readonly hidden: boolean + readonly default: string[] + readonly value: string[] } -// From codersdk/config.go -export interface TelemetryConfig { - readonly enable: boolean - readonly trace_enable: boolean - readonly url: string +// From codersdk/flags.go +export interface StringFlag { + readonly name: string + readonly flag: string + readonly env_var: string + readonly shorthand: string + readonly description: string + readonly enterprise: boolean + readonly secret: boolean + readonly hidden: boolean + readonly default: string + readonly value: string } // From codersdk/templates.go From e306ff1b9310e516cbddca641398513beb47cda6 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 20:54:58 +0000 Subject: [PATCH 20/43] back to reflect --- cli/deployment/config.go | 445 +++++++++++++++++++++---------- codersdk/config.go | 244 ++++++++--------- scripts/deploymentconfig/main.go | 320 ---------------------- 3 files changed, 419 insertions(+), 590 deletions(-) delete mode 100644 scripts/deploymentconfig/main.go diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 8fc17606f8c2a..1b3d9b7f35993 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -1,12 +1,12 @@ -// Code generated by go generate; DO NOT EDIT. -// This file was generated by the script at scripts/deploymentconfig/main.go -// The data for populating this file is from codersdk.DeploymentConfig. package deployment import ( "flag" + "fmt" "os" "path/filepath" + "reflect" + "strings" "time" "github.com/coreos/go-oidc/v3/oidc" @@ -17,155 +17,325 @@ import ( "github.com/coder/coder/codersdk" ) +func newConfig() codersdk.DeploymentConfig { + return codersdk.DeploymentConfig{ + AccessURL: codersdk.DeploymentConfigField[string]{ + Usage: "External URL to access your deployment. This must be accessible by all provisioned workspaces.", + Flag: "access-url", + }, + WildcardAccessURL: codersdk.DeploymentConfigField[string]{ + Usage: "Specifies the wildcard hostname to use for workspace applications in the form \"*.example.com\".", + Flag: "wildcard-access-url", + }, + Address: codersdk.DeploymentConfigField[string]{ + Usage: "Bind address of the server.", + Flag: "address", + Shorthand: "a", + Value: "127.0.0.1:3000", + }, + AutobuildPollInterval: codersdk.DeploymentConfigField[time.Duration]{ + Usage: "Interval to poll for scheduled workspace builds.", + Flag: "autobuild-poll-interval", + Hidden: true, + Value: time.Minute, + }, + DERPServerEnable: codersdk.DeploymentConfigField[bool]{ + Usage: "Whether to enable or disable the embedded DERP relay server.", + Flag: "derp-server-enable", + Value: true, + }, + DERPServerRegionID: codersdk.DeploymentConfigField[int]{ + Usage: "Region ID to use for the embedded DERP server.", + Flag: "derp-server-region-id", + Value: 999, + }, + DERPServerRegionCode: codersdk.DeploymentConfigField[string]{ + Usage: "Region code to use for the embedded DERP server.", + Flag: "derp-server-region-code", + Value: "coder", + }, + DERPServerRegionName: codersdk.DeploymentConfigField[string]{ + Usage: "Region name that for the embedded DERP server.", + Flag: "derp-server-region-name", + Value: "Coder Embedded Relay", + }, + DERPServerSTUNAddresses: codersdk.DeploymentConfigField[[]string]{ + Usage: "Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections.", + Flag: "derp-server-stun-addresses", + Value: []string{"stun.l.google.com:19302"}, + }, + DERPServerRelayAddress: codersdk.DeploymentConfigField[string]{ + Usage: "An HTTP address that is accessible by other replicas to relay DERP traffic. Required for high availability.", + Flag: "derp-server-relay-address", + Enterprise: true, + }, + DERPConfigURL: codersdk.DeploymentConfigField[string]{ + Usage: "URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/", + Flag: "derp-config-url", + }, + DERPConfigPath: codersdk.DeploymentConfigField[string]{ + Usage: "Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/", + Flag: "derp-config-path", + }, + PrometheusEnable: codersdk.DeploymentConfigField[bool]{ + Usage: "Serve prometheus metrics on the address defined by prometheus address.", + Flag: "prometheus-enable", + }, + PrometheusAddress: codersdk.DeploymentConfigField[string]{ + Usage: "The bind address to serve prometheus metrics.", + Flag: "prometheus-address", + Value: "127.0.0.1:2112", + }, + PprofEnable: codersdk.DeploymentConfigField[bool]{ + Usage: "Serve pprof metrics on the address defined by pprof address.", + Flag: "pprof-enable", + }, + PprofAddress: codersdk.DeploymentConfigField[string]{ + Usage: "The bind address to serve pprof.", + Flag: "pprof-address", + Value: "127.0.0.1:6060", + }, + CacheDir: codersdk.DeploymentConfigField[string]{ + Usage: "The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.", + Flag: "cache-dir", + Value: defaultCacheDir(), + }, + InMemoryDatabase: codersdk.DeploymentConfigField[bool]{ + Usage: "Controls whether data will be stored in an in-memory database.", + Flag: "in-memory", + Hidden: true, + }, + ProvisionerDaemonCount: codersdk.DeploymentConfigField[int]{ + Usage: "Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this.", + Flag: "provisioner-daemons", + Value: 3, + }, + PostgresURL: codersdk.DeploymentConfigField[string]{ + Usage: "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\".", + Flag: "postgres-url", + }, + OAuth2GithubClientID: codersdk.DeploymentConfigField[string]{ + Usage: "Client ID for Login with GitHub.", + Flag: "oauth2-github-client-id", + }, + OAuth2GithubClientSecret: codersdk.DeploymentConfigField[string]{ + Usage: "Client secret for Login with GitHub.", + Flag: "oauth2-github-client-secret", + }, + OAuth2GithubAllowedOrganizations: codersdk.DeploymentConfigField[[]string]{ + Usage: "Organizations the user must be a member of to Login with GitHub.", + Flag: "oauth2-github-allowed-orgs", + }, + OAuth2GithubAllowedTeams: codersdk.DeploymentConfigField[[]string]{ + Usage: "Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /.", + Flag: "oauth2-github-allowed-teams", + }, + OAuth2GithubAllowSignups: codersdk.DeploymentConfigField[bool]{ + Usage: "Whether new users can sign up with GitHub.", + Flag: "oauth2-github-allow-signups", + }, + OAuth2GithubEnterpriseBaseURL: codersdk.DeploymentConfigField[string]{ + Usage: "Base URL of a GitHub Enterprise deployment to use for Login with GitHub.", + Flag: "oauth2-github-enterprise-base-url", + }, + OIDCAllowSignups: codersdk.DeploymentConfigField[bool]{ + Usage: "Whether new users can sign up with OIDC.", + Flag: "oidc-allow-signups", + Value: true, + }, + OIDCClientID: codersdk.DeploymentConfigField[string]{ + Usage: "Client ID to use for Login with OIDC.", + Flag: "oidc-client-id", + }, + OIDCClientSecret: codersdk.DeploymentConfigField[string]{ + Usage: "Client secret to use for Login with OIDC.", + Flag: "oidc-client-secret", + }, + OIDCEmailDomain: codersdk.DeploymentConfigField[string]{ + Usage: "Email domain that clients logging in with OIDC must match.", + Flag: "oidc-email-domain", + }, + OIDCIssuerURL: codersdk.DeploymentConfigField[string]{ + Usage: "Issuer URL to use for Login with OIDC.", + Flag: "oidc-issuer-url", + }, + OIDCScopes: codersdk.DeploymentConfigField[[]string]{ + Usage: "Scopes to grant when authenticating with OIDC.", + Flag: "oidc-scopes", + Value: []string{oidc.ScopeOpenID, "profile", "email"}, + }, + TelemetryEnable: codersdk.DeploymentConfigField[bool]{ + Usage: "Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product.", + Flag: "telemetry", + Value: flag.Lookup("test.v") == nil, + }, + TelemetryTraceEnable: codersdk.DeploymentConfigField[bool]{ + Usage: "Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option.", + Flag: "telemetry-trace", + Value: flag.Lookup("test.v") == nil, + }, + TelemetryURL: codersdk.DeploymentConfigField[string]{ + Usage: "URL to send telemetry.", + Flag: "telemetry-url", + Hidden: true, + Value: "https://telemetry.coder.com", + }, + TLSEnable: codersdk.DeploymentConfigField[bool]{ + Usage: "Whether TLS will be enabled.", + Flag: "tls-enable", + }, + TLSCertFiles: codersdk.DeploymentConfigField[[]string]{ + Usage: "Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file.", + Flag: "tls-cert-file", + }, + TLSClientCAFile: codersdk.DeploymentConfigField[string]{ + Usage: "PEM-encoded Certificate Authority file used for checking the authenticity of client", + Flag: "tls-client-ca-file", + }, + TLSClientAuth: codersdk.DeploymentConfigField[string]{ + Usage: "Policy the server will follow for TLS Client Authentication. Accepted values are \"none\", \"request\", \"require-any\", \"verify-if-given\", or \"require-and-verify\".", + Flag: "tls-client-auth", + Value: "request", + }, + TLSKeyFiles: codersdk.DeploymentConfigField[[]string]{ + Usage: "Paths to the private keys for each of the certificates. It requires a PEM-encoded file.", + Flag: "tls-key-file", + }, + TLSMinVersion: codersdk.DeploymentConfigField[string]{ + Usage: "Minimum supported version of TLS. Accepted values are \"tls10\", \"tls11\", \"tls12\" or \"tls13\"", + Flag: "tls-min-version", + Value: "tls12", + }, + TraceEnable: codersdk.DeploymentConfigField[bool]{ + Usage: "Whether application tracing data is collected.", + Flag: "trace", + }, + SecureAuthCookie: codersdk.DeploymentConfigField[bool]{ + Usage: "Controls if the 'Secure' property is set on browser session cookies.", + Flag: "secure-auth-cookie", + }, + SSHKeygenAlgorithm: codersdk.DeploymentConfigField[string]{ + Usage: "The algorithm to use for generating ssh keys. Accepted values are \"ed25519\", \"ecdsa\", or \"rsa4096\".", + Flag: "ssh-keygen-algorithm", + Value: "ed25519", + }, + AutoImportTemplates: codersdk.DeploymentConfigField[[]string]{ + Usage: "Templates to auto-import. Available auto-importable templates are: kubernetes", + Flag: "auto-import-template", + Hidden: true, + }, + MetricsCacheRefreshInterval: codersdk.DeploymentConfigField[time.Duration]{ + Usage: "How frequently metrics are refreshed", + Flag: "metrics-cache-refresh-interval", + Hidden: true, + Value: time.Hour, + }, + AgentStatRefreshInterval: codersdk.DeploymentConfigField[time.Duration]{ + Usage: "How frequently agent stats are recorded", + Flag: "agent-stats-refresh-interval", + Hidden: true, + Value: 10 * time.Minute, + }, + Verbose: codersdk.DeploymentConfigField[bool]{ + Usage: "Enables verbose logging.", + Flag: "verbose", + Shorthand: "v", + }, + AuditLogging: codersdk.DeploymentConfigField[bool]{ + Usage: "Specifies whether audit logging is enabled.", + Flag: "audit-logging", + Value: true, + Enterprise: true, + }, + BrowserOnly: codersdk.DeploymentConfigField[bool]{ + Usage: "Whether Coder only allows connections to workspaces via the browser.", + Flag: "browser-only", + Enterprise: true, + }, + SCIMAuthHeader: codersdk.DeploymentConfigField[string]{ + Usage: "Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication.", + Flag: "scim-auth-header", + Enterprise: true, + }, + UserWorkspaceQuota: codersdk.DeploymentConfigField[int]{ + Usage: "Enables and sets a limit on how many workspaces each user can create.", + Flag: "user-workspace-quota", + Enterprise: true, + }, + } +} + func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { cfg := codersdk.DeploymentConfig{} return cfg, vip.Unmarshal(&cfg) } func NewViper() *viper.Viper { + dc := newConfig() v := viper.New() v.SetEnvPrefix("coder") v.AutomaticEnv() - v.SetDefault("address", "127.0.0.1:3000") - v.SetDefault("autobuild_poll_interval", time.Minute) - v.SetDefault("derp.server.enabled", true) - v.SetDefault("derp.server.region_id", 999) - v.SetDefault("derp.server.region_code", "coder") - v.SetDefault("derp.server.region_name", "Coder Embedded Relay") - v.SetDefault("derp.server.stun_address", []string{"stun.l.google.com:19302"}) - v.SetDefault("prometheus.address", "127.0.0.1:2112") - v.SetDefault("pprof.address", "127.0.0.1:6060") - v.SetDefault("cache_dir", defaultCacheDir()) - v.SetDefault("provisioner_daemon_count", 3) - v.SetDefault("oidc.allow_signups", true) - v.SetDefault("oidc.scopes", []string{oidc.ScopeOpenID, "profile", "email"}) - v.SetDefault("telemetry.enable", flag.Lookup("test.v") == nil) - v.SetDefault("telemetry.trace_enable", flag.Lookup("test.v") == nil) - v.SetDefault("telemetry.url", "https://telemetry.coder.com") - v.SetDefault("tls_config.tls_client_auth", "request") - v.SetDefault("tls_config.tls_min_version", "tls12") - v.SetDefault("ssh_keygen_algorithm", "ed25519") - v.SetDefault("metrics_cache_refresh_interval", time.Hour) - v.SetDefault("agent_stat_refresh_interval", 10 * time.Minute) - v.SetDefault("audit_logging", true) + + dcv := reflect.ValueOf(dc).Elem() + t := dcv.Type() + for i := 0; i < t.NumField(); i++ { + fv := dcv.Field(i) + fve := fv.Elem() + key := fve.FieldByName("Key").String() + value := fve.FieldByName("Value").Interface() + v.SetDefault(key, value) + } return v } -func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { - _ = flagset.StringP("access-url", "", vip.GetString("access_url"), `External URL to access your deployment. This must be accessible by all provisioned workspaces.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_ACCESS_URL")) - _ = vip.BindPFlag("access_url", flagset.Lookup("access-url")) - _ = flagset.StringP("wildcard-access-url", "", vip.GetString("wildcard_access_url"), `Specifies the wildcard hostname to use for workspace applications in the form "*.example.com".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_WILDCARD_ACCESS_URL")) - _ = vip.BindPFlag("wildcard_access_url", flagset.Lookup("wildcard-access-url")) - _ = flagset.StringP("address", "a", vip.GetString("address"), `Bind address of the server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_ADDRESS")) - _ = vip.BindPFlag("address", flagset.Lookup("address")) - _ = flagset.DurationP("autobuild-poll-interval", "", vip.GetDuration("autobuild_poll_interval"), `Interval to poll for scheduled workspace builds.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_AUTOBUILD_POLL_INTERVAL")) - _ = vip.BindPFlag("autobuild_poll_interval", flagset.Lookup("autobuild-poll-interval")) - _ = flagset.MarkHidden("autobuild-poll-interval") - _ = flagset.BoolP("derp-server-enable", "", vip.GetBool("derp.server.enabled"), `Whether to enable or disable the embedded DERP relay server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_SERVER_ENABLED")) - _ = vip.BindPFlag("derp.server.enabled", flagset.Lookup("derp-server-enable")) - _ = flagset.IntP("derp-server-region-id", "", vip.GetInt("derp.server.region_id"), `Region ID to use for the embedded DERP server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_SERVER_REGION_ID")) - _ = vip.BindPFlag("derp.server.region_id", flagset.Lookup("derp-server-region-id")) - _ = flagset.StringP("derp-server-region-code", "", vip.GetString("derp.server.region_code"), `Region code to use for the embedded DERP server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_SERVER_REGION_CODE")) - _ = vip.BindPFlag("derp.server.region_code", flagset.Lookup("derp-server-region-code")) - _ = flagset.StringP("derp-server-region-name", "", vip.GetString("derp.server.region_name"), `Region name that for the embedded DERP server.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_SERVER_REGION_NAME")) - _ = vip.BindPFlag("derp.server.region_name", flagset.Lookup("derp-server-region-name")) - _ = flagset.StringArrayP("derp-server-stun-addresses", "", vip.GetStringSlice("derp.server.stun_address"), `Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_SERVER_STUN_ADDRESS")) - _ = vip.BindPFlag("derp.server.stun_address", flagset.Lookup("derp-server-stun-addresses")) - _ = flagset.StringP("derp-config-url", "", vip.GetString("derp.config.url"), `URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_CONFIG_URL")) - _ = vip.BindPFlag("derp.config.url", flagset.Lookup("derp-config-url")) - _ = flagset.StringP("derp-config-path", "", vip.GetString("derp.config.path"), `Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_DERP_CONFIG_PATH")) - _ = vip.BindPFlag("derp.config.path", flagset.Lookup("derp-config-path")) - _ = flagset.BoolP("prometheus-enable", "", vip.GetBool("prometheus.enabled"), `Serve prometheus metrics on the address defined by prometheus address.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PROMETHEUS_ENABLED")) - _ = vip.BindPFlag("prometheus.enabled", flagset.Lookup("prometheus-enable")) - _ = flagset.StringP("prometheus-address", "", vip.GetString("prometheus.address"), `The bind address to serve prometheus metrics.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PROMETHEUS_ADDRESS")) - _ = vip.BindPFlag("prometheus.address", flagset.Lookup("prometheus-address")) - _ = flagset.BoolP("pprof-enable", "", vip.GetBool("pprof.enabled"), `Serve pprof metrics on the address defined by pprof address.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PPROF_ENABLED")) - _ = vip.BindPFlag("pprof.enabled", flagset.Lookup("pprof-enable")) - _ = flagset.StringP("pprof-address", "", vip.GetString("pprof.address"), `The bind address to serve pprof.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PPROF_ADDRESS")) - _ = vip.BindPFlag("pprof.address", flagset.Lookup("pprof-address")) - _ = flagset.StringP("cache-dir", "", vip.GetString("cache_dir"), `The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_CACHE_DIR")) - _ = vip.BindPFlag("cache_dir", flagset.Lookup("cache-dir")) - _ = flagset.BoolP("in-memory", "", vip.GetBool("in_memory_database"), `Controls whether data will be stored in an in-memory database.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_IN_MEMORY_DATABASE")) - _ = vip.BindPFlag("in_memory_database", flagset.Lookup("in-memory")) - _ = flagset.MarkHidden("in-memory") - _ = flagset.IntP("provisioner-daemons", "", vip.GetInt("provisioner_daemon_count"), `Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_PROVISIONER_DAEMON_COUNT")) - _ = vip.BindPFlag("provisioner_daemon_count", flagset.Lookup("provisioner-daemons")) - _ = flagset.StringP("postgres-url", "", vip.GetString("postgres_url"), `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".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_POSTGRES_URL")) - _ = vip.BindPFlag("postgres_url", flagset.Lookup("postgres-url")) - _ = flagset.StringP("oauth2-github-client-id", "", vip.GetString("oauth2_github.client_id"), `Client ID for Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB_CLIENT_ID")) - _ = vip.BindPFlag("oauth2_github.client_id", flagset.Lookup("oauth2-github-client-id")) - _ = flagset.StringP("oauth2-github-client-secret", "", vip.GetString("oauth2_github.client_secret"), `Client secret for Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB_CLIENT_SECRET")) - _ = vip.BindPFlag("oauth2_github.client_secret", flagset.Lookup("oauth2-github-client-secret")) - _ = flagset.StringArrayP("oauth2-github-allowed-orgs", "", vip.GetStringSlice("oauth2_github.allowed_organizations"), `Organizations the user must be a member of to Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB_ALLOWED_ORGANIZATIONS")) - _ = vip.BindPFlag("oauth2_github.allowed_organizations", flagset.Lookup("oauth2-github-allowed-orgs")) - _ = flagset.StringArrayP("oauth2-github-allowed-teams", "", vip.GetStringSlice("oauth2_github.allowed_teams"), `Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB_ALLOWED_TEAMS")) - _ = vip.BindPFlag("oauth2_github.allowed_teams", flagset.Lookup("oauth2-github-allowed-teams")) - _ = flagset.BoolP("oauth2-github-allow-signups", "", vip.GetBool("oauth2_github.allow_signups"), `Whether new users can sign up with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS")) - _ = vip.BindPFlag("oauth2_github.allow_signups", flagset.Lookup("oauth2-github-allow-signups")) - _ = flagset.StringP("oauth2-github-enterprise-base-url", "", vip.GetString("oauth2_github.enterprise_base_url"), `Base URL of a GitHub Enterprise deployment to use for Login with GitHub.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OAUTH2_GITHUB_ENTERPRISE_BASE_URL")) - _ = vip.BindPFlag("oauth2_github.enterprise_base_url", flagset.Lookup("oauth2-github-enterprise-base-url")) - _ = flagset.BoolP("oidc-allow-signups", "", vip.GetBool("oidc.allow_signups"), `Whether new users can sign up with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC_ALLOW_SIGNUPS")) - _ = vip.BindPFlag("oidc.allow_signups", flagset.Lookup("oidc-allow-signups")) - _ = flagset.StringP("oidc-client-id", "", vip.GetString("oidc.client_id"), `Client ID to use for Login with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC_CLIENT_ID")) - _ = vip.BindPFlag("oidc.client_id", flagset.Lookup("oidc-client-id")) - _ = flagset.StringP("oidc-client-secret", "", vip.GetString("oidc.cliet_secret"), `Client secret to use for Login with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC_CLIET_SECRET")) - _ = vip.BindPFlag("oidc.cliet_secret", flagset.Lookup("oidc-client-secret")) - _ = flagset.StringP("oidc-email-domain", "", vip.GetString("oidc.email_domain"), `Email domain that clients logging in with OIDC must match.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC_EMAIL_DOMAIN")) - _ = vip.BindPFlag("oidc.email_domain", flagset.Lookup("oidc-email-domain")) - _ = flagset.StringP("oidc-issuer-url", "", vip.GetString("oidc.issuer_url"), `Issuer URL to use for Login with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC_ISSUER_URL")) - _ = vip.BindPFlag("oidc.issuer_url", flagset.Lookup("oidc-issuer-url")) - _ = flagset.StringArrayP("oidc-scopes", "", vip.GetStringSlice("oidc.scopes"), `Scopes to grant when authenticating with OIDC.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_OIDC_SCOPES")) - _ = vip.BindPFlag("oidc.scopes", flagset.Lookup("oidc-scopes")) - _ = flagset.BoolP("telemetry", "", vip.GetBool("telemetry.enable"), `Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TELEMETRY_ENABLE")) - _ = vip.BindPFlag("telemetry.enable", flagset.Lookup("telemetry")) - _ = flagset.BoolP("telemetry-trace", "", vip.GetBool("telemetry.trace_enable"), `Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TELEMETRY_TRACE_ENABLE")) - _ = vip.BindPFlag("telemetry.trace_enable", flagset.Lookup("telemetry-trace")) - _ = flagset.StringP("telemetry-url", "", vip.GetString("telemetry.url"), `URL to send telemetry.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TELEMETRY_URL")) - _ = vip.BindPFlag("telemetry.url", flagset.Lookup("telemetry-url")) - _ = flagset.MarkHidden("telemetry-url") - _ = flagset.BoolP("tls-enable", "", vip.GetBool("tls_config.tls_enable"), `Whether TLS will be enabled.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_ENABLE")) - _ = vip.BindPFlag("tls_config.tls_enable", flagset.Lookup("tls-enable")) - _ = flagset.StringArrayP("tls-cert-file", "", vip.GetStringSlice("tls_config.tls_cert_files"), `Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_CERT_FILES")) - _ = vip.BindPFlag("tls_config.tls_cert_files", flagset.Lookup("tls-cert-file")) - _ = flagset.StringP("tls-client-ca-file", "", vip.GetString("tls_config.tls_client_ca_file"), `PEM-encoded Certificate Authority file used for checking the authenticity of client`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_CLIENT_CA_FILE")) - _ = vip.BindPFlag("tls_config.tls_client_ca_file", flagset.Lookup("tls-client-ca-file")) - _ = flagset.StringP("tls-client-auth", "", vip.GetString("tls_config.tls_client_auth"), `Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_CLIENT_AUTH")) - _ = vip.BindPFlag("tls_config.tls_client_auth", flagset.Lookup("tls-client-auth")) - _ = flagset.StringArrayP("tls-key-file", "", vip.GetStringSlice("tls_config.tls_key_files"), `Paths to the private keys for each of the certificates. It requires a PEM-encoded file.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_KEY_FILES")) - _ = vip.BindPFlag("tls_config.tls_key_files", flagset.Lookup("tls-key-file")) - _ = flagset.StringP("tls-min-version", "", vip.GetString("tls_config.tls_min_version"), `Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13"`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TLS_CONFIG_TLS_MIN_VERSION")) - _ = vip.BindPFlag("tls_config.tls_min_version", flagset.Lookup("tls-min-version")) - _ = flagset.BoolP("trace", "", vip.GetBool("trace_enable"), `Whether application tracing data is collected.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_TRACE_ENABLE")) - _ = vip.BindPFlag("trace_enable", flagset.Lookup("trace")) - _ = flagset.BoolP("secure-auth-cookie", "", vip.GetBool("secure_auth_cookie"), `Controls if the 'Secure' property is set on browser session cookies.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_SECURE_AUTH_COOKIE")) - _ = vip.BindPFlag("secure_auth_cookie", flagset.Lookup("secure-auth-cookie")) - _ = flagset.StringP("ssh-keygen-algorithm", "", vip.GetString("ssh_keygen_algorithm"), `The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096".`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_SSH_KEYGEN_ALGORITHM")) - _ = vip.BindPFlag("ssh_keygen_algorithm", flagset.Lookup("ssh-keygen-algorithm")) - _ = flagset.StringArrayP("auto-import-template", "", vip.GetStringSlice("auto_import_templates"), `Templates to auto-import. Available auto-importable templates are: kubernetes`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_AUTO_IMPORT_TEMPLATES")) - _ = vip.BindPFlag("auto_import_templates", flagset.Lookup("auto-import-template")) - _ = flagset.MarkHidden("auto-import-template") - _ = flagset.DurationP("metrics-cache-refresh-interval", "", vip.GetDuration("metrics_cache_refresh_interval"), `How frequently metrics are refreshed`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_METRICS_CACHE_REFRESH_INTERVAL")) - _ = vip.BindPFlag("metrics_cache_refresh_interval", flagset.Lookup("metrics-cache-refresh-interval")) - _ = flagset.MarkHidden("metrics-cache-refresh-interval") - _ = flagset.DurationP("agent-stats-refresh-interval", "", vip.GetDuration("agent_stat_refresh_interval"), `How frequently agent stats are recorded`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_AGENT_STAT_REFRESH_INTERVAL")) - _ = vip.BindPFlag("agent_stat_refresh_interval", flagset.Lookup("agent-stats-refresh-interval")) - _ = flagset.MarkHidden("agent-stats-refresh-interval") - _ = flagset.BoolP("verbose", "v", vip.GetBool("verbose"), `Enables verbose logging.`+"\n"+cliui.Styles.Placeholder.Render("Consumes $CODER_VERBOSE")) - _ = vip.BindPFlag("verbose", flagset.Lookup("verbose")) +//nolint:revive +func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper, enterprise bool) { + dc := newConfig() + dcv := reflect.ValueOf(dc).Elem() + t := dcv.Type() + for i := 0; i < t.NumField(); i++ { + fv := dcv.Field(i) + fve := fv.Elem() + isEnt := fve.FieldByName("Enterprise").Bool() + if enterprise != isEnt { + continue + } + key := fve.FieldByName("Key").String() + flg := fve.FieldByName("Flag").String() + if flg == "" { + continue + } + usage := fve.FieldByName("Usage").String() + usage = fmt.Sprintf("%s\n%s", usage, cliui.Styles.Placeholder.Render("Consumes $"+formatEnv(key))) + shorthand := fve.FieldByName("Shorthand").String() + hidden := fve.FieldByName("Hidden").Bool() + value := fve.FieldByName("Value").Interface() + + switch value.(type) { + case string: + _ = flagset.StringP(flg, shorthand, vip.GetString(key), usage) + case bool: + _ = flagset.BoolP(flg, shorthand, vip.GetBool(key), usage) + case int: + _ = flagset.IntP(flg, shorthand, vip.GetInt(key), usage) + case time.Duration: + _ = flagset.DurationP(flg, shorthand, vip.GetDuration(key), usage) + case []string: + _ = flagset.StringSliceP(flg, shorthand, vip.GetStringSlice(key), usage) + default: + continue + } + + _ = vip.BindPFlag(key, flagset.Lookup(flg)) + if hidden { + _ = flagset.MarkHidden(flg) + } + } } -func AttachEnterpriseFlags(flagset *pflag.FlagSet, vip *viper.Viper) { - _ = flagset.StringP("derp-server-relay-address", "", vip.GetString("derp.server.relay_address"), `An HTTP address that is accessible by other replicas to relay DERP traffic. Required for high availability.`) - _ = vip.BindPFlag("derp.server.relay_address", flagset.Lookup("derp-server-relay-address")) - _ = flagset.BoolP("audit-logging", "", vip.GetBool("audit_logging"), `Specifies whether audit logging is enabled.`) - _ = vip.BindPFlag("audit_logging", flagset.Lookup("audit-logging")) - _ = flagset.BoolP("browser-only", "", vip.GetBool("browser_only"), `Whether Coder only allows connections to workspaces via the browser.`) - _ = vip.BindPFlag("browser_only", flagset.Lookup("browser-only")) - _ = flagset.StringP("scim-auth-header", "", vip.GetString("scim_auth_header"), `Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication.`) - _ = vip.BindPFlag("scim_auth_header", flagset.Lookup("scim-auth-header")) - _ = flagset.IntP("user-workspace-quota", "", vip.GetInt("user_workspace_quota"), `Enables and sets a limit on how many workspaces each user can create.`) - _ = vip.BindPFlag("user_workspace_quota", flagset.Lookup("user-workspace-quota")) +func formatEnv(key string) string { + return "CODER_" + strings.ToUpper(strings.NewReplacer("-", "_", ".", "_").Replace(key)) } func defaultCacheDir() string { @@ -180,4 +350,3 @@ func defaultCacheDir() string { return filepath.Join(defaultCacheDir, "coder") } - diff --git a/codersdk/config.go b/codersdk/config.go index 162d584dd2aa0..4b228162530d1 100644 --- a/codersdk/config.go +++ b/codersdk/config.go @@ -22,230 +22,210 @@ import ( type DeploymentConfig struct { // Usage: External URL to access your deployment. This must be accessible by all provisioned workspaces. // Flag: access-url - AccessURL string `mapstructure:"access_url" json:"access_url"` + AccessURL DeploymentConfigField[string] `mapstructure:"access_url" json:"access_url"` // Usage: Specifies the wildcard hostname to use for workspace applications in the form "*.example.com". // Flag: wildcard-access-url - WildcardAccessURL string `mapstructure:"wildcard_access_url" json:"wildcard_access_url"` + WildcardAccessURL DeploymentConfigField[string] `mapstructure:"wildcard_access_url" json:"wildcard_access_url"` // Usage: Bind address of the server. // Flag: address // Shorthand: a // Default: "127.0.0.1:3000" - Address string `mapstructure:"address" json:"address"` + Address DeploymentConfigField[string] `mapstructure:"address" json:"address"` // Usage: Interval to poll for scheduled workspace builds. // Flag: autobuild-poll-interval // Hidden: true // Default: time.Minute - AutobuildPollInterval time.Duration `mapstructure:"autobuild_poll_interval" json:"autobuild_poll_interval"` - DERP DERPConfig `mapstructure:"derp" json:"derp"` - Prometheus PrometheusConfig `mapstructure:"prometheus" json:"prometheus"` - Pprof PprofConfig `mapstructure:"pprof" json:"pprof"` - // Usage: The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. - // Flag: cache-dir - // Default: defaultCacheDir() - CacheDir string `mapstructure:"cache_dir" json:"cache_dir"` - // Usage: Controls whether data will be stored in an in-memory database. - // Flag: in-memory - // Hidden: true - InMemoryDatabase bool `mapstructure:"in_memory_database" json:"in_memory_database"` - // Usage: Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. - // Flag: provisioner-daemons - // Default: 3 - ProvisionerDaemonCount int `mapstructure:"provisioner_daemon_count" json:"provisioner_daemon_count"` - // Usage: 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". - // Flag: postgres-url - PostgresURL string `mapstructure:"postgres_url" json:"-"` - OAuth2Github OAuth2GithubConfig `mapstructure:"oauth2_github" json:"oauth2_github"` - OIDC OIDCConfig `mapstructure:"oidc" json:"oidc"` - Telemetry TelemetryConfig `mapstructure:"telemetry" json:"telemetry"` - TLS TLSConfig `mapstructure:"tls_config" json:"tls_config"` - // Usage: Whether application tracing data is collected. - // Flag: trace - TraceEnable bool `mapstructure:"trace_enable" json:"trace_enable"` - // Usage: Controls if the 'Secure' property is set on browser session cookies. - // Flag: secure-auth-cookie - SecureAuthCookie bool `mapstructure:"secure_auth_cookie" json:"secure_auth_cookie"` - // Usage: The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". - // Flag: ssh-keygen-algorithm - // Default: "ed25519" - SSHKeygenAlgorithm string `mapstructure:"ssh_keygen_algorithm" json:"ssh_keygen_algorithm"` - // Usage: Templates to auto-import. Available auto-importable templates are: kubernetes - // Flag: auto-import-template - // Hidden: true - AutoImportTemplates []string `mapstructure:"auto_import_templates" json:"auto_import_templates"` - // Usage: How frequently metrics are refreshed - // Flag: metrics-cache-refresh-interval - // Hidden: true - // Default: time.Hour - MetricsCacheRefreshInterval time.Duration `mapstructure:"metrics_cache_refresh_interval" json:"metrics_cache_refresh_interval"` - // Usage: How frequently agent stats are recorded - // Flag: agent-stats-refresh-interval - // Hidden: true - // Default: 10 * time.Minute - AgentStatRefreshInterval time.Duration `mapstructure:"agent_stat_refresh_interval" json:"agent_stat_refresh_interval"` - // Usage: Enables verbose logging. - // Flag: verbose - // Shorthand: v - Verbose bool `mapstructure:"verbose" json:"verbose"` - // Usage: Specifies whether audit logging is enabled. - // Flag: audit-logging - // Default: true - // Enterprise: true - AuditLogging bool `mapstructure:"audit_logging" json:"audit_logging"` - // Usage: Whether Coder only allows connections to workspaces via the browser. - // Flag: browser-only - // Enterprise: true - BrowserOnly bool `mapstructure:"browser_only" json:"browser_only"` - // Usage: Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. - // Flag: scim-auth-header - // Enterprise: true - SCIMAuthHeader string `mapstructure:"scim_auth_header" json:"-"` - // Usage: Enables and sets a limit on how many workspaces each user can create. - // Flag: user-workspace-quota - // Enterprise: true - UserWorkspaceQuota int `mapstructure:"user_workspace_quota" json:"user_workspace_quota"` -} - -type DERPConfig struct { - Server DERPServerConfig `mapstructure:"server" json:"server"` - Config DERPConfigConfig `mapstructure:"config" json:"config"` -} - -type DERPServerConfig struct { + AutobuildPollInterval DeploymentConfigField[time.Duration] `mapstructure:"autobuild_poll_interval" json:"autobuild_poll_interval"` // Usage: Whether to enable or disable the embedded DERP relay server. // Flag: derp-server-enable // Default: true - Enable bool `mapstructure:"enabled" json:"enabled"` + DERPServerEnable DeploymentConfigField[bool] `mapstructure:"enabled" json:"derp_server_enabled"` // Usage: Region ID to use for the embedded DERP server. // Flag: derp-server-region-id // Default: 999 - RegionID int `mapstructure:"region_id" json:"region_id"` + DERPServerRegionID DeploymentConfigField[int] `mapstructure:"region_id" json:"derp_server_region_id"` // Usage: Region code to use for the embedded DERP server. // Flag: derp-server-region-code // Default: "coder" - RegionCode string `mapstructure:"region_code" json:"region_code"` + DERPServerRegionCode DeploymentConfigField[string] `mapstructure:"region_code" json:"derp_server_region_code"` // Usage: Region name that for the embedded DERP server. // Flag: derp-server-region-name // Default: "Coder Embedded Relay" - RegionName string `mapstructure:"region_name" json:"region_name"` + DERPServerRegionName DeploymentConfigField[string] `mapstructure:"region_name" json:"derp_server_region_name"` // Usage: Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections. // Flag: derp-server-stun-addresses // Default: []string{"stun.l.google.com:19302"} - STUNAddresses []string `mapstructure:"stun_address" json:"stun_address"` + DERPServerSTUNAddresses DeploymentConfigField[[]string] `mapstructure:"stun_address" json:"derp_server_stun_address"` // Usage: An HTTP address that is accessible by other replicas to relay DERP traffic. Required for high availability. // Flag: derp-server-relay-address // Enterprise: true - RelayAddress string `mapstructure:"relay_address" json:"relay_address"` -} - -type DERPConfigConfig struct { + DERPServerRelayAddress DeploymentConfigField[string] `mapstructure:"relay_address" json:"derp_server_relay_address"` // Usage: URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/ // Flag: derp-config-url - URL string `mapstructure:"url" json:"url"` + DERPConfigURL DeploymentConfigField[string] `mapstructure:"url" json:"derp_config_url"` // Usage: Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/ // Flag: derp-config-path - Path string `mapstructure:"path" json:"path"` -} - -type PrometheusConfig struct { + DERPConfigPath DeploymentConfigField[string] `mapstructure:"path" json:"derp_config_path"` // Usage: Serve prometheus metrics on the address defined by prometheus address. // Flag: prometheus-enable - Enable bool `mapstructure:"enabled" json:"enabled"` + PrometheusEnable DeploymentConfigField[bool] `mapstructure:"enabled" json:"prometheus_enabled"` // Usage: The bind address to serve prometheus metrics. // Flag: prometheus-address // Default: "127.0.0.1:2112" - Address string `mapstructure:"address" json:"address"` -} - -type PprofConfig struct { + PrometheusAddress DeploymentConfigField[string] `mapstructure:"address" json:"prometheus_address"` // Usage: Serve pprof metrics on the address defined by pprof address. // Flag: pprof-enable - Enable bool `mapstructure:"enabled" json:"enabled"` + PprofEnable DeploymentConfigField[bool] `mapstructure:"enabled" json:"pprof_enabled"` // Usage: The bind address to serve pprof. // Flag: pprof-address // Default: "127.0.0.1:6060" - Address string `mapstructure:"address" json:"address"` -} - -type OAuth2GithubConfig struct { + PprofAddress DeploymentConfigField[string] `mapstructure:"address" json:"pprof_address"` + // Usage: The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. + // Flag: cache-dir + // Default: defaultCacheDir() + CacheDir DeploymentConfigField[string] `mapstructure:"cache_dir" json:"cache_dir"` + // Usage: Controls whether data will be stored in an in-memory database. + // Flag: in-memory + // Hidden: true + InMemoryDatabase DeploymentConfigField[bool] `mapstructure:"in_memory_database" json:"in_memory_database"` + // Usage: Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. + // Flag: provisioner-daemons + // Default: 3 + ProvisionerDaemonCount DeploymentConfigField[int] `mapstructure:"provisioner_daemon_count" json:"provisioner_daemon_count"` + // Usage: 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". + // Flag: postgres-url + PostgresURL DeploymentConfigField[string] `mapstructure:"postgres_url" json:"-"` // Usage: Client ID for Login with GitHub. // Flag: oauth2-github-client-id - ClientID string `mapstructure:"client_id" json:"client_id"` + OAuth2GithubClientID DeploymentConfigField[string] `mapstructure:"client_id" json:"oauth2_github_client_id"` // Usage: Client secret for Login with GitHub. // Flag: oauth2-github-client-secret - ClientSecret string `mapstructure:"client_secret" json:"-"` + OAuth2GithubClientSecret DeploymentConfigField[string] `mapstructure:"client_secret" json:"-"` // Usage: Organizations the user must be a member of to Login with GitHub. // Flag: oauth2-github-allowed-orgs - AllowedOrganizations []string `mapstructure:"allowed_organizations" json:"allowed_organizations"` + OAuth2GithubAllowedOrganizations DeploymentConfigField[[]string] `mapstructure:"allowed_organizations" json:"oauth2_github_allowed_organizations"` // Usage: Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /. // Flag: oauth2-github-allowed-teams - AllowedTeams []string `mapstructure:"allowed_teams" json:"allowed_teams"` + OAuth2GithubAllowedTeams DeploymentConfigField[[]string] `mapstructure:"allowed_teams" json:"oauth2_github_allowed_teams"` // Usage: Whether new users can sign up with GitHub. // Flag: oauth2-github-allow-signups - AllowSignups bool `mapstructure:"allow_signups" json:"allow_signups"` + OAuth2GithubAllowSignups DeploymentConfigField[bool] `mapstructure:"allow_signups" json:"oauth2_github_allow_signups"` // Usage: Base URL of a GitHub Enterprise deployment to use for Login with GitHub. // Flag: oauth2-github-enterprise-base-url - EnterpriseBaseURL string `mapstructure:"enterprise_base_url" json:"enterprise_base_url"` -} - -type OIDCConfig struct { + OAuth2GithubEnterpriseBaseURL DeploymentConfigField[string] `mapstructure:"enterprise_base_url" json:"oauth2_github_enterprise_base_url"` // Usage: Whether new users can sign up with OIDC. // Flag: oidc-allow-signups // Default: true - AllowSignups bool `mapstructure:"allow_signups" json:"allow_signups"` + OIDCAllowSignups DeploymentConfigField[bool] `mapstructure:"allow_signups" json:"oidc_allow_signups"` // Usage: Client ID to use for Login with OIDC. // Flag: oidc-client-id - ClientID string `mapstructure:"client_id" json:"client_id"` + OIDCClientID DeploymentConfigField[string] `mapstructure:"client_id" json:"oidc_client_id"` // Usage: Client secret to use for Login with OIDC. // Flag: oidc-client-secret - ClientSecret string `mapstructure:"cliet_secret" json:"-"` + OIDCClientSecret DeploymentConfigField[string] `mapstructure:"cliet_secret" json:"-"` // Usage: Email domain that clients logging in with OIDC must match. // Flag: oidc-email-domain - EmailDomain string `mapstructure:"email_domain" json:"email_domain"` + OIDCEmailDomain DeploymentConfigField[string] `mapstructure:"email_domain" json:"oidc_email_domain"` // Usage: Issuer URL to use for Login with OIDC. // Flag: oidc-issuer-url - IssuerURL string `mapstructure:"issuer_url" json:"issuer_url"` + OIDCIssuerURL DeploymentConfigField[string] `mapstructure:"issuer_url" json:"oidc_issuer_url"` // Usage: Scopes to grant when authenticating with OIDC. // Flag: oidc-scopes // Default: []string{oidc.ScopeOpenID, "profile", "email"} - Scopes []string `mapstructure:"scopes" json:"scopes"` -} - -type TelemetryConfig struct { + OIDCScopes DeploymentConfigField[[]string] `mapstructure:"scopes" json:"oidc_scopes"` // Usage: Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product. // Flag: telemetry // Default: flag.Lookup("test.v") == nil - Enable bool `mapstructure:"enable" json:"enable"` + TelemetryEnable DeploymentConfigField[bool] `mapstructure:"enable" json:"telemetry_enable"` // Usage: Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option. // Flag: telemetry-trace // Default: flag.Lookup("test.v") == nil - TraceEnable bool `mapstructure:"trace_enable" json:"trace_enable"` + TelemetryTraceEnable DeploymentConfigField[bool] `mapstructure:"trace_enable" json:"telemetry_trace_enable"` // Usage: URL to send telemetry. // Flag: telemetry-url // Hidden: true // Default: "https://telemetry.coder.com" - URL string `mapstructure:"url" json:"url"` -} -type TLSConfig struct { + TelemetryURL DeploymentConfigField[string] `mapstructure:"url" json:"telemetry_url"` // Usage: Whether TLS will be enabled. // Flag: tls-enable - Enable bool `mapstructure:"tls_enable" json:"tls_enable"` + TLSEnable DeploymentConfigField[bool] `mapstructure:"tls_enable" json:"tls_enable"` // Usage: Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file. // Flag: tls-cert-file - CertFiles []string `mapstructure:"tls_cert_files" json:"tls_cert_files"` + TLSCertFiles DeploymentConfigField[[]string] `mapstructure:"tls_cert_files" json:"tls_cert_files"` // Usage: PEM-encoded Certificate Authority file used for checking the authenticity of client // Flag: tls-client-ca-file - ClientCAFile string `mapstructure:"tls_client_ca_file" json:"tls_client_ca_file"` + TLSClientCAFile DeploymentConfigField[string] `mapstructure:"tls_client_ca_file" json:"tls_client_ca_file"` // Usage: Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". // Flag: tls-client-auth // Default: "request" - ClientAuth string `mapstructure:"tls_client_auth" json:"tls_client_auth"` + TLSClientAuth DeploymentConfigField[string] `mapstructure:"tls_client_auth" json:"tls_client_auth"` // Usage: Paths to the private keys for each of the certificates. It requires a PEM-encoded file. // Flag: tls-key-file - KeyFiles []string `mapstructure:"tls_key_files" json:"tls_key_files"` + TLSKeyFiles DeploymentConfigField[[]string] `mapstructure:"tls_key_files" json:"tls_key_files"` // Usage: Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" // Flag: tls-min-version // Default: "tls12" - MinVersion string `mapstructure:"tls_min_version" json:"tls_min_version"` + TLSMinVersion DeploymentConfigField[string] `mapstructure:"tls_min_version" json:"tls_min_version"` + // Usage: Whether application tracing data is collected. + // Flag: trace + TraceEnable DeploymentConfigField[bool] `mapstructure:"trace_enable" json:"trace_enable"` + // Usage: Controls if the 'Secure' property is set on browser session cookies. + // Flag: secure-auth-cookie + SecureAuthCookie DeploymentConfigField[bool] `mapstructure:"secure_auth_cookie" json:"secure_auth_cookie"` + // Usage: The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". + // Flag: ssh-keygen-algorithm + // Default: "ed25519" + SSHKeygenAlgorithm DeploymentConfigField[string] `mapstructure:"ssh_keygen_algorithm" json:"ssh_keygen_algorithm"` + // Usage: Templates to auto-import. Available auto-importable templates are: kubernetes + // Flag: auto-import-template + // Hidden: true + AutoImportTemplates DeploymentConfigField[[]string] `mapstructure:"auto_import_templates" json:"auto_import_templates"` + // Usage: How frequently metrics are refreshed + // Flag: metrics-cache-refresh-interval + // Hidden: true + // Default: time.Hour + MetricsCacheRefreshInterval DeploymentConfigField[time.Duration] `mapstructure:"metrics_cache_refresh_interval" json:"metrics_cache_refresh_interval"` + // Usage: How frequently agent stats are recorded + // Flag: agent-stats-refresh-interval + // Hidden: true + // Default: 10 * time.Minute + AgentStatRefreshInterval DeploymentConfigField[time.Duration] `mapstructure:"agent_stat_refresh_interval" json:"agent_stat_refresh_interval"` + // Usage: Enables verbose logging. + // Flag: verbose + // Shorthand: v + Verbose DeploymentConfigField[bool] `mapstructure:"verbose" json:"verbose"` + // Usage: Specifies whether audit logging is enabled. + // Flag: audit-logging + // Default: true + // Enterprise: true + AuditLogging DeploymentConfigField[bool] `mapstructure:"audit_logging" json:"audit_logging"` + // Usage: Whether Coder only allows connections to workspaces via the browser. + // Flag: browser-only + // Enterprise: true + BrowserOnly DeploymentConfigField[bool] `mapstructure:"browser_only" json:"browser_only"` + // Usage: Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. + // Flag: scim-auth-header + // Enterprise: true + SCIMAuthHeader DeploymentConfigField[string] `mapstructure:"scim_auth_header" json:"-"` + // Usage: Enables and sets a limit on how many workspaces each user can create. + // Flag: user-workspace-quota + // Enterprise: true + UserWorkspaceQuota DeploymentConfigField[int] `mapstructure:"user_workspace_quota" json:"user_workspace_quota"` +} + +type Flaggable interface { + string | bool | int | time.Duration | []string +} + +type DeploymentConfigField[T Flaggable] struct { + Key string + Name string + Usage string + Flag string + Shorthand string + Enterprise bool + Hidden bool + Value T } // DeploymentConfig returns the deployment config for the coder server. diff --git a/scripts/deploymentconfig/main.go b/scripts/deploymentconfig/main.go deleted file mode 100644 index abf6f6d9a384d..0000000000000 --- a/scripts/deploymentconfig/main.go +++ /dev/null @@ -1,320 +0,0 @@ -package main - -import ( - "bytes" - "context" - "fmt" - "go/ast" - "go/token" - "reflect" - "strconv" - "strings" - "text/template" - - "golang.org/x/tools/go/packages" - "golang.org/x/xerrors" -) - -const ( - StructTarget = "DeploymentConfig" - EnvPrefix = "CODER_" -) - -func main() { - data, err := GenerateData(context.Background(), "./codersdk") - if err != nil { - panic(err.Error()) - } - - // Just cat the output to a file to capture it - _, _ = fmt.Println(Render(data)) -} - -type Data struct { - StructTarget string - Fields []Field -} - -type Field struct { - Key string - Env string - Usage string - Flag string - Shorthand string - Default string - Enterprise bool - Hidden bool - Type string - ViperType string -} - -func GenerateData(ctx context.Context, dir string) (*Data, error) { - pkg, err := parsePackage(ctx, dir) - if err != nil { - return nil, xerrors.Errorf("parse package %q: %w", dir, err) - } - - data, err := generateAll(pkg) - if err != nil { - return nil, xerrors.Errorf("parse package %q: %w", dir, err) - } - - return data, nil -} - -// parsePackage takes a list of patterns such as a directory, and parses them. -func parsePackage(ctx context.Context, patterns ...string) (*packages.Package, error) { - cfg := &packages.Config{ - // Just accept the fact we need these flags for what we want. Feel free to add - // more, it'll just increase the time it takes to parse. - Mode: packages.NeedTypes | packages.NeedName | packages.NeedTypesInfo | - packages.NeedTypesSizes | packages.NeedSyntax, - Tests: false, - Context: ctx, - } - - pkgs, err := packages.Load(cfg, patterns...) - if err != nil { - return nil, xerrors.Errorf("load package: %w", err) - } - - // Only support 1 package for now. We can expand it if we need later, we - // just need to hook up multiple packages in the generator. - if len(pkgs) != 1 { - return nil, xerrors.Errorf("expected 1 package, found %d", len(pkgs)) - } - - return pkgs[0], nil -} - -func generateAll(pkg *packages.Package) (*Data, error) { - cb := Data{ - StructTarget: StructTarget, - } - structs := make(map[string]*ast.StructType) - for _, file := range pkg.Syntax { - for _, decl := range file.Decls { - decl, ok := decl.(*ast.GenDecl) - if !ok { - continue - } - if decl.Tok != token.TYPE { - continue - } - for _, speci := range decl.Specs { - spec, ok := speci.(*ast.TypeSpec) - if !ok { - continue - } - t, ok := spec.Type.(*ast.StructType) - if !ok { - continue - } - structs[spec.Name.Name] = t - } - } - } - - cb.Fields = handleStruct("", StructTarget, structs, cb.Fields) - - return &cb, nil -} - -func handleStruct(prefix string, target string, structs map[string]*ast.StructType, fields []Field) []Field { - var dc *ast.StructType - for name, t := range structs { - if name == target { - dc = t - break - } - } - if dc == nil { - return fields - } - for _, field := range dc.Fields.List { - key := reflect.StructTag(strings.Trim(field.Tag.Value, "`")).Get("mapstructure") - if key == "" { - continue - } - if prefix != "" { - key = fmt.Sprintf("%s.%s", prefix, key) - } - f := Field{ - Key: key, - Env: EnvPrefix + strings.ToUpper(strings.NewReplacer("-", "_", ".", "_").Replace(key)), - } - switch ft := field.Type.(type) { - case *ast.Ident: - switch ft.Name { - case "string": - f.Type = "String" - f.ViperType = "String" - case "int": - f.Type = "Int" - f.ViperType = "Int" - case "bool": - f.Type = "Bool" - f.ViperType = "Bool" - default: - _, ok := structs[ft.Name] - if !ok { - continue - } - fields = handleStruct(key, ft.Name, structs, fields) - continue - } - case *ast.SelectorExpr: - if ft.Sel.Name != "Duration" { - continue - } - f.Type = "Duration" - f.ViperType = "Duration" - case *ast.ArrayType: - i, ok := ft.Elt.(*ast.Ident) - if !ok { - continue - } - if i.Name != "string" { - continue - } - f.Type = "StringArray" - f.ViperType = "StringSlice" - default: - continue - } - - for _, line := range field.Doc.List { - if strings.HasPrefix(line.Text, "// Usage:") { - v := strings.TrimPrefix(line.Text, "// Usage:") - v = strings.TrimSpace(v) - f.Usage = v - } - if strings.HasPrefix(line.Text, "// Flag:") { - v := strings.TrimPrefix(line.Text, "// Flag:") - v = strings.TrimSpace(v) - f.Flag = v - } - if strings.HasPrefix(line.Text, "// Shorthand:") { - v := strings.TrimPrefix(line.Text, "// Shorthand:") - v = strings.TrimSpace(v) - f.Shorthand = v - } - if strings.HasPrefix(line.Text, "// Default:") { - v := strings.TrimPrefix(line.Text, "// Default:") - v = strings.TrimSpace(v) - f.Default = v - } - if strings.HasPrefix(line.Text, "// Enterprise:") { - v := strings.TrimPrefix(line.Text, "// Enterprise:") - v = strings.TrimSpace(v) - b, err := strconv.ParseBool(v) - if err != nil { - continue - } - f.Enterprise = b - } - if strings.HasPrefix(line.Text, "// Hidden:") { - v := strings.TrimPrefix(line.Text, "// Hidden:") - v = strings.TrimSpace(v) - v = strings.TrimSpace(v) - b, err := strconv.ParseBool(v) - if err != nil { - continue - } - f.Hidden = b - } - } - - fields = append(fields, f) - } - return fields -} - -func Render(d *Data) string { - t, err := template.New(StructTarget).Parse(deploymentConfigTemplate) - if err != nil { - panic(err) - } - var b bytes.Buffer - err = t.Execute(&b, d) - if err != nil { - panic(err) - } - - return b.String() -} - -const deploymentConfigTemplate = `// Code generated by go generate; DO NOT EDIT. -// This file was generated by the script at scripts/deploymentconfig/main.go -// The data for populating this file is from codersdk.{{ .StructTarget }}. -package deployment - -import ( - "flag" - "os" - "path/filepath" - "time" - - "github.com/coreos/go-oidc/v3/oidc" - "github.com/spf13/pflag" - "github.com/spf13/viper" - - "github.com/coder/coder/cli/cliui" - "github.com/coder/coder/codersdk" -) - -func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { - cfg := codersdk.DeploymentConfig{} - return cfg, vip.Unmarshal(&cfg) -} - -func NewViper() *viper.Viper { - v := viper.New() - v.SetEnvPrefix("coder") - v.AutomaticEnv() - {{- range .Fields }} - {{- if .Default }} - v.SetDefault("{{ .Key }}", {{ .Default }}) - {{- end }} - {{- end }} - - return v -} - -func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper) { - {{- range .Fields }} - {{- if and (.Flag) (not .Enterprise) }} - _ = flagset.{{ .Type }}P("{{ .Flag }}", "{{ .Shorthand }}", vip.Get{{ .ViperType }}("{{ .Key }}"), ` + "`{{ .Usage }}`" + `+"\n"+cliui.Styles.Placeholder.Render("Consumes ${{ .Env }}")) - _ = vip.BindPFlag("{{ .Key }}", flagset.Lookup("{{ .Flag }}")) - {{- if and .Hidden }} - _ = flagset.MarkHidden("{{ .Flag }}") - {{- end }} - {{- end }} - {{- end }} -} - -func AttachEnterpriseFlags(flagset *pflag.FlagSet, vip *viper.Viper) { - {{- range .Fields }} - {{- if and (.Flag) (.Enterprise) }} - _ = flagset.{{ .Type }}P("{{ .Flag }}", "{{ .Shorthand }}", vip.Get{{ .ViperType }}("{{ .Key }}"), ` + "`{{ .Usage }}`" + `) - _ = vip.BindPFlag("{{ .Key }}", flagset.Lookup("{{ .Flag }}")) - {{- if and .Hidden }} - _ = flagset.MarkHidden("{{ .Flag }}") - {{- end }} - {{- end }} - {{- end }} -} - -func defaultCacheDir() string { - defaultCacheDir, err := os.UserCacheDir() - if err != nil { - defaultCacheDir = os.TempDir() - } - if dir := os.Getenv("CACHE_DIRECTORY"); dir != "" { - // For compatibility with systemd. - defaultCacheDir = dir - } - - return filepath.Join(defaultCacheDir, "coder") -} -` From 3cafe7abfa3956583825152533da0b055a341e3c Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 18 Oct 2022 21:02:59 +0000 Subject: [PATCH 21/43] more convert --- cli/deployment/config.go | 28 +++++++++++++++++++++++++--- cli/server.go | 12 +++--------- enterprise/cli/server.go | 2 +- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 1b3d9b7f35993..bf1f271a384d3 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -265,9 +265,31 @@ func newConfig() codersdk.DeploymentConfig { } } -func Config(vip *viper.Viper) (codersdk.DeploymentConfig, error) { - cfg := codersdk.DeploymentConfig{} - return cfg, vip.Unmarshal(&cfg) +func Config(vip *viper.Viper) codersdk.DeploymentConfig { + dc := newConfig() + dcv := reflect.ValueOf(dc).Elem() + t := dcv.Type() + for i := 0; i < t.NumField(); i++ { + fv := dcv.Field(i) + fve := fv.Elem() + key := fve.FieldByName("Key").String() + value := fve.FieldByName("Value").Interface() + + switch value.(type) { + case string: + fve.FieldByName("Value").SetString(vip.GetString(key)) + case bool: + fve.FieldByName("Value").SetBool(vip.GetBool(key)) + case int: + fve.FieldByName("Value").SetInt(int64(vip.GetInt(key))) + case time.Duration: + fve.FieldByName("Value").SetInt(int64(vip.GetDuration(key))) + case []string: + fve.FieldByName("Value").Set(reflect.ValueOf(vip.GetStringSlice(key))) + } + } + + return dc } func NewViper() *viper.Viper { diff --git a/cli/server.go b/cli/server.go index 0030e2c6ff4f4..2c822d68d3d27 100644 --- a/cli/server.go +++ b/cli/server.go @@ -76,10 +76,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co Use: "server", Short: "Start a Coder server", RunE: func(cmd *cobra.Command, args []string) error { - cfg, err := deployment.Config(vip) - if err != nil { - return xerrors.Errorf("failed to read config: %w", err) - } + cfg := deployment.Config(vip) printLogo(cmd) logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) if cfg.Verbose { @@ -736,10 +733,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co Use: "postgres-builtin-serve", Short: "Run the built-in PostgreSQL deployment.", RunE: func(cmd *cobra.Command, args []string) error { - dcfg, err := deployment.Config(vip) - if err != nil { - return xerrors.Errorf("failed to read config: %w", err) - } + dcfg := deployment.Config(vip) cfg := createConfig(cmd) logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) if dcfg.Verbose { @@ -763,7 +757,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co }, }) - deployment.AttachFlags(root.Flags(), vip) + deployment.AttachFlags(root.Flags(), vip, false) return root } diff --git a/enterprise/cli/server.go b/enterprise/cli/server.go index bc391997985d4..3c71f22c2c0fd 100644 --- a/enterprise/cli/server.go +++ b/enterprise/cli/server.go @@ -81,7 +81,7 @@ func server() *cobra.Command { return api.AGPL, api, nil }) - deployment.AttachEnterpriseFlags(cmd.Flags(), vip) + deployment.AttachFlags(cmd.Flags(), vip, true) return cmd } From cb95772a5754c39a29ad80575f7ff5328d28b300 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 19 Oct 2022 20:48:20 +0000 Subject: [PATCH 22/43] move to reflect --- cli/deployment/config.go | 81 ++++++++-- cli/server.go | 143 +++++++++--------- coderd/{flags.go => deploymentconfig.go} | 0 ...flags_test.go => deploymentconfig_test.go} | 23 ++- codersdk/config.go | 104 ++++++------- 5 files changed, 200 insertions(+), 151 deletions(-) rename coderd/{flags.go => deploymentconfig.go} (100%) rename coderd/{flags_test.go => deploymentconfig_test.go} (62%) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index bf1f271a384d3..624835b4f3dc2 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -20,244 +20,296 @@ import ( func newConfig() codersdk.DeploymentConfig { return codersdk.DeploymentConfig{ AccessURL: codersdk.DeploymentConfigField[string]{ + Key: "access_url", Usage: "External URL to access your deployment. This must be accessible by all provisioned workspaces.", Flag: "access-url", }, WildcardAccessURL: codersdk.DeploymentConfigField[string]{ + Key: "wildcard_access_url", Usage: "Specifies the wildcard hostname to use for workspace applications in the form \"*.example.com\".", Flag: "wildcard-access-url", }, Address: codersdk.DeploymentConfigField[string]{ + Key: "address", Usage: "Bind address of the server.", Flag: "address", Shorthand: "a", Value: "127.0.0.1:3000", }, AutobuildPollInterval: codersdk.DeploymentConfigField[time.Duration]{ + Key: "autobuild_poll_interval", Usage: "Interval to poll for scheduled workspace builds.", Flag: "autobuild-poll-interval", Hidden: true, Value: time.Minute, }, DERPServerEnable: codersdk.DeploymentConfigField[bool]{ + Key: "derp.server.enable", Usage: "Whether to enable or disable the embedded DERP relay server.", Flag: "derp-server-enable", Value: true, }, DERPServerRegionID: codersdk.DeploymentConfigField[int]{ + Key: "derp.server.region_id", Usage: "Region ID to use for the embedded DERP server.", Flag: "derp-server-region-id", Value: 999, }, DERPServerRegionCode: codersdk.DeploymentConfigField[string]{ + Key: "derp.server.region_code", Usage: "Region code to use for the embedded DERP server.", Flag: "derp-server-region-code", Value: "coder", }, DERPServerRegionName: codersdk.DeploymentConfigField[string]{ + Key: "derp.server.region_name", Usage: "Region name that for the embedded DERP server.", Flag: "derp-server-region-name", Value: "Coder Embedded Relay", }, DERPServerSTUNAddresses: codersdk.DeploymentConfigField[[]string]{ + Key: "derp.server.stun_addresses", Usage: "Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections.", Flag: "derp-server-stun-addresses", Value: []string{"stun.l.google.com:19302"}, }, DERPServerRelayAddress: codersdk.DeploymentConfigField[string]{ + Key: "derp.server.relay_address", Usage: "An HTTP address that is accessible by other replicas to relay DERP traffic. Required for high availability.", Flag: "derp-server-relay-address", Enterprise: true, }, DERPConfigURL: codersdk.DeploymentConfigField[string]{ + Key: "derp.config.url", Usage: "URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/", Flag: "derp-config-url", }, DERPConfigPath: codersdk.DeploymentConfigField[string]{ + Key: "derp.config.path", Usage: "Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/", Flag: "derp-config-path", }, PrometheusEnable: codersdk.DeploymentConfigField[bool]{ + Key: "prometheus.enable", Usage: "Serve prometheus metrics on the address defined by prometheus address.", Flag: "prometheus-enable", }, PrometheusAddress: codersdk.DeploymentConfigField[string]{ + Key: "prometheus.address", Usage: "The bind address to serve prometheus metrics.", Flag: "prometheus-address", Value: "127.0.0.1:2112", }, PprofEnable: codersdk.DeploymentConfigField[bool]{ + Key: "pprof.enable", Usage: "Serve pprof metrics on the address defined by pprof address.", Flag: "pprof-enable", }, PprofAddress: codersdk.DeploymentConfigField[string]{ + Key: "pprof.address", Usage: "The bind address to serve pprof.", Flag: "pprof-address", Value: "127.0.0.1:6060", }, CacheDir: codersdk.DeploymentConfigField[string]{ + Key: "cache_dir", Usage: "The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.", Flag: "cache-dir", Value: defaultCacheDir(), }, InMemoryDatabase: codersdk.DeploymentConfigField[bool]{ + Key: "in_memory_database", Usage: "Controls whether data will be stored in an in-memory database.", Flag: "in-memory", Hidden: true, }, ProvisionerDaemonCount: codersdk.DeploymentConfigField[int]{ + Key: "provisioner.daemon_count", Usage: "Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this.", Flag: "provisioner-daemons", Value: 3, }, PostgresURL: codersdk.DeploymentConfigField[string]{ + Key: "postgres_url", Usage: "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\".", Flag: "postgres-url", }, OAuth2GithubClientID: codersdk.DeploymentConfigField[string]{ + Key: "oauth2github.client_id", Usage: "Client ID for Login with GitHub.", Flag: "oauth2-github-client-id", }, OAuth2GithubClientSecret: codersdk.DeploymentConfigField[string]{ + Key: "oauth2github.client_secret", Usage: "Client secret for Login with GitHub.", Flag: "oauth2-github-client-secret", }, OAuth2GithubAllowedOrganizations: codersdk.DeploymentConfigField[[]string]{ + Key: "oauth2github.allowed_organizations", Usage: "Organizations the user must be a member of to Login with GitHub.", Flag: "oauth2-github-allowed-orgs", }, OAuth2GithubAllowedTeams: codersdk.DeploymentConfigField[[]string]{ + Key: "oauth2github.allowed_teams", Usage: "Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /.", Flag: "oauth2-github-allowed-teams", }, OAuth2GithubAllowSignups: codersdk.DeploymentConfigField[bool]{ + Key: "oauth2github.allow_signups", Usage: "Whether new users can sign up with GitHub.", Flag: "oauth2-github-allow-signups", }, OAuth2GithubEnterpriseBaseURL: codersdk.DeploymentConfigField[string]{ + Key: "oauth2github.enterprise_base_url", Usage: "Base URL of a GitHub Enterprise deployment to use for Login with GitHub.", Flag: "oauth2-github-enterprise-base-url", }, OIDCAllowSignups: codersdk.DeploymentConfigField[bool]{ + Key: "oidc.allow_signups", Usage: "Whether new users can sign up with OIDC.", Flag: "oidc-allow-signups", Value: true, }, OIDCClientID: codersdk.DeploymentConfigField[string]{ + Key: "oidc.client_id", Usage: "Client ID to use for Login with OIDC.", Flag: "oidc-client-id", }, OIDCClientSecret: codersdk.DeploymentConfigField[string]{ + Key: "oidc.client_secret", Usage: "Client secret to use for Login with OIDC.", Flag: "oidc-client-secret", }, OIDCEmailDomain: codersdk.DeploymentConfigField[string]{ + Key: "oidc.email_domain", Usage: "Email domain that clients logging in with OIDC must match.", Flag: "oidc-email-domain", }, OIDCIssuerURL: codersdk.DeploymentConfigField[string]{ + Key: "oidc.issuer_url", Usage: "Issuer URL to use for Login with OIDC.", Flag: "oidc-issuer-url", }, OIDCScopes: codersdk.DeploymentConfigField[[]string]{ + Key: "oidc.scopes", Usage: "Scopes to grant when authenticating with OIDC.", Flag: "oidc-scopes", Value: []string{oidc.ScopeOpenID, "profile", "email"}, }, TelemetryEnable: codersdk.DeploymentConfigField[bool]{ + Key: "telemetry.enable", Usage: "Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product.", Flag: "telemetry", Value: flag.Lookup("test.v") == nil, }, TelemetryTraceEnable: codersdk.DeploymentConfigField[bool]{ + Key: "telemetry.trace.enable", Usage: "Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option.", Flag: "telemetry-trace", Value: flag.Lookup("test.v") == nil, }, TelemetryURL: codersdk.DeploymentConfigField[string]{ + Key: "telemetry.url", Usage: "URL to send telemetry.", Flag: "telemetry-url", Hidden: true, Value: "https://telemetry.coder.com", }, TLSEnable: codersdk.DeploymentConfigField[bool]{ + Key: "tls.enable", Usage: "Whether TLS will be enabled.", Flag: "tls-enable", }, TLSCertFiles: codersdk.DeploymentConfigField[[]string]{ + Key: "tls.cert_files", Usage: "Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file.", Flag: "tls-cert-file", }, TLSClientCAFile: codersdk.DeploymentConfigField[string]{ + Key: "tls.client_ca_file", Usage: "PEM-encoded Certificate Authority file used for checking the authenticity of client", Flag: "tls-client-ca-file", }, TLSClientAuth: codersdk.DeploymentConfigField[string]{ + Key: "tls.client_auth", Usage: "Policy the server will follow for TLS Client Authentication. Accepted values are \"none\", \"request\", \"require-any\", \"verify-if-given\", or \"require-and-verify\".", Flag: "tls-client-auth", Value: "request", }, TLSKeyFiles: codersdk.DeploymentConfigField[[]string]{ + Key: "tls.key_files", Usage: "Paths to the private keys for each of the certificates. It requires a PEM-encoded file.", Flag: "tls-key-file", }, TLSMinVersion: codersdk.DeploymentConfigField[string]{ + Key: "tls.min_version", Usage: "Minimum supported version of TLS. Accepted values are \"tls10\", \"tls11\", \"tls12\" or \"tls13\"", Flag: "tls-min-version", Value: "tls12", }, TraceEnable: codersdk.DeploymentConfigField[bool]{ + Key: "trace", Usage: "Whether application tracing data is collected.", Flag: "trace", }, SecureAuthCookie: codersdk.DeploymentConfigField[bool]{ + Key: "secure_auth_cookie", Usage: "Controls if the 'Secure' property is set on browser session cookies.", Flag: "secure-auth-cookie", }, SSHKeygenAlgorithm: codersdk.DeploymentConfigField[string]{ + Key: "ssh_keygen_algorithm", Usage: "The algorithm to use for generating ssh keys. Accepted values are \"ed25519\", \"ecdsa\", or \"rsa4096\".", Flag: "ssh-keygen-algorithm", Value: "ed25519", }, AutoImportTemplates: codersdk.DeploymentConfigField[[]string]{ + Key: "auto_import_templates", Usage: "Templates to auto-import. Available auto-importable templates are: kubernetes", Flag: "auto-import-template", Hidden: true, }, MetricsCacheRefreshInterval: codersdk.DeploymentConfigField[time.Duration]{ + Key: "metrics_cache_refresh_interval", Usage: "How frequently metrics are refreshed", Flag: "metrics-cache-refresh-interval", Hidden: true, Value: time.Hour, }, AgentStatRefreshInterval: codersdk.DeploymentConfigField[time.Duration]{ + Key: "agent_stat_refresh_interval", Usage: "How frequently agent stats are recorded", Flag: "agent-stats-refresh-interval", Hidden: true, Value: 10 * time.Minute, }, Verbose: codersdk.DeploymentConfigField[bool]{ + Key: "verbose", Usage: "Enables verbose logging.", Flag: "verbose", Shorthand: "v", }, AuditLogging: codersdk.DeploymentConfigField[bool]{ + Key: "audit_logging", Usage: "Specifies whether audit logging is enabled.", Flag: "audit-logging", Value: true, Enterprise: true, }, BrowserOnly: codersdk.DeploymentConfigField[bool]{ + Key: "browser_only", Usage: "Whether Coder only allows connections to workspaces via the browser.", Flag: "browser-only", Enterprise: true, }, SCIMAuthHeader: codersdk.DeploymentConfigField[string]{ + Key: "scim_auth_header", Usage: "Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication.", Flag: "scim-auth-header", Enterprise: true, }, UserWorkspaceQuota: codersdk.DeploymentConfigField[int]{ + Key: "user_workspace_quota", Usage: "Enables and sets a limit on how many workspaces each user can create.", Flag: "user-workspace-quota", Enterprise: true, @@ -267,11 +319,10 @@ func newConfig() codersdk.DeploymentConfig { func Config(vip *viper.Viper) codersdk.DeploymentConfig { dc := newConfig() - dcv := reflect.ValueOf(dc).Elem() + dcv := reflect.ValueOf(&dc).Elem() t := dcv.Type() for i := 0; i < t.NumField(); i++ { - fv := dcv.Field(i) - fve := fv.Elem() + fve := dcv.Field(i) key := fve.FieldByName("Key").String() value := fve.FieldByName("Value").Interface() @@ -298,13 +349,12 @@ func NewViper() *viper.Viper { v.SetEnvPrefix("coder") v.AutomaticEnv() - dcv := reflect.ValueOf(dc).Elem() + dcv := reflect.ValueOf(dc) t := dcv.Type() for i := 0; i < t.NumField(); i++ { fv := dcv.Field(i) - fve := fv.Elem() - key := fve.FieldByName("Key").String() - value := fve.FieldByName("Value").Interface() + key := fv.FieldByName("Key").String() + value := fv.FieldByName("Value").Interface() v.SetDefault(key, value) } @@ -314,25 +364,24 @@ func NewViper() *viper.Viper { //nolint:revive func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper, enterprise bool) { dc := newConfig() - dcv := reflect.ValueOf(dc).Elem() + dcv := reflect.ValueOf(dc) t := dcv.Type() for i := 0; i < t.NumField(); i++ { fv := dcv.Field(i) - fve := fv.Elem() - isEnt := fve.FieldByName("Enterprise").Bool() + isEnt := fv.FieldByName("Enterprise").Bool() if enterprise != isEnt { continue } - key := fve.FieldByName("Key").String() - flg := fve.FieldByName("Flag").String() + key := fv.FieldByName("Key").String() + flg := fv.FieldByName("Flag").String() if flg == "" { continue } - usage := fve.FieldByName("Usage").String() + usage := fv.FieldByName("Usage").String() usage = fmt.Sprintf("%s\n%s", usage, cliui.Styles.Placeholder.Render("Consumes $"+formatEnv(key))) - shorthand := fve.FieldByName("Shorthand").String() - hidden := fve.FieldByName("Hidden").Bool() - value := fve.FieldByName("Value").Interface() + shorthand := fv.FieldByName("Shorthand").String() + hidden := fv.FieldByName("Hidden").Bool() + value := fv.FieldByName("Value").Interface() switch value.(type) { case string: diff --git a/cli/server.go b/cli/server.go index 2c822d68d3d27..b8e1f10d3e7d4 100644 --- a/cli/server.go +++ b/cli/server.go @@ -79,7 +79,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co cfg := deployment.Config(vip) printLogo(cmd) logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) - if cfg.Verbose { + if cfg.Verbose.Value { logger = logger.Leveled(slog.LevelDebug) } @@ -113,16 +113,16 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co // Coder tracing should be disabled if telemetry is disabled unless // --telemetry-trace was explicitly provided. - shouldCoderTrace := cfg.Telemetry.Enable && !isTest() + shouldCoderTrace := cfg.TelemetryEnable.Value && !isTest() // Only override if telemetryTraceEnable was specifically set. // By default we want it to be controlled by telemetryEnable. if cmd.Flags().Changed("telemetry-trace") { - shouldCoderTrace = cfg.Telemetry.TraceEnable + shouldCoderTrace = cfg.TelemetryTraceEnable.Value } - if cfg.TraceEnable || shouldCoderTrace { + if cfg.TraceEnable.Value || shouldCoderTrace { sdkTracerProvider, closeTracing, err := tracing.TracerProvider(ctx, "coderd", tracing.TracerOpts{ - Default: cfg.TraceEnable, + Default: cfg.TraceEnable.Value, Coder: shouldCoderTrace, }) if err != nil { @@ -144,13 +144,14 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co } } + var err error config := createConfig(cmd) builtinPostgres := false // Only use built-in if PostgreSQL URL isn't specified! - if !cfg.InMemoryDatabase && cfg.PostgresURL == "" { + if !cfg.InMemoryDatabase.Value && cfg.PostgresURL.Value == "" { var closeFunc func() error cmd.Printf("Using built-in PostgreSQL (%s)\n", config.PostgresPath()) - cfg.PostgresURL, closeFunc, err = startBuiltinPostgres(ctx, config, logger) + cfg.PostgresURL.Value, closeFunc, err = startBuiltinPostgres(ctx, config, logger) if err != nil { return err } @@ -163,20 +164,20 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co }() } - listener, err := net.Listen("tcp", cfg.Address) + listener, err := net.Listen("tcp", cfg.Address.Value) if err != nil { return xerrors.Errorf("listen %q: %w", cfg.Address, err) } defer listener.Close() var tlsConfig *tls.Config - if cfg.TLS.Enable { + if cfg.TLSEnable.Value { tlsConfig, err = configureTLS( - cfg.TLS.MinVersion, - cfg.TLS.ClientAuth, - cfg.TLS.CertFiles, - cfg.TLS.KeyFiles, - cfg.TLS.ClientCAFile, + cfg.TLSMinVersion.Value, + cfg.TLSClientAuth.Value, + cfg.TLSCertFiles.Value, + cfg.TLSKeyFiles.Value, + cfg.TLSClientCAFile.Value, ) if err != nil { return xerrors.Errorf("configure tls: %w", err) @@ -198,7 +199,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co Scheme: "http", Host: tcpAddr.String(), } - if cfg.TLS.Enable { + if cfg.TLSEnable.Value { localURL.Scheme = "https" } @@ -230,7 +231,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co } } - accessURLParsed, err := parseURL(ctx, cfg.AccessURL) + accessURLParsed, err := parseURL(ctx, cfg.AccessURL.Value) if err != nil { return xerrors.Errorf("parse URL: %w", err) } @@ -265,17 +266,17 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co return err } - sshKeygenAlgorithm, err := gitsshkey.ParseAlgorithm(cfg.SSHKeygenAlgorithm) + sshKeygenAlgorithm, err := gitsshkey.ParseAlgorithm(cfg.SSHKeygenAlgorithm.Value) if err != nil { return xerrors.Errorf("parse ssh keygen algorithm %s: %w", cfg.SSHKeygenAlgorithm, err) } // Validate provided auto-import templates. var ( - validatedAutoImportTemplates = make([]coderd.AutoImportTemplate, len(cfg.AutoImportTemplates)) - seenValidatedAutoImportTemplates = make(map[coderd.AutoImportTemplate]struct{}, len(cfg.AutoImportTemplates)) + validatedAutoImportTemplates = make([]coderd.AutoImportTemplate, len(cfg.AutoImportTemplates.Value)) + seenValidatedAutoImportTemplates = make(map[coderd.AutoImportTemplate]struct{}, len(cfg.AutoImportTemplates.Value)) ) - for i, autoImportTemplate := range cfg.AutoImportTemplates { + for i, autoImportTemplate := range cfg.AutoImportTemplates.Value { var v coderd.AutoImportTemplate switch autoImportTemplate { case "kubernetes": @@ -293,22 +294,22 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co defaultRegion := &tailcfg.DERPRegion{ EmbeddedRelay: true, - RegionID: cfg.DERP.Server.RegionID, - RegionCode: cfg.DERP.Server.RegionCode, - RegionName: cfg.DERP.Server.RegionName, + RegionID: cfg.DERPServerRegionID.Value, + RegionCode: cfg.DERPServerRegionCode.Value, + RegionName: cfg.DERPServerRegionName.Value, Nodes: []*tailcfg.DERPNode{{ - Name: fmt.Sprintf("%db", cfg.DERP.Server.RegionID), - RegionID: cfg.DERP.Server.RegionID, + Name: fmt.Sprintf("%db", cfg.DERPServerRegionID.Value), + RegionID: cfg.DERPServerRegionID.Value, HostName: accessURLParsed.Hostname(), DERPPort: accessURLPort, STUNPort: -1, ForceHTTP: accessURLParsed.Scheme == "http", }}, } - if !cfg.DERP.Server.Enable { + if !cfg.DERPServerEnable.Value { defaultRegion = nil } - derpMap, err := tailnet.NewDERPMap(ctx, defaultRegion, cfg.DERP.Server.STUNAddresses, cfg.DERP.Config.URL, cfg.DERP.Config.Path) + derpMap, err := tailnet.NewDERPMap(ctx, defaultRegion, cfg.DERPServerSTUNAddresses.Value, cfg.DERPConfigURL.Value, cfg.DERPConfigPath.Value) if err != nil { return xerrors.Errorf("create derp map: %w", err) } @@ -330,15 +331,15 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co Database: databasefake.New(), DERPMap: derpMap, Pubsub: database.NewPubsubInMemory(), - CacheDir: cfg.CacheDir, + CacheDir: cfg.CacheDir.Value, GoogleTokenValidator: googleTokenValidator, - SecureAuthCookie: cfg.SecureAuthCookie, + SecureAuthCookie: cfg.SecureAuthCookie.Value, SSHKeygenAlgorithm: sshKeygenAlgorithm, TracerProvider: tracerProvider, Telemetry: telemetry.NewNoop(), AutoImportTemplates: validatedAutoImportTemplates, - MetricsCacheRefreshInterval: cfg.MetricsCacheRefreshInterval, - AgentStatsRefreshInterval: cfg.AgentStatRefreshInterval, + MetricsCacheRefreshInterval: cfg.MetricsCacheRefreshInterval.Value, + AgentStatsRefreshInterval: cfg.AgentStatRefreshInterval.Value, Experimental: ExperimentalEnabled(cmd), DeploymentConfig: &cfg, } @@ -346,29 +347,29 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co options.TLSCertificates = tlsConfig.Certificates } - if cfg.OAuth2Github.ClientSecret != "" { + if cfg.OAuth2GithubClientSecret.Value != "" { options.GithubOAuth2Config, err = configureGithubOAuth2(accessURLParsed, - cfg.OAuth2Github.ClientID, - cfg.OAuth2Github.ClientSecret, - cfg.OAuth2Github.AllowSignups, - cfg.OAuth2Github.AllowedOrganizations, - cfg.OAuth2Github.AllowedTeams, - cfg.OAuth2Github.EnterpriseBaseURL, + cfg.OAuth2GithubClientID.Value, + cfg.OAuth2GithubClientSecret.Value, + cfg.OAuth2GithubAllowSignups.Value, + cfg.OAuth2GithubAllowedOrganizations.Value, + cfg.OAuth2GithubAllowedTeams.Value, + cfg.OAuth2GithubEnterpriseBaseURL.Value, ) if err != nil { return xerrors.Errorf("configure github oauth2: %w", err) } } - if cfg.OIDC.ClientSecret != "" { - if cfg.OIDC.ClientID == "" { + if cfg.OIDCClientSecret.Value != "" { + if cfg.OIDCClientID.Value == "" { return xerrors.Errorf("OIDC client ID be set!") } - if cfg.OIDC.IssuerURL == "" { + if cfg.OIDCIssuerURL.Value == "" { return xerrors.Errorf("OIDC issuer URL must be set!") } - oidcProvider, err := oidc.NewProvider(ctx, cfg.OIDC.IssuerURL) + oidcProvider, err := oidc.NewProvider(ctx, cfg.OIDCIssuerURL.Value) if err != nil { return xerrors.Errorf("configure oidc provider: %w", err) } @@ -378,25 +379,25 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co } options.OIDCConfig = &coderd.OIDCConfig{ OAuth2Config: &oauth2.Config{ - ClientID: cfg.OIDC.ClientID, - ClientSecret: cfg.OIDC.ClientSecret, + ClientID: cfg.OIDCClientID.Value, + ClientSecret: cfg.OIDCClientSecret.Value, RedirectURL: redirectURL.String(), Endpoint: oidcProvider.Endpoint(), - Scopes: cfg.OIDC.Scopes, + Scopes: cfg.OIDCScopes.Value, }, Verifier: oidcProvider.Verifier(&oidc.Config{ - ClientID: cfg.OIDC.ClientID, + ClientID: cfg.OIDCClientID.Value, }), - EmailDomain: cfg.OIDC.EmailDomain, - AllowSignups: cfg.OIDC.AllowSignups, + EmailDomain: cfg.OIDCEmailDomain.Value, + AllowSignups: cfg.OIDCAllowSignups.Value, } } - if cfg.InMemoryDatabase { + if cfg.InMemoryDatabase.Value { options.Database = databasefake.New() options.Pubsub = database.NewPubsubInMemory() } else { - sqlDB, err := sql.Open(sqlDriver, cfg.PostgresURL) + sqlDB, err := sql.Open(sqlDriver, cfg.PostgresURL.Value) if err != nil { return xerrors.Errorf("dial postgres: %w", err) } @@ -428,7 +429,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co return xerrors.Errorf("migrate up: %w", err) } options.Database = database.New(sqlDB) - options.Pubsub, err = database.NewPubsub(ctx, sqlDB, cfg.PostgresURL) + options.Pubsub, err = database.NewPubsub(ctx, sqlDB, cfg.PostgresURL.Value) if err != nil { return xerrors.Errorf("create pubsub: %w", err) } @@ -451,26 +452,26 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co } // Parse the raw telemetry URL! - telemetryURL, err := parseURL(ctx, cfg.Telemetry.URL) + telemetryURL, err := parseURL(ctx, cfg.TelemetryURL.Value) if err != nil { return xerrors.Errorf("parse telemetry url: %w", err) } // Disable telemetry if the in-memory database is used unless explicitly defined! - if cfg.InMemoryDatabase && !cmd.Flags().Changed("telemetry") { - cfg.Telemetry.Enable = false + if cfg.InMemoryDatabase.Value && !cmd.Flags().Changed(cfg.TelemetryEnable.Flag) { + cfg.TelemetryEnable.Value = false } - if cfg.Telemetry.Enable { + if cfg.TelemetryEnable.Value { options.Telemetry, err = telemetry.New(telemetry.Options{ BuiltinPostgres: builtinPostgres, DeploymentID: deploymentID, Database: options.Database, Logger: logger.Named("telemetry"), URL: telemetryURL, - GitHubOAuth: cfg.OAuth2Github.ClientID != "", - OIDCAuth: cfg.OIDC.ClientID != "", - OIDCIssuerURL: cfg.OIDC.IssuerURL, - Prometheus: cfg.Prometheus.Enable, - STUN: len(cfg.DERP.Server.STUNAddresses) != 0, + GitHubOAuth: cfg.OAuth2GithubClientID.Value != "", + OIDCAuth: cfg.OIDCClientID.Value != "", + OIDCIssuerURL: cfg.OIDCIssuerURL.Value, + Prometheus: cfg.PrometheusEnable.Value, + STUN: len(cfg.DERPServerSTUNAddresses.Value) != 0, Tunnel: tunnel != nil, }) if err != nil { @@ -481,11 +482,11 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co // This prevents the pprof import from being accidentally deleted. _ = pprof.Handler - if cfg.Pprof.Enable { + if cfg.PprofEnable.Value { //nolint:revive - defer serveHandler(ctx, logger, nil, cfg.Pprof.Address, "pprof")() + defer serveHandler(ctx, logger, nil, cfg.PprofAddress.Value, "pprof")() } - if cfg.Prometheus.Enable { + if cfg.PrometheusEnable.Value { options.PrometheusRegistry = prometheus.NewRegistry() closeUsersFunc, err := prometheusmetrics.ActiveUsers(ctx, options.PrometheusRegistry, options.Database, 0) if err != nil { @@ -502,7 +503,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co //nolint:revive defer serveHandler(ctx, logger, promhttp.InstrumentMetricHandler( options.PrometheusRegistry, promhttp.HandlerFor(options.PrometheusRegistry, promhttp.HandlerOpts{}), - ), cfg.Prometheus.Address, "prometheus")() + ), cfg.PrometheusAddress.Value, "prometheus")() } // We use a separate coderAPICloser so the Enterprise API @@ -514,7 +515,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co } client := codersdk.New(localURL) - if cfg.TLS.Enable { + if cfg.TLSEnable.Value { // Secure transport isn't needed for locally communicating! client.HTTPClient.Transport = &http.Transport{ TLSClientConfig: &tls.Config{ @@ -538,8 +539,8 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co _ = daemon.Close() } }() - for i := 0; i < cfg.ProvisionerDaemonCount; i++ { - daemon, err := newProvisionerDaemon(ctx, coderAPI, logger, cfg.CacheDir, errCh, false) + for i := 0; i < cfg.ProvisionerDaemonCount.Value; i++ { + daemon, err := newProvisionerDaemon(ctx, coderAPI, logger, cfg.CacheDir.Value, errCh, false) if err != nil { return xerrors.Errorf("create provisioner daemon: %w", err) } @@ -605,7 +606,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co return xerrors.Errorf("notify systemd: %w", err) } - autobuildPoller := time.NewTicker(cfg.AutobuildPollInterval) + autobuildPoller := time.NewTicker(cfg.AutobuildPollInterval.Value) defer autobuildPoller.Stop() autobuildExecutor := executor.New(ctx, options.Database, logger, autobuildPoller.C) autobuildExecutor.Run() @@ -670,7 +671,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co go func() { defer wg.Done() - if cfg.Verbose { + if cfg.Verbose.Value { cmd.Printf("Shutting down provisioner daemon %d...\n", id) } err := shutdownWithTimeout(provisionerDaemon.Shutdown, 5*time.Second) @@ -683,7 +684,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co cmd.PrintErrf("Close provisioner daemon %d: %s\n", id, err) return } - if cfg.Verbose { + if cfg.Verbose.Value { cmd.Printf("Gracefully shut down provisioner daemon %d\n", id) } }() @@ -736,7 +737,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co dcfg := deployment.Config(vip) cfg := createConfig(cmd) logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) - if dcfg.Verbose { + if dcfg.Verbose.Value { logger = logger.Leveled(slog.LevelDebug) } diff --git a/coderd/flags.go b/coderd/deploymentconfig.go similarity index 100% rename from coderd/flags.go rename to coderd/deploymentconfig.go diff --git a/coderd/flags_test.go b/coderd/deploymentconfig_test.go similarity index 62% rename from coderd/flags_test.go rename to coderd/deploymentconfig_test.go index 106db53f85d21..877a6278ed99c 100644 --- a/coderd/flags_test.go +++ b/coderd/deploymentconfig_test.go @@ -17,15 +17,14 @@ func TestDeploymentConfig(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() vip := deployment.NewViper() - cfg, err := deployment.Config(vip) - require.NoError(t, err) + cfg := deployment.Config(vip) // values should be returned - cfg.AccessURL = hi + cfg.AccessURL.Value = hi // values should not be returned - cfg.OAuth2Github.ClientSecret = hi - cfg.OIDC.ClientSecret = hi - cfg.PostgresURL = hi - cfg.SCIMAuthHeader = hi + cfg.OAuth2GithubClientSecret.Value = hi + cfg.OIDCClientSecret.Value = hi + cfg.PostgresURL.Value = hi + cfg.SCIMAuthHeader.Value = hi client := coderdtest.New(t, &coderdtest.Options{ DeploymentConfig: &cfg, @@ -34,10 +33,10 @@ func TestDeploymentConfig(t *testing.T) { scrubbed, err := client.DeploymentConfig(ctx) require.NoError(t, err) // ensure normal values pass through - require.EqualValues(t, hi, scrubbed.AccessURL) + require.EqualValues(t, hi, scrubbed.AccessURL.Value) // ensure secrets are removed - require.Empty(t, scrubbed.OAuth2Github.ClientSecret) - require.Empty(t, scrubbed.OIDC.ClientSecret) - require.Empty(t, scrubbed.PostgresURL) - require.Empty(t, scrubbed.SCIMAuthHeader) + require.Empty(t, scrubbed.OAuth2GithubClientSecret.Value) + require.Empty(t, scrubbed.OIDCClientSecret.Value) + require.Empty(t, scrubbed.PostgresURL.Value) + require.Empty(t, scrubbed.SCIMAuthHeader.Value) } diff --git a/codersdk/config.go b/codersdk/config.go index 4b228162530d1..3b2488a0f02b9 100644 --- a/codersdk/config.go +++ b/codersdk/config.go @@ -22,195 +22,195 @@ import ( type DeploymentConfig struct { // Usage: External URL to access your deployment. This must be accessible by all provisioned workspaces. // Flag: access-url - AccessURL DeploymentConfigField[string] `mapstructure:"access_url" json:"access_url"` + AccessURL DeploymentConfigField[string] `json:"access_url"` // Usage: Specifies the wildcard hostname to use for workspace applications in the form "*.example.com". // Flag: wildcard-access-url - WildcardAccessURL DeploymentConfigField[string] `mapstructure:"wildcard_access_url" json:"wildcard_access_url"` + WildcardAccessURL DeploymentConfigField[string] `json:"wildcard_access_url"` // Usage: Bind address of the server. // Flag: address // Shorthand: a // Default: "127.0.0.1:3000" - Address DeploymentConfigField[string] `mapstructure:"address" json:"address"` + Address DeploymentConfigField[string] `json:"address"` // Usage: Interval to poll for scheduled workspace builds. // Flag: autobuild-poll-interval // Hidden: true // Default: time.Minute - AutobuildPollInterval DeploymentConfigField[time.Duration] `mapstructure:"autobuild_poll_interval" json:"autobuild_poll_interval"` + AutobuildPollInterval DeploymentConfigField[time.Duration] `json:"autobuild_poll_interval"` // Usage: Whether to enable or disable the embedded DERP relay server. // Flag: derp-server-enable // Default: true - DERPServerEnable DeploymentConfigField[bool] `mapstructure:"enabled" json:"derp_server_enabled"` + DERPServerEnable DeploymentConfigField[bool] `json:"derp_server_enabled"` // Usage: Region ID to use for the embedded DERP server. // Flag: derp-server-region-id // Default: 999 - DERPServerRegionID DeploymentConfigField[int] `mapstructure:"region_id" json:"derp_server_region_id"` + DERPServerRegionID DeploymentConfigField[int] `json:"derp_server_region_id"` // Usage: Region code to use for the embedded DERP server. // Flag: derp-server-region-code // Default: "coder" - DERPServerRegionCode DeploymentConfigField[string] `mapstructure:"region_code" json:"derp_server_region_code"` + DERPServerRegionCode DeploymentConfigField[string] `json:"derp_server_region_code"` // Usage: Region name that for the embedded DERP server. // Flag: derp-server-region-name // Default: "Coder Embedded Relay" - DERPServerRegionName DeploymentConfigField[string] `mapstructure:"region_name" json:"derp_server_region_name"` + DERPServerRegionName DeploymentConfigField[string] `json:"derp_server_region_name"` // Usage: Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections. // Flag: derp-server-stun-addresses // Default: []string{"stun.l.google.com:19302"} - DERPServerSTUNAddresses DeploymentConfigField[[]string] `mapstructure:"stun_address" json:"derp_server_stun_address"` + DERPServerSTUNAddresses DeploymentConfigField[[]string] `json:"derp_server_stun_address"` // Usage: An HTTP address that is accessible by other replicas to relay DERP traffic. Required for high availability. // Flag: derp-server-relay-address // Enterprise: true - DERPServerRelayAddress DeploymentConfigField[string] `mapstructure:"relay_address" json:"derp_server_relay_address"` + DERPServerRelayAddress DeploymentConfigField[string] `json:"derp_server_relay_address"` // Usage: URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/ // Flag: derp-config-url - DERPConfigURL DeploymentConfigField[string] `mapstructure:"url" json:"derp_config_url"` + DERPConfigURL DeploymentConfigField[string] `json:"derp_config_url"` // Usage: Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/ // Flag: derp-config-path - DERPConfigPath DeploymentConfigField[string] `mapstructure:"path" json:"derp_config_path"` + DERPConfigPath DeploymentConfigField[string] `json:"derp_config_path"` // Usage: Serve prometheus metrics on the address defined by prometheus address. // Flag: prometheus-enable - PrometheusEnable DeploymentConfigField[bool] `mapstructure:"enabled" json:"prometheus_enabled"` + PrometheusEnable DeploymentConfigField[bool] `json:"prometheus_enabled"` // Usage: The bind address to serve prometheus metrics. // Flag: prometheus-address // Default: "127.0.0.1:2112" - PrometheusAddress DeploymentConfigField[string] `mapstructure:"address" json:"prometheus_address"` + PrometheusAddress DeploymentConfigField[string] `json:"prometheus_address"` // Usage: Serve pprof metrics on the address defined by pprof address. // Flag: pprof-enable - PprofEnable DeploymentConfigField[bool] `mapstructure:"enabled" json:"pprof_enabled"` + PprofEnable DeploymentConfigField[bool] `json:"pprof_enabled"` // Usage: The bind address to serve pprof. // Flag: pprof-address // Default: "127.0.0.1:6060" - PprofAddress DeploymentConfigField[string] `mapstructure:"address" json:"pprof_address"` + PprofAddress DeploymentConfigField[string] `json:"pprof_address"` // Usage: The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. // Flag: cache-dir // Default: defaultCacheDir() - CacheDir DeploymentConfigField[string] `mapstructure:"cache_dir" json:"cache_dir"` + CacheDir DeploymentConfigField[string] `json:"cache_dir"` // Usage: Controls whether data will be stored in an in-memory database. // Flag: in-memory // Hidden: true - InMemoryDatabase DeploymentConfigField[bool] `mapstructure:"in_memory_database" json:"in_memory_database"` + InMemoryDatabase DeploymentConfigField[bool] `json:"in_memory_database"` // Usage: Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. // Flag: provisioner-daemons // Default: 3 - ProvisionerDaemonCount DeploymentConfigField[int] `mapstructure:"provisioner_daemon_count" json:"provisioner_daemon_count"` + ProvisionerDaemonCount DeploymentConfigField[int] `json:"provisioner_daemon_count"` // Usage: 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". // Flag: postgres-url - PostgresURL DeploymentConfigField[string] `mapstructure:"postgres_url" json:"-"` + PostgresURL DeploymentConfigField[string] `json:"-"` // Usage: Client ID for Login with GitHub. // Flag: oauth2-github-client-id - OAuth2GithubClientID DeploymentConfigField[string] `mapstructure:"client_id" json:"oauth2_github_client_id"` + OAuth2GithubClientID DeploymentConfigField[string] `json:"oauth2_github_client_id"` // Usage: Client secret for Login with GitHub. // Flag: oauth2-github-client-secret - OAuth2GithubClientSecret DeploymentConfigField[string] `mapstructure:"client_secret" json:"-"` + OAuth2GithubClientSecret DeploymentConfigField[string] `json:"-"` // Usage: Organizations the user must be a member of to Login with GitHub. // Flag: oauth2-github-allowed-orgs - OAuth2GithubAllowedOrganizations DeploymentConfigField[[]string] `mapstructure:"allowed_organizations" json:"oauth2_github_allowed_organizations"` + OAuth2GithubAllowedOrganizations DeploymentConfigField[[]string] `json:"oauth2_github_allowed_organizations"` // Usage: Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /. // Flag: oauth2-github-allowed-teams - OAuth2GithubAllowedTeams DeploymentConfigField[[]string] `mapstructure:"allowed_teams" json:"oauth2_github_allowed_teams"` + OAuth2GithubAllowedTeams DeploymentConfigField[[]string] `json:"oauth2_github_allowed_teams"` // Usage: Whether new users can sign up with GitHub. // Flag: oauth2-github-allow-signups - OAuth2GithubAllowSignups DeploymentConfigField[bool] `mapstructure:"allow_signups" json:"oauth2_github_allow_signups"` + OAuth2GithubAllowSignups DeploymentConfigField[bool] `json:"oauth2_github_allow_signups"` // Usage: Base URL of a GitHub Enterprise deployment to use for Login with GitHub. // Flag: oauth2-github-enterprise-base-url - OAuth2GithubEnterpriseBaseURL DeploymentConfigField[string] `mapstructure:"enterprise_base_url" json:"oauth2_github_enterprise_base_url"` + OAuth2GithubEnterpriseBaseURL DeploymentConfigField[string] `json:"oauth2_github_enterprise_base_url"` // Usage: Whether new users can sign up with OIDC. // Flag: oidc-allow-signups // Default: true - OIDCAllowSignups DeploymentConfigField[bool] `mapstructure:"allow_signups" json:"oidc_allow_signups"` + OIDCAllowSignups DeploymentConfigField[bool] `json:"oidc_allow_signups"` // Usage: Client ID to use for Login with OIDC. // Flag: oidc-client-id - OIDCClientID DeploymentConfigField[string] `mapstructure:"client_id" json:"oidc_client_id"` + OIDCClientID DeploymentConfigField[string] `json:"oidc_client_id"` // Usage: Client secret to use for Login with OIDC. // Flag: oidc-client-secret - OIDCClientSecret DeploymentConfigField[string] `mapstructure:"cliet_secret" json:"-"` + OIDCClientSecret DeploymentConfigField[string] `json:"-"` // Usage: Email domain that clients logging in with OIDC must match. // Flag: oidc-email-domain - OIDCEmailDomain DeploymentConfigField[string] `mapstructure:"email_domain" json:"oidc_email_domain"` + OIDCEmailDomain DeploymentConfigField[string] `json:"oidc_email_domain"` // Usage: Issuer URL to use for Login with OIDC. // Flag: oidc-issuer-url - OIDCIssuerURL DeploymentConfigField[string] `mapstructure:"issuer_url" json:"oidc_issuer_url"` + OIDCIssuerURL DeploymentConfigField[string] `json:"oidc_issuer_url"` // Usage: Scopes to grant when authenticating with OIDC. // Flag: oidc-scopes // Default: []string{oidc.ScopeOpenID, "profile", "email"} - OIDCScopes DeploymentConfigField[[]string] `mapstructure:"scopes" json:"oidc_scopes"` + OIDCScopes DeploymentConfigField[[]string] `json:"oidc_scopes"` // Usage: Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product. // Flag: telemetry // Default: flag.Lookup("test.v") == nil - TelemetryEnable DeploymentConfigField[bool] `mapstructure:"enable" json:"telemetry_enable"` + TelemetryEnable DeploymentConfigField[bool] `json:"telemetry_enable"` // Usage: Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option. // Flag: telemetry-trace // Default: flag.Lookup("test.v") == nil - TelemetryTraceEnable DeploymentConfigField[bool] `mapstructure:"trace_enable" json:"telemetry_trace_enable"` + TelemetryTraceEnable DeploymentConfigField[bool] `json:"telemetry_trace_enable"` // Usage: URL to send telemetry. // Flag: telemetry-url // Hidden: true // Default: "https://telemetry.coder.com" - TelemetryURL DeploymentConfigField[string] `mapstructure:"url" json:"telemetry_url"` + TelemetryURL DeploymentConfigField[string] `json:"telemetry_url"` // Usage: Whether TLS will be enabled. // Flag: tls-enable - TLSEnable DeploymentConfigField[bool] `mapstructure:"tls_enable" json:"tls_enable"` + TLSEnable DeploymentConfigField[bool] `json:"tls_enable"` // Usage: Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file. // Flag: tls-cert-file - TLSCertFiles DeploymentConfigField[[]string] `mapstructure:"tls_cert_files" json:"tls_cert_files"` + TLSCertFiles DeploymentConfigField[[]string] `json:"tls_cert_files"` // Usage: PEM-encoded Certificate Authority file used for checking the authenticity of client // Flag: tls-client-ca-file - TLSClientCAFile DeploymentConfigField[string] `mapstructure:"tls_client_ca_file" json:"tls_client_ca_file"` + TLSClientCAFile DeploymentConfigField[string] `json:"tls_client_ca_file"` // Usage: Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". // Flag: tls-client-auth // Default: "request" - TLSClientAuth DeploymentConfigField[string] `mapstructure:"tls_client_auth" json:"tls_client_auth"` + TLSClientAuth DeploymentConfigField[string] `json:"tls_client_auth"` // Usage: Paths to the private keys for each of the certificates. It requires a PEM-encoded file. // Flag: tls-key-file - TLSKeyFiles DeploymentConfigField[[]string] `mapstructure:"tls_key_files" json:"tls_key_files"` + TLSKeyFiles DeploymentConfigField[[]string] `json:"tls_key_files"` // Usage: Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" // Flag: tls-min-version // Default: "tls12" - TLSMinVersion DeploymentConfigField[string] `mapstructure:"tls_min_version" json:"tls_min_version"` + TLSMinVersion DeploymentConfigField[string] `json:"tls_min_version"` // Usage: Whether application tracing data is collected. // Flag: trace - TraceEnable DeploymentConfigField[bool] `mapstructure:"trace_enable" json:"trace_enable"` + TraceEnable DeploymentConfigField[bool] `json:"trace_enable"` // Usage: Controls if the 'Secure' property is set on browser session cookies. // Flag: secure-auth-cookie - SecureAuthCookie DeploymentConfigField[bool] `mapstructure:"secure_auth_cookie" json:"secure_auth_cookie"` + SecureAuthCookie DeploymentConfigField[bool] `json:"secure_auth_cookie"` // Usage: The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". // Flag: ssh-keygen-algorithm // Default: "ed25519" - SSHKeygenAlgorithm DeploymentConfigField[string] `mapstructure:"ssh_keygen_algorithm" json:"ssh_keygen_algorithm"` + SSHKeygenAlgorithm DeploymentConfigField[string] `json:"ssh_keygen_algorithm"` // Usage: Templates to auto-import. Available auto-importable templates are: kubernetes // Flag: auto-import-template // Hidden: true - AutoImportTemplates DeploymentConfigField[[]string] `mapstructure:"auto_import_templates" json:"auto_import_templates"` + AutoImportTemplates DeploymentConfigField[[]string] `json:"auto_import_templates"` // Usage: How frequently metrics are refreshed // Flag: metrics-cache-refresh-interval // Hidden: true // Default: time.Hour - MetricsCacheRefreshInterval DeploymentConfigField[time.Duration] `mapstructure:"metrics_cache_refresh_interval" json:"metrics_cache_refresh_interval"` + MetricsCacheRefreshInterval DeploymentConfigField[time.Duration] `json:"metrics_cache_refresh_interval"` // Usage: How frequently agent stats are recorded // Flag: agent-stats-refresh-interval // Hidden: true // Default: 10 * time.Minute - AgentStatRefreshInterval DeploymentConfigField[time.Duration] `mapstructure:"agent_stat_refresh_interval" json:"agent_stat_refresh_interval"` + AgentStatRefreshInterval DeploymentConfigField[time.Duration] `json:"agent_stat_refresh_interval"` // Usage: Enables verbose logging. // Flag: verbose // Shorthand: v - Verbose DeploymentConfigField[bool] `mapstructure:"verbose" json:"verbose"` + Verbose DeploymentConfigField[bool] `json:"verbose"` // Usage: Specifies whether audit logging is enabled. // Flag: audit-logging // Default: true // Enterprise: true - AuditLogging DeploymentConfigField[bool] `mapstructure:"audit_logging" json:"audit_logging"` + AuditLogging DeploymentConfigField[bool] `json:"audit_logging"` // Usage: Whether Coder only allows connections to workspaces via the browser. // Flag: browser-only // Enterprise: true - BrowserOnly DeploymentConfigField[bool] `mapstructure:"browser_only" json:"browser_only"` + BrowserOnly DeploymentConfigField[bool] `json:"browser_only"` // Usage: Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. // Flag: scim-auth-header // Enterprise: true - SCIMAuthHeader DeploymentConfigField[string] `mapstructure:"scim_auth_header" json:"-"` + SCIMAuthHeader DeploymentConfigField[string] `json:"-"` // Usage: Enables and sets a limit on how many workspaces each user can create. // Flag: user-workspace-quota // Enterprise: true - UserWorkspaceQuota DeploymentConfigField[int] `mapstructure:"user_workspace_quota" json:"user_workspace_quota"` + UserWorkspaceQuota DeploymentConfigField[int] `json:"user_workspace_quota"` } type Flaggable interface { From 45dd96273738a068ef55e7183239d575ffd7c301 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 19 Oct 2022 20:52:17 +0000 Subject: [PATCH 23/43] clean docs --- codersdk/config.go | 245 ----------------------------------- codersdk/deploymentconfig.go | 98 ++++++++++++++ 2 files changed, 98 insertions(+), 245 deletions(-) delete mode 100644 codersdk/config.go create mode 100644 codersdk/deploymentconfig.go diff --git a/codersdk/config.go b/codersdk/config.go deleted file mode 100644 index 3b2488a0f02b9..0000000000000 --- a/codersdk/config.go +++ /dev/null @@ -1,245 +0,0 @@ -package codersdk - -import ( - "context" - "encoding/json" - "net/http" - "time" - - "golang.org/x/xerrors" -) - -// DeploymentConfig is the central configuration for the coder server. -// Secret values should specify `json:"-"` to prevent them from being returned by the API. -// All config values can be set via environment variables in the form of `CODER_` with `.` and `-` replaced by `_`. -// Optional doc comments above fields will generate CLI commands with the following options: -// Usage: - Describe what the setting field does (required) -// Flag: - Long flag name (required) -// Shorthand: - Single character shorthand flag name (optional) -// Default: - Default value for the field as you would write in go code (ex. "string", int, time.Minute, []string{"one", "two"}) (optional) -// Enterprise - Whether or not the field is only available in enterprise (optional) -// Hidden - Whether or not the field should be hidden from the CLI (optional) -type DeploymentConfig struct { - // Usage: External URL to access your deployment. This must be accessible by all provisioned workspaces. - // Flag: access-url - AccessURL DeploymentConfigField[string] `json:"access_url"` - // Usage: Specifies the wildcard hostname to use for workspace applications in the form "*.example.com". - // Flag: wildcard-access-url - WildcardAccessURL DeploymentConfigField[string] `json:"wildcard_access_url"` - // Usage: Bind address of the server. - // Flag: address - // Shorthand: a - // Default: "127.0.0.1:3000" - Address DeploymentConfigField[string] `json:"address"` - // Usage: Interval to poll for scheduled workspace builds. - // Flag: autobuild-poll-interval - // Hidden: true - // Default: time.Minute - AutobuildPollInterval DeploymentConfigField[time.Duration] `json:"autobuild_poll_interval"` - // Usage: Whether to enable or disable the embedded DERP relay server. - // Flag: derp-server-enable - // Default: true - DERPServerEnable DeploymentConfigField[bool] `json:"derp_server_enabled"` - // Usage: Region ID to use for the embedded DERP server. - // Flag: derp-server-region-id - // Default: 999 - DERPServerRegionID DeploymentConfigField[int] `json:"derp_server_region_id"` - // Usage: Region code to use for the embedded DERP server. - // Flag: derp-server-region-code - // Default: "coder" - DERPServerRegionCode DeploymentConfigField[string] `json:"derp_server_region_code"` - // Usage: Region name that for the embedded DERP server. - // Flag: derp-server-region-name - // Default: "Coder Embedded Relay" - DERPServerRegionName DeploymentConfigField[string] `json:"derp_server_region_name"` - // Usage: Addresses for STUN servers to establish P2P connections. Set empty to disable P2P connections. - // Flag: derp-server-stun-addresses - // Default: []string{"stun.l.google.com:19302"} - DERPServerSTUNAddresses DeploymentConfigField[[]string] `json:"derp_server_stun_address"` - // Usage: An HTTP address that is accessible by other replicas to relay DERP traffic. Required for high availability. - // Flag: derp-server-relay-address - // Enterprise: true - DERPServerRelayAddress DeploymentConfigField[string] `json:"derp_server_relay_address"` - // Usage: URL to fetch a DERP mapping on startup. See: https://tailscale.com/kb/1118/custom-derp-servers/ - // Flag: derp-config-url - DERPConfigURL DeploymentConfigField[string] `json:"derp_config_url"` - // Usage: Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/ - // Flag: derp-config-path - DERPConfigPath DeploymentConfigField[string] `json:"derp_config_path"` - // Usage: Serve prometheus metrics on the address defined by prometheus address. - // Flag: prometheus-enable - PrometheusEnable DeploymentConfigField[bool] `json:"prometheus_enabled"` - // Usage: The bind address to serve prometheus metrics. - // Flag: prometheus-address - // Default: "127.0.0.1:2112" - PrometheusAddress DeploymentConfigField[string] `json:"prometheus_address"` - // Usage: Serve pprof metrics on the address defined by pprof address. - // Flag: pprof-enable - PprofEnable DeploymentConfigField[bool] `json:"pprof_enabled"` - // Usage: The bind address to serve pprof. - // Flag: pprof-address - // Default: "127.0.0.1:6060" - PprofAddress DeploymentConfigField[string] `json:"pprof_address"` - // Usage: The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. - // Flag: cache-dir - // Default: defaultCacheDir() - CacheDir DeploymentConfigField[string] `json:"cache_dir"` - // Usage: Controls whether data will be stored in an in-memory database. - // Flag: in-memory - // Hidden: true - InMemoryDatabase DeploymentConfigField[bool] `json:"in_memory_database"` - // Usage: Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this. - // Flag: provisioner-daemons - // Default: 3 - ProvisionerDaemonCount DeploymentConfigField[int] `json:"provisioner_daemon_count"` - // Usage: 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". - // Flag: postgres-url - PostgresURL DeploymentConfigField[string] `json:"-"` - // Usage: Client ID for Login with GitHub. - // Flag: oauth2-github-client-id - OAuth2GithubClientID DeploymentConfigField[string] `json:"oauth2_github_client_id"` - // Usage: Client secret for Login with GitHub. - // Flag: oauth2-github-client-secret - OAuth2GithubClientSecret DeploymentConfigField[string] `json:"-"` - // Usage: Organizations the user must be a member of to Login with GitHub. - // Flag: oauth2-github-allowed-orgs - OAuth2GithubAllowedOrganizations DeploymentConfigField[[]string] `json:"oauth2_github_allowed_organizations"` - // Usage: Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /. - // Flag: oauth2-github-allowed-teams - OAuth2GithubAllowedTeams DeploymentConfigField[[]string] `json:"oauth2_github_allowed_teams"` - // Usage: Whether new users can sign up with GitHub. - // Flag: oauth2-github-allow-signups - OAuth2GithubAllowSignups DeploymentConfigField[bool] `json:"oauth2_github_allow_signups"` - // Usage: Base URL of a GitHub Enterprise deployment to use for Login with GitHub. - // Flag: oauth2-github-enterprise-base-url - OAuth2GithubEnterpriseBaseURL DeploymentConfigField[string] `json:"oauth2_github_enterprise_base_url"` - // Usage: Whether new users can sign up with OIDC. - // Flag: oidc-allow-signups - // Default: true - OIDCAllowSignups DeploymentConfigField[bool] `json:"oidc_allow_signups"` - // Usage: Client ID to use for Login with OIDC. - // Flag: oidc-client-id - OIDCClientID DeploymentConfigField[string] `json:"oidc_client_id"` - // Usage: Client secret to use for Login with OIDC. - // Flag: oidc-client-secret - OIDCClientSecret DeploymentConfigField[string] `json:"-"` - // Usage: Email domain that clients logging in with OIDC must match. - // Flag: oidc-email-domain - OIDCEmailDomain DeploymentConfigField[string] `json:"oidc_email_domain"` - // Usage: Issuer URL to use for Login with OIDC. - // Flag: oidc-issuer-url - OIDCIssuerURL DeploymentConfigField[string] `json:"oidc_issuer_url"` - // Usage: Scopes to grant when authenticating with OIDC. - // Flag: oidc-scopes - // Default: []string{oidc.ScopeOpenID, "profile", "email"} - OIDCScopes DeploymentConfigField[[]string] `json:"oidc_scopes"` - // Usage: Whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product. - // Flag: telemetry - // Default: flag.Lookup("test.v") == nil - TelemetryEnable DeploymentConfigField[bool] `json:"telemetry_enable"` - // Usage: Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option. - // Flag: telemetry-trace - // Default: flag.Lookup("test.v") == nil - TelemetryTraceEnable DeploymentConfigField[bool] `json:"telemetry_trace_enable"` - // Usage: URL to send telemetry. - // Flag: telemetry-url - // Hidden: true - // Default: "https://telemetry.coder.com" - TelemetryURL DeploymentConfigField[string] `json:"telemetry_url"` - // Usage: Whether TLS will be enabled. - // Flag: tls-enable - TLSEnable DeploymentConfigField[bool] `json:"tls_enable"` - // Usage: Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file. - // Flag: tls-cert-file - TLSCertFiles DeploymentConfigField[[]string] `json:"tls_cert_files"` - // Usage: PEM-encoded Certificate Authority file used for checking the authenticity of client - // Flag: tls-client-ca-file - TLSClientCAFile DeploymentConfigField[string] `json:"tls_client_ca_file"` - // Usage: Policy the server will follow for TLS Client Authentication. Accepted values are "none", "request", "require-any", "verify-if-given", or "require-and-verify". - // Flag: tls-client-auth - // Default: "request" - TLSClientAuth DeploymentConfigField[string] `json:"tls_client_auth"` - // Usage: Paths to the private keys for each of the certificates. It requires a PEM-encoded file. - // Flag: tls-key-file - TLSKeyFiles DeploymentConfigField[[]string] `json:"tls_key_files"` - // Usage: Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13" - // Flag: tls-min-version - // Default: "tls12" - TLSMinVersion DeploymentConfigField[string] `json:"tls_min_version"` - // Usage: Whether application tracing data is collected. - // Flag: trace - TraceEnable DeploymentConfigField[bool] `json:"trace_enable"` - // Usage: Controls if the 'Secure' property is set on browser session cookies. - // Flag: secure-auth-cookie - SecureAuthCookie DeploymentConfigField[bool] `json:"secure_auth_cookie"` - // Usage: The algorithm to use for generating ssh keys. Accepted values are "ed25519", "ecdsa", or "rsa4096". - // Flag: ssh-keygen-algorithm - // Default: "ed25519" - SSHKeygenAlgorithm DeploymentConfigField[string] `json:"ssh_keygen_algorithm"` - // Usage: Templates to auto-import. Available auto-importable templates are: kubernetes - // Flag: auto-import-template - // Hidden: true - AutoImportTemplates DeploymentConfigField[[]string] `json:"auto_import_templates"` - // Usage: How frequently metrics are refreshed - // Flag: metrics-cache-refresh-interval - // Hidden: true - // Default: time.Hour - MetricsCacheRefreshInterval DeploymentConfigField[time.Duration] `json:"metrics_cache_refresh_interval"` - // Usage: How frequently agent stats are recorded - // Flag: agent-stats-refresh-interval - // Hidden: true - // Default: 10 * time.Minute - AgentStatRefreshInterval DeploymentConfigField[time.Duration] `json:"agent_stat_refresh_interval"` - // Usage: Enables verbose logging. - // Flag: verbose - // Shorthand: v - Verbose DeploymentConfigField[bool] `json:"verbose"` - // Usage: Specifies whether audit logging is enabled. - // Flag: audit-logging - // Default: true - // Enterprise: true - AuditLogging DeploymentConfigField[bool] `json:"audit_logging"` - // Usage: Whether Coder only allows connections to workspaces via the browser. - // Flag: browser-only - // Enterprise: true - BrowserOnly DeploymentConfigField[bool] `json:"browser_only"` - // Usage: Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication. - // Flag: scim-auth-header - // Enterprise: true - SCIMAuthHeader DeploymentConfigField[string] `json:"-"` - // Usage: Enables and sets a limit on how many workspaces each user can create. - // Flag: user-workspace-quota - // Enterprise: true - UserWorkspaceQuota DeploymentConfigField[int] `json:"user_workspace_quota"` -} - -type Flaggable interface { - string | bool | int | time.Duration | []string -} - -type DeploymentConfigField[T Flaggable] struct { - Key string - Name string - Usage string - Flag string - Shorthand string - Enterprise bool - Hidden bool - Value T -} - -// DeploymentConfig returns the deployment config for the coder server. -func (c *Client) DeploymentConfig(ctx context.Context) (DeploymentConfig, error) { - res, err := c.Request(ctx, http.MethodGet, "/api/v2/config/deployment", nil) - if err != nil { - return DeploymentConfig{}, xerrors.Errorf("execute request: %w", err) - } - defer res.Body.Close() - - if res.StatusCode != http.StatusOK { - return DeploymentConfig{}, readBodyAsError(res) - } - - var df DeploymentConfig - return df, json.NewDecoder(res.Body).Decode(&df) -} diff --git a/codersdk/deploymentconfig.go b/codersdk/deploymentconfig.go new file mode 100644 index 0000000000000..810a13c615c69 --- /dev/null +++ b/codersdk/deploymentconfig.go @@ -0,0 +1,98 @@ +package codersdk + +import ( + "context" + "encoding/json" + "net/http" + "time" + + "golang.org/x/xerrors" +) + +// DeploymentConfig is the central configuration for the coder server. +// Secret values should specify `json:"-"` to prevent them from being returned by the API. +type DeploymentConfig struct { + AccessURL DeploymentConfigField[string] `json:"access_url"` + WildcardAccessURL DeploymentConfigField[string] `json:"wildcard_access_url"` + Address DeploymentConfigField[string] `json:"address"` + AutobuildPollInterval DeploymentConfigField[time.Duration] `json:"autobuild_poll_interval"` + DERPServerEnable DeploymentConfigField[bool] `json:"derp_server_enabled"` + DERPServerRegionID DeploymentConfigField[int] `json:"derp_server_region_id"` + DERPServerRegionCode DeploymentConfigField[string] `json:"derp_server_region_code"` + DERPServerRegionName DeploymentConfigField[string] `json:"derp_server_region_name"` + DERPServerSTUNAddresses DeploymentConfigField[[]string] `json:"derp_server_stun_address"` + DERPServerRelayAddress DeploymentConfigField[string] `json:"derp_server_relay_address"` + DERPConfigURL DeploymentConfigField[string] `json:"derp_config_url"` + DERPConfigPath DeploymentConfigField[string] `json:"derp_config_path"` + PrometheusEnable DeploymentConfigField[bool] `json:"prometheus_enabled"` + PrometheusAddress DeploymentConfigField[string] `json:"prometheus_address"` + PprofEnable DeploymentConfigField[bool] `json:"pprof_enabled"` + PprofAddress DeploymentConfigField[string] `json:"pprof_address"` + CacheDir DeploymentConfigField[string] `json:"cache_dir"` + InMemoryDatabase DeploymentConfigField[bool] `json:"in_memory_database"` + ProvisionerDaemonCount DeploymentConfigField[int] `json:"provisioner_daemon_count"` + PostgresURL DeploymentConfigField[string] `json:"-"` + OAuth2GithubClientID DeploymentConfigField[string] `json:"oauth2_github_client_id"` + OAuth2GithubClientSecret DeploymentConfigField[string] `json:"-"` + OAuth2GithubAllowedOrganizations DeploymentConfigField[[]string] `json:"oauth2_github_allowed_organizations"` + OAuth2GithubAllowedTeams DeploymentConfigField[[]string] `json:"oauth2_github_allowed_teams"` + OAuth2GithubAllowSignups DeploymentConfigField[bool] `json:"oauth2_github_allow_signups"` + OAuth2GithubEnterpriseBaseURL DeploymentConfigField[string] `json:"oauth2_github_enterprise_base_url"` + OIDCAllowSignups DeploymentConfigField[bool] `json:"oidc_allow_signups"` + OIDCClientID DeploymentConfigField[string] `json:"oidc_client_id"` + OIDCClientSecret DeploymentConfigField[string] `json:"-"` + OIDCEmailDomain DeploymentConfigField[string] `json:"oidc_email_domain"` + OIDCIssuerURL DeploymentConfigField[string] `json:"oidc_issuer_url"` + OIDCScopes DeploymentConfigField[[]string] `json:"oidc_scopes"` + TelemetryEnable DeploymentConfigField[bool] `json:"telemetry_enable"` + TelemetryTraceEnable DeploymentConfigField[bool] `json:"telemetry_trace_enable"` + TelemetryURL DeploymentConfigField[string] `json:"telemetry_url"` + TLSEnable DeploymentConfigField[bool] `json:"tls_enable"` + TLSCertFiles DeploymentConfigField[[]string] `json:"tls_cert_files"` + TLSClientCAFile DeploymentConfigField[string] `json:"tls_client_ca_file"` + TLSClientAuth DeploymentConfigField[string] `json:"tls_client_auth"` + TLSKeyFiles DeploymentConfigField[[]string] `json:"tls_key_files"` + TLSMinVersion DeploymentConfigField[string] `json:"tls_min_version"` + TraceEnable DeploymentConfigField[bool] `json:"trace_enable"` + SecureAuthCookie DeploymentConfigField[bool] `json:"secure_auth_cookie"` + SSHKeygenAlgorithm DeploymentConfigField[string] `json:"ssh_keygen_algorithm"` + AutoImportTemplates DeploymentConfigField[[]string] `json:"auto_import_templates"` + MetricsCacheRefreshInterval DeploymentConfigField[time.Duration] `json:"metrics_cache_refresh_interval"` + AgentStatRefreshInterval DeploymentConfigField[time.Duration] `json:"agent_stat_refresh_interval"` + Verbose DeploymentConfigField[bool] `json:"verbose"` + AuditLogging DeploymentConfigField[bool] `json:"audit_logging"` + BrowserOnly DeploymentConfigField[bool] `json:"browser_only"` + SCIMAuthHeader DeploymentConfigField[string] `json:"-"` + UserWorkspaceQuota DeploymentConfigField[int] `json:"user_workspace_quota"` +} + +type Flaggable interface { + string | bool | int | time.Duration | []string +} + +type DeploymentConfigField[T Flaggable] struct { + Key string + Name string + Usage string + Flag string + Shorthand string + Enterprise bool + Hidden bool + Value T +} + +// DeploymentConfig returns the deployment config for the coder server. +func (c *Client) DeploymentConfig(ctx context.Context) (DeploymentConfig, error) { + res, err := c.Request(ctx, http.MethodGet, "/api/v2/config/deployment", nil) + if err != nil { + return DeploymentConfig{}, xerrors.Errorf("execute request: %w", err) + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return DeploymentConfig{}, readBodyAsError(res) + } + + var df DeploymentConfig + return df, json.NewDecoder(res.Body).Decode(&df) +} From 687f474c21c78088a695b8aa62faf53a6fb645df Mon Sep 17 00:00:00 2001 From: Garrett Date: Thu, 20 Oct 2022 21:25:40 +0000 Subject: [PATCH 24/43] fix ent --- enterprise/cli/server.go | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/enterprise/cli/server.go b/enterprise/cli/server.go index 3c71f22c2c0fd..eb30ec18c29fc 100644 --- a/enterprise/cli/server.go +++ b/enterprise/cli/server.go @@ -26,13 +26,10 @@ import ( func server() *cobra.Command { vip := deployment.NewViper() cmd := agpl.Server(vip, func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, io.Closer, error) { - cfg, err := deployment.Config(vip) - if err != nil { - return nil, nil, xerrors.Errorf("failed to read config: %w", err) - } + cfg := deployment.Config(vip) - if cfg.DERP.Server.RelayAddress != "" { - _, err := url.Parse(cfg.DERP.Server.RelayAddress) + if cfg.DERPServerRelayAddress.Value != "" { + _, err := url.Parse(cfg.DERPServerRelayAddress.Value) if err != nil { return nil, nil, xerrors.Errorf("derp-server-relay-address must be a valid HTTP URL: %w", err) } @@ -55,7 +52,7 @@ func server() *cobra.Command { } options.DERPServer.SetMeshKey(meshKey) - if dflags.AuditLogging.Value { + if options.DeploymentConfig.AuditLogging.Value { options.Auditor = audit.NewAuditor(audit.DefaultFilter, backends.NewPostgres(options.Database, true), backends.NewSlog(options.Logger), @@ -63,13 +60,13 @@ func server() *cobra.Command { } o := &coderd.Options{ - AuditLogging: options.DeploymentConfig.AuditLogging, - BrowserOnly: options.DeploymentConfig.BrowserOnly, - SCIMAPIKey: []byte(options.DeploymentConfig.SCIMAuthHeader), - UserWorkspaceQuota: options.DeploymentConfig.UserWorkspaceQuota, + AuditLogging: options.DeploymentConfig.AuditLogging.Value, + BrowserOnly: options.DeploymentConfig.BrowserOnly.Value, + SCIMAPIKey: []byte(options.DeploymentConfig.SCIMAuthHeader.Value), + UserWorkspaceQuota: options.DeploymentConfig.UserWorkspaceQuota.Value, RBAC: true, - DERPServerRelayAddress: options.DeploymentConfig.DERP.Server.RelayAddress, - DERPServerRegionID: options.DeploymentConfig.DERP.Server.RegionID, + DERPServerRelayAddress: options.DeploymentConfig.DERPServerRelayAddress.Value, + DERPServerRegionID: options.DeploymentConfig.DERPServerRegionID.Value, Options: options, } From b776d7d1af05dd02e710f39eff028e8e51d09b1b Mon Sep 17 00:00:00 2001 From: Garrett Date: Thu, 20 Oct 2022 21:33:33 +0000 Subject: [PATCH 25/43] lint --- cli/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/server.go b/cli/server.go index b8e1f10d3e7d4..7513a25e51ca9 100644 --- a/cli/server.go +++ b/cli/server.go @@ -166,7 +166,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co listener, err := net.Listen("tcp", cfg.Address.Value) if err != nil { - return xerrors.Errorf("listen %q: %w", cfg.Address, err) + return xerrors.Errorf("listen %q: %w", cfg.Address.Value, err) } defer listener.Close() @@ -268,7 +268,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co sshKeygenAlgorithm, err := gitsshkey.ParseAlgorithm(cfg.SSHKeygenAlgorithm.Value) if err != nil { - return xerrors.Errorf("parse ssh keygen algorithm %s: %w", cfg.SSHKeygenAlgorithm, err) + return xerrors.Errorf("parse ssh keygen algorithm %s: %w", cfg.SSHKeygenAlgorithm.Value, err) } // Validate provided auto-import templates. From eac9ad379702433b7b63e1cb50083ea359cfce4a Mon Sep 17 00:00:00 2001 From: Garrett Date: Thu, 20 Oct 2022 21:34:15 +0000 Subject: [PATCH 26/43] make gen --- site/src/api/typesGenerated.ts | 187 ++++++++++++--------------------- 1 file changed, 65 insertions(+), 122 deletions(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index c8540c31888a9..6fa61d9db2836 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -126,19 +126,6 @@ export interface AzureInstanceIdentityToken { readonly encoding: string } -// From codersdk/flags.go -export interface BoolFlag { - readonly name: string - readonly flag: string - readonly env_var: string - readonly shorthand: string - readonly description: string - readonly enterprise: boolean - readonly hidden: boolean - readonly default: boolean - readonly value: boolean -} - // From codersdk/buildinfo.go export interface BuildInfoResponse { readonly external_url: string @@ -264,75 +251,68 @@ export interface DERPRegion { readonly latency_ms: number } -// From codersdk/flags.go -export interface DeploymentFlags { - readonly access_url: StringFlag - readonly wildcard_access_url: StringFlag - readonly address: StringFlag - readonly autobuild_poll_interval: DurationFlag - readonly derp_server_enabled: BoolFlag - readonly derp_server_region_id: IntFlag - readonly derp_server_region_code: StringFlag - readonly derp_server_region_name: StringFlag - readonly derp_server_stun_address: StringArrayFlag - readonly derp_server_relay_address: StringFlag - readonly derp_config_url: StringFlag - readonly derp_config_path: StringFlag - readonly prom_enabled: BoolFlag - readonly prom_address: StringFlag - readonly pprof_enabled: BoolFlag - readonly pprof_address: StringFlag - readonly cache_dir: StringFlag - readonly in_memory_database: BoolFlag - readonly provisioner_daemon_count: IntFlag - readonly postgres_url: StringFlag - readonly oauth2_github_client_id: StringFlag - readonly oauth2_github_client_secret: StringFlag - readonly oauth2_github_allowed_organizations: StringArrayFlag - readonly oauth2_github_allowed_teams: StringArrayFlag - readonly oauth2_github_allow_signups: BoolFlag - readonly oauth2_github_enterprise_base_url: StringFlag - readonly oidc_allow_signups: BoolFlag - readonly oidc_client_id: StringFlag - readonly oidc_client_secret: StringFlag - readonly oidc_email_domain: StringFlag - readonly oidc_issuer_url: StringFlag - readonly oidc_scopes: StringArrayFlag - readonly telemetry_enable: BoolFlag - readonly telemetry_trace_enable: BoolFlag - readonly telemetry_url: StringFlag - readonly tls_enable: BoolFlag - readonly tls_cert_files: StringArrayFlag - readonly tls_client_ca_file: StringFlag - readonly tls_client_auth: StringFlag - readonly tls_key_files: StringArrayFlag - readonly tls_min_version: StringFlag - readonly trace_enable: BoolFlag - readonly secure_auth_cookie: BoolFlag - readonly ssh_keygen_algorithm: StringFlag - readonly auto_import_templates: StringArrayFlag - readonly metrics_cache_refresh_interval: DurationFlag - readonly agent_stat_refresh_interval: DurationFlag - readonly verbose: BoolFlag - readonly audit_logging: BoolFlag - readonly browser_only: BoolFlag - readonly scim_auth_header: StringFlag - readonly user_workspace_quota: IntFlag -} - -// From codersdk/flags.go -export interface DurationFlag { - readonly name: string - readonly flag: string - readonly env_var: string - 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") - readonly value: number +// From codersdk/deploymentconfig.go +export interface DeploymentConfig { + readonly access_url: DeploymentConfigField + readonly wildcard_access_url: DeploymentConfigField + readonly address: DeploymentConfigField + readonly autobuild_poll_interval: DeploymentConfigField + readonly derp_server_enabled: DeploymentConfigField + readonly derp_server_region_id: DeploymentConfigField + readonly derp_server_region_code: DeploymentConfigField + readonly derp_server_region_name: DeploymentConfigField + readonly derp_server_stun_address: DeploymentConfigField + readonly derp_server_relay_address: DeploymentConfigField + readonly derp_config_url: DeploymentConfigField + readonly derp_config_path: DeploymentConfigField + readonly prometheus_enabled: DeploymentConfigField + readonly prometheus_address: DeploymentConfigField + readonly pprof_enabled: DeploymentConfigField + readonly pprof_address: DeploymentConfigField + readonly cache_dir: DeploymentConfigField + readonly in_memory_database: DeploymentConfigField + readonly provisioner_daemon_count: DeploymentConfigField + readonly oauth2_github_client_id: DeploymentConfigField + readonly oauth2_github_allowed_organizations: DeploymentConfigField + readonly oauth2_github_allowed_teams: DeploymentConfigField + readonly oauth2_github_allow_signups: DeploymentConfigField + readonly oauth2_github_enterprise_base_url: DeploymentConfigField + readonly oidc_allow_signups: DeploymentConfigField + readonly oidc_client_id: DeploymentConfigField + readonly oidc_email_domain: DeploymentConfigField + readonly oidc_issuer_url: DeploymentConfigField + readonly oidc_scopes: DeploymentConfigField + readonly telemetry_enable: DeploymentConfigField + readonly telemetry_trace_enable: DeploymentConfigField + readonly telemetry_url: DeploymentConfigField + readonly tls_enable: DeploymentConfigField + readonly tls_cert_files: DeploymentConfigField + readonly tls_client_ca_file: DeploymentConfigField + readonly tls_client_auth: DeploymentConfigField + readonly tls_key_files: DeploymentConfigField + readonly tls_min_version: DeploymentConfigField + readonly trace_enable: DeploymentConfigField + readonly secure_auth_cookie: DeploymentConfigField + readonly ssh_keygen_algorithm: DeploymentConfigField + readonly auto_import_templates: DeploymentConfigField + readonly metrics_cache_refresh_interval: DeploymentConfigField + readonly agent_stat_refresh_interval: DeploymentConfigField + readonly verbose: DeploymentConfigField + readonly audit_logging: DeploymentConfigField + readonly browser_only: DeploymentConfigField + readonly user_workspace_quota: DeploymentConfigField +} + +// From codersdk/deploymentconfig.go +export interface DeploymentConfigField { + readonly Key: string + readonly Name: string + readonly Usage: string + readonly Flag: string + readonly Shorthand: string + readonly Enterprise: boolean + readonly Hidden: boolean + readonly Value: T } // From codersdk/features.go @@ -387,19 +367,6 @@ export interface Healthcheck { readonly threshold: number } -// From codersdk/flags.go -export interface IntFlag { - readonly name: string - readonly flag: string - readonly env_var: string - readonly shorthand: string - readonly description: string - readonly enterprise: boolean - readonly hidden: boolean - readonly default: number - readonly value: number -} - // From codersdk/licenses.go export interface License { readonly id: number @@ -564,33 +531,6 @@ export interface ServerSentEvent { readonly data: any } -// From codersdk/flags.go -export interface StringArrayFlag { - readonly name: string - readonly flag: string - readonly env_var: string - readonly shorthand: string - readonly description: string - readonly enterprise: boolean - readonly hidden: boolean - readonly default: string[] - readonly value: string[] -} - -// From codersdk/flags.go -export interface StringFlag { - readonly name: string - readonly flag: string - readonly env_var: string - readonly shorthand: string - readonly description: string - readonly enterprise: boolean - readonly secret: boolean - readonly hidden: boolean - readonly default: string - readonly value: string -} - // From codersdk/templates.go export interface Template { readonly id: string @@ -999,3 +939,6 @@ export type WorkspaceStatus = // From codersdk/workspacebuilds.go export type WorkspaceTransition = "delete" | "start" | "stop" + +// From codersdk/deploymentconfig.go +export type Flaggable = string | boolean | number | string[] From 42e70eed47be25f338c137b71b07a49d581db5a2 Mon Sep 17 00:00:00 2001 From: Garrett Date: Thu, 20 Oct 2022 22:29:53 +0000 Subject: [PATCH 27/43] try supporting flag --- cli/config/file.go | 4 ++ cli/deployment/config.go | 70 +++++++++++++++++++++++++++++++-- cli/root.go | 3 +- cli/server.go | 16 ++++---- coderd/deploymentconfig_test.go | 67 +++++++++++++------------------ enterprise/cli/server.go | 12 ++---- 6 files changed, 112 insertions(+), 60 deletions(-) diff --git a/cli/config/file.go b/cli/config/file.go index 388ce0881f304..fb4fa5f842687 100644 --- a/cli/config/file.go +++ b/cli/config/file.go @@ -42,6 +42,10 @@ func (r Root) PostgresPort() File { return File(filepath.Join(r.PostgresPath(), "port")) } +func (r Root) DeploymentConfigPath() string { + return filepath.Join(string(r), "server.yaml") +} + // File provides convenience methods for interacting with *os.File. type File string diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 624835b4f3dc2..f041afa4dbc63 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -12,6 +12,7 @@ import ( "github.com/coreos/go-oidc/v3/oidc" "github.com/spf13/pflag" "github.com/spf13/viper" + "golang.org/x/xerrors" "github.com/coder/coder/cli/cliui" "github.com/coder/coder/codersdk" @@ -317,10 +318,71 @@ func newConfig() codersdk.DeploymentConfig { } } -func Config(vip *viper.Viper) codersdk.DeploymentConfig { +//nolint:revive +func Config(flagset *pflag.FlagSet, enterprise bool) (codersdk.DeploymentConfig, error) { dc := newConfig() - dcv := reflect.ValueOf(&dc).Elem() + flg, err := flagset.GetString("global-config") + if err != nil { + return dc, xerrors.Errorf("get global config from flag: %w", err) + } + vip := viper.New() + vip.SetEnvPrefix("coder") + vip.AutomaticEnv() + + dcv := reflect.ValueOf(dc) t := dcv.Type() + for i := 0; i < t.NumField(); i++ { + fv := dcv.Field(i) + key := fv.FieldByName("Key").String() + value := fv.FieldByName("Value").Interface() + vip.SetDefault(key, value) + } + if flg != "" { + vip.SetConfigFile(flg) + err = vip.ReadInConfig() + if err != nil { + return dc, xerrors.Errorf("reading deployment config: %w", err) + } + } + + for i := 0; i < t.NumField(); i++ { + fv := dcv.Field(i) + isEnt := fv.FieldByName("Enterprise").Bool() + if enterprise != isEnt { + continue + } + key := fv.FieldByName("Key").String() + flg := fv.FieldByName("Flag").String() + if flg == "" { + continue + } + usage := fv.FieldByName("Usage").String() + usage = fmt.Sprintf("%s\n%s", usage, cliui.Styles.Placeholder.Render("Consumes $"+formatEnv(key))) + shorthand := fv.FieldByName("Shorthand").String() + hidden := fv.FieldByName("Hidden").Bool() + value := fv.FieldByName("Value").Interface() + + switch value.(type) { + case string: + _ = flagset.StringP(flg, shorthand, vip.GetString(key), usage) + case bool: + _ = flagset.BoolP(flg, shorthand, vip.GetBool(key), usage) + case int: + _ = flagset.IntP(flg, shorthand, vip.GetInt(key), usage) + case time.Duration: + _ = flagset.DurationP(flg, shorthand, vip.GetDuration(key), usage) + case []string: + _ = flagset.StringSliceP(flg, shorthand, vip.GetStringSlice(key), usage) + default: + continue + } + + _ = vip.BindPFlag(key, flagset.Lookup(flg)) + if hidden { + _ = flagset.MarkHidden(flg) + } + } + for i := 0; i < t.NumField(); i++ { fve := dcv.Field(i) key := fve.FieldByName("Key").String() @@ -337,10 +399,12 @@ func Config(vip *viper.Viper) codersdk.DeploymentConfig { fve.FieldByName("Value").SetInt(int64(vip.GetDuration(key))) case []string: fve.FieldByName("Value").Set(reflect.ValueOf(vip.GetStringSlice(key))) + default: + return dc, xerrors.Errorf("unsupported type %T", value) } } - return dc + return dc, nil } func NewViper() *viper.Viper { diff --git a/cli/root.go b/cli/root.go index f86d44161e46f..fd7b9087f48f2 100644 --- a/cli/root.go +++ b/cli/root.go @@ -23,7 +23,6 @@ import ( "github.com/coder/coder/cli/cliflag" "github.com/coder/coder/cli/cliui" "github.com/coder/coder/cli/config" - "github.com/coder/coder/cli/deployment" "github.com/coder/coder/coderd" "github.com/coder/coder/codersdk" ) @@ -101,7 +100,7 @@ func Core() []*cobra.Command { } func AGPL() []*cobra.Command { - all := append(Core(), Server(deployment.NewViper(), func(_ context.Context, o *coderd.Options) (*coderd.API, io.Closer, error) { + all := append(Core(), Server(func(_ context.Context, o *coderd.Options) (*coderd.API, io.Closer, error) { api := coderd.New(o) return api, api, nil })) diff --git a/cli/server.go b/cli/server.go index 7513a25e51ca9..616b4427be3cf 100644 --- a/cli/server.go +++ b/cli/server.go @@ -32,7 +32,6 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/afero" "github.com/spf13/cobra" - "github.com/spf13/viper" "go.opentelemetry.io/otel/trace" "golang.org/x/mod/semver" "golang.org/x/oauth2" @@ -71,12 +70,15 @@ import ( ) // nolint:gocyclo -func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Closer, error)) *cobra.Command { +func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Closer, error)) *cobra.Command { root := &cobra.Command{ Use: "server", Short: "Start a Coder server", RunE: func(cmd *cobra.Command, args []string) error { - cfg := deployment.Config(vip) + cfg, err := deployment.Config(cmd.Flags(), false) + if err != nil { + return xerrors.Errorf("getting up deployment config", err) + } printLogo(cmd) logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) if cfg.Verbose.Value { @@ -144,7 +146,6 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co } } - var err error config := createConfig(cmd) builtinPostgres := false // Only use built-in if PostgreSQL URL isn't specified! @@ -734,7 +735,10 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co Use: "postgres-builtin-serve", Short: "Run the built-in PostgreSQL deployment.", RunE: func(cmd *cobra.Command, args []string) error { - dcfg := deployment.Config(vip) + dcfg, err := deployment.Config(cmd.Flags(), false) + if err != nil { + return xerrors.Errorf("getting up deployment config", err) + } cfg := createConfig(cmd) logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) if dcfg.Verbose.Value { @@ -758,8 +762,6 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co }, }) - deployment.AttachFlags(root.Flags(), vip, false) - return root } diff --git a/coderd/deploymentconfig_test.go b/coderd/deploymentconfig_test.go index 877a6278ed99c..6fabbdf45f3ac 100644 --- a/coderd/deploymentconfig_test.go +++ b/coderd/deploymentconfig_test.go @@ -1,42 +1,31 @@ package coderd_test -import ( - "context" - "testing" +// func TestDeploymentConfig(t *testing.T) { +// t.Parallel() +// hi := "hi" +// ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) +// defer cancel() +// vip := deployment.NewViper() +// cfg := deployment.Config(vip) +// // values should be returned +// cfg.AccessURL.Value = hi +// // values should not be returned +// cfg.OAuth2GithubClientSecret.Value = hi +// cfg.OIDCClientSecret.Value = hi +// cfg.PostgresURL.Value = hi +// cfg.SCIMAuthHeader.Value = hi - "github.com/stretchr/testify/require" - - "github.com/coder/coder/cli/deployment" - "github.com/coder/coder/coderd/coderdtest" - "github.com/coder/coder/testutil" -) - -func TestDeploymentConfig(t *testing.T) { - t.Parallel() - hi := "hi" - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - vip := deployment.NewViper() - cfg := deployment.Config(vip) - // values should be returned - cfg.AccessURL.Value = hi - // values should not be returned - cfg.OAuth2GithubClientSecret.Value = hi - cfg.OIDCClientSecret.Value = hi - cfg.PostgresURL.Value = hi - cfg.SCIMAuthHeader.Value = hi - - client := coderdtest.New(t, &coderdtest.Options{ - DeploymentConfig: &cfg, - }) - _ = coderdtest.CreateFirstUser(t, client) - scrubbed, err := client.DeploymentConfig(ctx) - require.NoError(t, err) - // ensure normal values pass through - require.EqualValues(t, hi, scrubbed.AccessURL.Value) - // ensure secrets are removed - require.Empty(t, scrubbed.OAuth2GithubClientSecret.Value) - require.Empty(t, scrubbed.OIDCClientSecret.Value) - require.Empty(t, scrubbed.PostgresURL.Value) - require.Empty(t, scrubbed.SCIMAuthHeader.Value) -} +// client := coderdtest.New(t, &coderdtest.Options{ +// DeploymentConfig: &cfg, +// }) +// _ = coderdtest.CreateFirstUser(t, client) +// scrubbed, err := client.DeploymentConfig(ctx) +// require.NoError(t, err) +// // ensure normal values pass through +// require.EqualValues(t, hi, scrubbed.AccessURL.Value) +// // ensure secrets are removed +// require.Empty(t, scrubbed.OAuth2GithubClientSecret.Value) +// require.Empty(t, scrubbed.OIDCClientSecret.Value) +// require.Empty(t, scrubbed.PostgresURL.Value) +// require.Empty(t, scrubbed.SCIMAuthHeader.Value) +// } diff --git a/enterprise/cli/server.go b/enterprise/cli/server.go index eb30ec18c29fc..293c5c712aa8f 100644 --- a/enterprise/cli/server.go +++ b/enterprise/cli/server.go @@ -12,7 +12,6 @@ import ( "tailscale.com/derp" "tailscale.com/types/key" - "github.com/coder/coder/cli/deployment" "github.com/coder/coder/cryptorand" "github.com/coder/coder/enterprise/audit" "github.com/coder/coder/enterprise/audit/backends" @@ -24,12 +23,9 @@ import ( ) func server() *cobra.Command { - vip := deployment.NewViper() - cmd := agpl.Server(vip, func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, io.Closer, error) { - cfg := deployment.Config(vip) - - if cfg.DERPServerRelayAddress.Value != "" { - _, err := url.Parse(cfg.DERPServerRelayAddress.Value) + cmd := agpl.Server(func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, io.Closer, error) { + if options.DeploymentConfig.DERPServerRelayAddress.Value != "" { + _, err := url.Parse(options.DeploymentConfig.DERPServerRelayAddress.Value) if err != nil { return nil, nil, xerrors.Errorf("derp-server-relay-address must be a valid HTTP URL: %w", err) } @@ -78,7 +74,5 @@ func server() *cobra.Command { return api.AGPL, api, nil }) - deployment.AttachFlags(cmd.Flags(), vip, true) - return cmd } From 39409489a500006ed0a0c1e4bf98452c9e454d67 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 02:15:17 +0000 Subject: [PATCH 28/43] fix tests --- cli/deployment/config.go | 61 +++--------------------------------- cli/root.go | 3 +- cli/server.go | 21 ++++++------- codersdk/deploymentconfig.go | 1 - enterprise/cli/server.go | 6 +++- 5 files changed, 22 insertions(+), 70 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index f041afa4dbc63..6101dea3e3c94 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -284,12 +284,6 @@ func newConfig() codersdk.DeploymentConfig { Hidden: true, Value: 10 * time.Minute, }, - Verbose: codersdk.DeploymentConfigField[bool]{ - Key: "verbose", - Usage: "Enables verbose logging.", - Flag: "verbose", - Shorthand: "v", - }, AuditLogging: codersdk.DeploymentConfigField[bool]{ Key: "audit_logging", Usage: "Specifies whether audit logging is enabled.", @@ -319,70 +313,25 @@ func newConfig() codersdk.DeploymentConfig { } //nolint:revive -func Config(flagset *pflag.FlagSet, enterprise bool) (codersdk.DeploymentConfig, error) { +func Config(flagset *pflag.FlagSet, vip *viper.Viper, enterprise bool) (codersdk.DeploymentConfig, error) { dc := newConfig() flg, err := flagset.GetString("global-config") if err != nil { return dc, xerrors.Errorf("get global config from flag: %w", err) } - vip := viper.New() vip.SetEnvPrefix("coder") vip.AutomaticEnv() - dcv := reflect.ValueOf(dc) - t := dcv.Type() - for i := 0; i < t.NumField(); i++ { - fv := dcv.Field(i) - key := fv.FieldByName("Key").String() - value := fv.FieldByName("Value").Interface() - vip.SetDefault(key, value) - } if flg != "" { - vip.SetConfigFile(flg) + vip.SetConfigFile(flg + "/server.yaml") err = vip.ReadInConfig() - if err != nil { + if err != nil && !xerrors.Is(err, os.ErrNotExist) { return dc, xerrors.Errorf("reading deployment config: %w", err) } } - for i := 0; i < t.NumField(); i++ { - fv := dcv.Field(i) - isEnt := fv.FieldByName("Enterprise").Bool() - if enterprise != isEnt { - continue - } - key := fv.FieldByName("Key").String() - flg := fv.FieldByName("Flag").String() - if flg == "" { - continue - } - usage := fv.FieldByName("Usage").String() - usage = fmt.Sprintf("%s\n%s", usage, cliui.Styles.Placeholder.Render("Consumes $"+formatEnv(key))) - shorthand := fv.FieldByName("Shorthand").String() - hidden := fv.FieldByName("Hidden").Bool() - value := fv.FieldByName("Value").Interface() - - switch value.(type) { - case string: - _ = flagset.StringP(flg, shorthand, vip.GetString(key), usage) - case bool: - _ = flagset.BoolP(flg, shorthand, vip.GetBool(key), usage) - case int: - _ = flagset.IntP(flg, shorthand, vip.GetInt(key), usage) - case time.Duration: - _ = flagset.DurationP(flg, shorthand, vip.GetDuration(key), usage) - case []string: - _ = flagset.StringSliceP(flg, shorthand, vip.GetStringSlice(key), usage) - default: - continue - } - - _ = vip.BindPFlag(key, flagset.Lookup(flg)) - if hidden { - _ = flagset.MarkHidden(flg) - } - } - + dcv := reflect.ValueOf(&dc).Elem() + t := dcv.Type() for i := 0; i < t.NumField(); i++ { fve := dcv.Field(i) key := fve.FieldByName("Key").String() diff --git a/cli/root.go b/cli/root.go index fd7b9087f48f2..f86d44161e46f 100644 --- a/cli/root.go +++ b/cli/root.go @@ -23,6 +23,7 @@ import ( "github.com/coder/coder/cli/cliflag" "github.com/coder/coder/cli/cliui" "github.com/coder/coder/cli/config" + "github.com/coder/coder/cli/deployment" "github.com/coder/coder/coderd" "github.com/coder/coder/codersdk" ) @@ -100,7 +101,7 @@ func Core() []*cobra.Command { } func AGPL() []*cobra.Command { - all := append(Core(), Server(func(_ context.Context, o *coderd.Options) (*coderd.API, io.Closer, error) { + all := append(Core(), Server(deployment.NewViper(), func(_ context.Context, o *coderd.Options) (*coderd.API, io.Closer, error) { api := coderd.New(o) return api, api, nil })) diff --git a/cli/server.go b/cli/server.go index 616b4427be3cf..46836e948180d 100644 --- a/cli/server.go +++ b/cli/server.go @@ -32,6 +32,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/afero" "github.com/spf13/cobra" + "github.com/spf13/viper" "go.opentelemetry.io/otel/trace" "golang.org/x/mod/semver" "golang.org/x/oauth2" @@ -70,18 +71,18 @@ import ( ) // nolint:gocyclo -func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Closer, error)) *cobra.Command { +func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Closer, error)) *cobra.Command { root := &cobra.Command{ Use: "server", Short: "Start a Coder server", RunE: func(cmd *cobra.Command, args []string) error { - cfg, err := deployment.Config(cmd.Flags(), false) + cfg, err := deployment.Config(cmd.Flags(), vip, false) if err != nil { - return xerrors.Errorf("getting up deployment config", err) + return xerrors.Errorf("getting deployment config", err) } printLogo(cmd) logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) - if cfg.Verbose.Value { + if ok, _ := cmd.Flags().GetBool(varVerbose); ok { logger = logger.Leveled(slog.LevelDebug) } @@ -672,7 +673,7 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Close go func() { defer wg.Done() - if cfg.Verbose.Value { + if ok, _ := cmd.Flags().GetBool(varVerbose); ok { cmd.Printf("Shutting down provisioner daemon %d...\n", id) } err := shutdownWithTimeout(provisionerDaemon.Shutdown, 5*time.Second) @@ -685,7 +686,7 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Close cmd.PrintErrf("Close provisioner daemon %d: %s\n", id, err) return } - if cfg.Verbose.Value { + if ok, _ := cmd.Flags().GetBool(varVerbose); ok { cmd.Printf("Gracefully shut down provisioner daemon %d\n", id) } }() @@ -735,13 +736,9 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Close Use: "postgres-builtin-serve", Short: "Run the built-in PostgreSQL deployment.", RunE: func(cmd *cobra.Command, args []string) error { - dcfg, err := deployment.Config(cmd.Flags(), false) - if err != nil { - return xerrors.Errorf("getting up deployment config", err) - } cfg := createConfig(cmd) logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) - if dcfg.Verbose.Value { + if ok, _ := cmd.Flags().GetBool(varVerbose); ok { logger = logger.Leveled(slog.LevelDebug) } @@ -762,6 +759,8 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Close }, }) + deployment.AttachFlags(root.Flags(), vip, false) + return root } diff --git a/codersdk/deploymentconfig.go b/codersdk/deploymentconfig.go index 810a13c615c69..b46adb117ee07 100644 --- a/codersdk/deploymentconfig.go +++ b/codersdk/deploymentconfig.go @@ -59,7 +59,6 @@ type DeploymentConfig struct { AutoImportTemplates DeploymentConfigField[[]string] `json:"auto_import_templates"` MetricsCacheRefreshInterval DeploymentConfigField[time.Duration] `json:"metrics_cache_refresh_interval"` AgentStatRefreshInterval DeploymentConfigField[time.Duration] `json:"agent_stat_refresh_interval"` - Verbose DeploymentConfigField[bool] `json:"verbose"` AuditLogging DeploymentConfigField[bool] `json:"audit_logging"` BrowserOnly DeploymentConfigField[bool] `json:"browser_only"` SCIMAuthHeader DeploymentConfigField[string] `json:"-"` diff --git a/enterprise/cli/server.go b/enterprise/cli/server.go index 293c5c712aa8f..04bbbe037907b 100644 --- a/enterprise/cli/server.go +++ b/enterprise/cli/server.go @@ -12,6 +12,7 @@ import ( "tailscale.com/derp" "tailscale.com/types/key" + "github.com/coder/coder/cli/deployment" "github.com/coder/coder/cryptorand" "github.com/coder/coder/enterprise/audit" "github.com/coder/coder/enterprise/audit/backends" @@ -23,7 +24,8 @@ import ( ) func server() *cobra.Command { - cmd := agpl.Server(func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, io.Closer, error) { + vip := deployment.NewViper() + cmd := agpl.Server(vip, func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, io.Closer, error) { if options.DeploymentConfig.DERPServerRelayAddress.Value != "" { _, err := url.Parse(options.DeploymentConfig.DERPServerRelayAddress.Value) if err != nil { @@ -74,5 +76,7 @@ func server() *cobra.Command { return api.AGPL, api, nil }) + deployment.AttachFlags(cmd.Flags(), vip, true) + return cmd } From dd2f4ff719ba74b3012e6254ff14e5a4f5a912e2 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 02:19:18 +0000 Subject: [PATCH 29/43] add back test --- coderd/deploymentconfig_test.go | 71 ++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/coderd/deploymentconfig_test.go b/coderd/deploymentconfig_test.go index 6fabbdf45f3ac..3020db07cfdbb 100644 --- a/coderd/deploymentconfig_test.go +++ b/coderd/deploymentconfig_test.go @@ -1,31 +1,46 @@ package coderd_test -// func TestDeploymentConfig(t *testing.T) { -// t.Parallel() -// hi := "hi" -// ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) -// defer cancel() -// vip := deployment.NewViper() -// cfg := deployment.Config(vip) -// // values should be returned -// cfg.AccessURL.Value = hi -// // values should not be returned -// cfg.OAuth2GithubClientSecret.Value = hi -// cfg.OIDCClientSecret.Value = hi -// cfg.PostgresURL.Value = hi -// cfg.SCIMAuthHeader.Value = hi +import ( + "context" + "testing" -// client := coderdtest.New(t, &coderdtest.Options{ -// DeploymentConfig: &cfg, -// }) -// _ = coderdtest.CreateFirstUser(t, client) -// scrubbed, err := client.DeploymentConfig(ctx) -// require.NoError(t, err) -// // ensure normal values pass through -// require.EqualValues(t, hi, scrubbed.AccessURL.Value) -// // ensure secrets are removed -// require.Empty(t, scrubbed.OAuth2GithubClientSecret.Value) -// require.Empty(t, scrubbed.OIDCClientSecret.Value) -// require.Empty(t, scrubbed.PostgresURL.Value) -// require.Empty(t, scrubbed.SCIMAuthHeader.Value) -// } + "github.com/spf13/pflag" + "github.com/stretchr/testify/require" + + "github.com/coder/coder/cli/deployment" + "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/testutil" +) + +func TestDeploymentConfig(t *testing.T) { + t.Parallel() + hi := "hi" + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + vip := deployment.NewViper() + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + fs.String("global-config", hi, "usage") + cfg, err := deployment.Config(fs, vip, false) + require.NoError(t, err) + // values should be returned + cfg.AccessURL.Value = hi + // values should not be returned + cfg.OAuth2GithubClientSecret.Value = hi + cfg.OIDCClientSecret.Value = hi + cfg.PostgresURL.Value = hi + cfg.SCIMAuthHeader.Value = hi + + client := coderdtest.New(t, &coderdtest.Options{ + DeploymentConfig: &cfg, + }) + _ = coderdtest.CreateFirstUser(t, client) + scrubbed, err := client.DeploymentConfig(ctx) + require.NoError(t, err) + // ensure normal values pass through + require.EqualValues(t, hi, scrubbed.AccessURL.Value) + // ensure secrets are removed + require.Empty(t, scrubbed.OAuth2GithubClientSecret.Value) + require.Empty(t, scrubbed.OIDCClientSecret.Value) + require.Empty(t, scrubbed.PostgresURL.Value) + require.Empty(t, scrubbed.SCIMAuthHeader.Value) +} From 26e698d97791641185424511e1318932bbe8f2c0 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 02:20:15 +0000 Subject: [PATCH 30/43] add back test2 --- cli/deployment/config.go | 2 +- cli/server.go | 2 +- coderd/deploymentconfig_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 6101dea3e3c94..a85801ab510b6 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -313,7 +313,7 @@ func newConfig() codersdk.DeploymentConfig { } //nolint:revive -func Config(flagset *pflag.FlagSet, vip *viper.Viper, enterprise bool) (codersdk.DeploymentConfig, error) { +func Config(flagset *pflag.FlagSet, vip *viper.Viper) (codersdk.DeploymentConfig, error) { dc := newConfig() flg, err := flagset.GetString("global-config") if err != nil { diff --git a/cli/server.go b/cli/server.go index 46836e948180d..cd914bf5b87f2 100644 --- a/cli/server.go +++ b/cli/server.go @@ -76,7 +76,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co Use: "server", Short: "Start a Coder server", RunE: func(cmd *cobra.Command, args []string) error { - cfg, err := deployment.Config(cmd.Flags(), vip, false) + cfg, err := deployment.Config(cmd.Flags(), vip) if err != nil { return xerrors.Errorf("getting deployment config", err) } diff --git a/coderd/deploymentconfig_test.go b/coderd/deploymentconfig_test.go index 3020db07cfdbb..58220c67e3bda 100644 --- a/coderd/deploymentconfig_test.go +++ b/coderd/deploymentconfig_test.go @@ -20,7 +20,7 @@ func TestDeploymentConfig(t *testing.T) { vip := deployment.NewViper() fs := pflag.NewFlagSet("test", pflag.ContinueOnError) fs.String("global-config", hi, "usage") - cfg, err := deployment.Config(fs, vip, false) + cfg, err := deployment.Config(fs, vip) require.NoError(t, err) // values should be returned cfg.AccessURL.Value = hi From b3c1ec8fdfc8eb4cbacdf582d3fc9b862b15f09f Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 02:22:39 +0000 Subject: [PATCH 31/43] centralize flag name --- cli/config/file.go | 4 ++++ cli/deployment/config.go | 3 ++- cli/root.go | 5 ++--- cli/server.go | 2 +- coderd/deploymentconfig_test.go | 3 ++- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/cli/config/file.go b/cli/config/file.go index fb4fa5f842687..26af6896c14b3 100644 --- a/cli/config/file.go +++ b/cli/config/file.go @@ -6,6 +6,10 @@ import ( "path/filepath" ) +const ( + FlagName = "global-config" +) + // Root represents the configuration directory. type Root string diff --git a/cli/deployment/config.go b/cli/deployment/config.go index a85801ab510b6..42634b2ca6b6c 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -15,6 +15,7 @@ import ( "golang.org/x/xerrors" "github.com/coder/coder/cli/cliui" + "github.com/coder/coder/cli/config" "github.com/coder/coder/codersdk" ) @@ -315,7 +316,7 @@ func newConfig() codersdk.DeploymentConfig { //nolint:revive func Config(flagset *pflag.FlagSet, vip *viper.Viper) (codersdk.DeploymentConfig, error) { dc := newConfig() - flg, err := flagset.GetString("global-config") + flg, err := flagset.GetString(config.FlagName) if err != nil { return dc, xerrors.Errorf("get global config from flag: %w", err) } diff --git a/cli/root.go b/cli/root.go index f86d44161e46f..c40bd927e2b90 100644 --- a/cli/root.go +++ b/cli/root.go @@ -43,7 +43,6 @@ const ( varToken = "token" varAgentToken = "agent-token" varAgentURL = "agent-url" - varGlobalConfig = "global-config" varHeader = "header" varNoOpen = "no-open" varNoVersionCheck = "no-version-warning" @@ -184,7 +183,7 @@ func Root(subcommands []*cobra.Command) *cobra.Command { _ = cmd.PersistentFlags().MarkHidden(varAgentToken) cliflag.String(cmd.PersistentFlags(), varAgentURL, "", "CODER_AGENT_URL", "", "URL for an agent to access your deployment.") _ = cmd.PersistentFlags().MarkHidden(varAgentURL) - cliflag.String(cmd.PersistentFlags(), varGlobalConfig, "", "CODER_CONFIG_DIR", configdir.LocalConfig("coderv2"), "Path to the global `coder` config directory.") + cliflag.String(cmd.PersistentFlags(), config.FlagName, "", "CODER_CONFIG_DIR", configdir.LocalConfig("coderv2"), "Path to the global `coder` config directory.") cliflag.StringArray(cmd.PersistentFlags(), varHeader, "", "CODER_HEADER", []string{}, "HTTP headers added to all requests. Provide as \"Key=Value\"") cmd.PersistentFlags().Bool(varForceTty, false, "Force the `coder` command to run as if connected to a TTY.") _ = cmd.PersistentFlags().MarkHidden(varForceTty) @@ -362,7 +361,7 @@ func namedWorkspace(cmd *cobra.Command, client *codersdk.Client, identifier stri // createConfig consumes the global configuration flag to produce a config root. func createConfig(cmd *cobra.Command) config.Root { - globalRoot, err := cmd.Flags().GetString(varGlobalConfig) + globalRoot, err := cmd.Flags().GetString(config.FlagName) if err != nil { panic(err) } diff --git a/cli/server.go b/cli/server.go index cd914bf5b87f2..a6fd3795bb1ae 100644 --- a/cli/server.go +++ b/cli/server.go @@ -78,7 +78,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co RunE: func(cmd *cobra.Command, args []string) error { cfg, err := deployment.Config(cmd.Flags(), vip) if err != nil { - return xerrors.Errorf("getting deployment config", err) + return xerrors.Errorf("getting deployment config: %w", err) } printLogo(cmd) logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) diff --git a/coderd/deploymentconfig_test.go b/coderd/deploymentconfig_test.go index 58220c67e3bda..7a3aa16c7aa98 100644 --- a/coderd/deploymentconfig_test.go +++ b/coderd/deploymentconfig_test.go @@ -7,6 +7,7 @@ import ( "github.com/spf13/pflag" "github.com/stretchr/testify/require" + "github.com/coder/coder/cli/config" "github.com/coder/coder/cli/deployment" "github.com/coder/coder/coderd/coderdtest" "github.com/coder/coder/testutil" @@ -19,7 +20,7 @@ func TestDeploymentConfig(t *testing.T) { defer cancel() vip := deployment.NewViper() fs := pflag.NewFlagSet("test", pflag.ContinueOnError) - fs.String("global-config", hi, "usage") + fs.String(config.FlagName, hi, "usage") cfg, err := deployment.Config(fs, vip) require.NoError(t, err) // values should be returned From 63587f622c76fc05a3f9d57439733bc5a459ea86 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 02:30:35 +0000 Subject: [PATCH 32/43] fix ts --- site/src/AppRouter.tsx | 8 +-- site/src/api/api.ts | 6 +- .../DeploySettingsLayout.tsx | 12 ++-- .../DeploySettingsLayout/OptionsTable.tsx | 4 +- site/src/components/Navbar/Navbar.tsx | 2 +- .../DeploySettingsPage/AuthSettingsPage.tsx | 30 ++++----- .../GeneralSettingsPage.tsx | 8 +-- .../NetworkSettingsPage.tsx | 10 +-- .../SecuritySettingsPage.tsx | 14 ++--- site/src/xServices/StateContext.tsx | 6 +- site/src/xServices/auth/authXService.ts | 4 +- .../deploymentFlags/deploymentFlagsMachine.ts | 62 ------------------- 12 files changed, 52 insertions(+), 114 deletions(-) delete mode 100644 site/src/xServices/deploymentFlags/deploymentFlagsMachine.ts diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index c0759aecafdaf..feecf7dd6ab72 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -256,7 +256,7 @@ export const AppRouter: FC = () => { element={ @@ -270,7 +270,7 @@ export const AppRouter: FC = () => { element={ @@ -284,7 +284,7 @@ export const AppRouter: FC = () => { element={ @@ -298,7 +298,7 @@ export const AppRouter: FC = () => { element={ diff --git a/site/src/api/api.ts b/site/src/api/api.ts index ded0d3d3c89ba..8bb9303f97fa2 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -667,9 +667,9 @@ export const getAgentListeningPorts = async ( return response.data } -export const getDeploymentFlags = - async (): Promise => { - const response = await axios.get(`/api/v2/flags/deployment`) +export const getDeploymentConfig = + async (): Promise => { + const response = await axios.get(`/api/v2/config/deployment`) return response.data } diff --git a/site/src/components/DeploySettingsLayout/DeploySettingsLayout.tsx b/site/src/components/DeploySettingsLayout/DeploySettingsLayout.tsx index b6abb27841f83..c53eda63a35bb 100644 --- a/site/src/components/DeploySettingsLayout/DeploySettingsLayout.tsx +++ b/site/src/components/DeploySettingsLayout/DeploySettingsLayout.tsx @@ -11,9 +11,9 @@ import React, { import { useActor } from "@xstate/react" import { XServiceContext } from "xServices/StateContext" import { Loader } from "components/Loader/Loader" -import { DeploymentFlags } from "api/typesGenerated" +import { DeploymentConfig } from "api/typesGenerated" -type DeploySettingsContextValue = { deploymentFlags: DeploymentFlags } +type DeploySettingsContextValue = { deploymentConfig: DeploymentConfig } const DeploySettingsContext = createContext< DeploySettingsContextValue | undefined @@ -33,9 +33,9 @@ export const DeploySettingsLayout: React.FC = ({ children, }) => { const xServices = useContext(XServiceContext) - const [state, send] = useActor(xServices.deploymentFlagsXService) + const [state, send] = useActor(xServices.deploymentConfigXService) const styles = useStyles() - const { deploymentFlags } = state.context + const { deploymentConfig } = state.context useEffect(() => { if (state.matches("idle")) { @@ -48,8 +48,8 @@ export const DeploySettingsLayout: React.FC = ({
- {deploymentFlags ? ( - + {deploymentConfig ? ( + {children} ) : ( diff --git a/site/src/components/DeploySettingsLayout/OptionsTable.tsx b/site/src/components/DeploySettingsLayout/OptionsTable.tsx index d54c614b48949..8b7fbd111d110 100644 --- a/site/src/components/DeploySettingsLayout/OptionsTable.tsx +++ b/site/src/components/DeploySettingsLayout/OptionsTable.tsx @@ -5,7 +5,7 @@ import TableCell from "@material-ui/core/TableCell" import TableContainer from "@material-ui/core/TableContainer" import TableHead from "@material-ui/core/TableHead" import TableRow from "@material-ui/core/TableRow" -import { DeploymentFlags } from "api/typesGenerated" +import { DeploymentConfig } from "api/typesGenerated" import { OptionDescription, OptionName, @@ -13,7 +13,7 @@ import { } from "components/DeploySettingsLayout/Option" import React from "react" -const OptionsTable: React.FC<{ options: Partial }> = ({ +const OptionsTable: React.FC<{ options: Partial }> = ({ options, }) => { const styles = useStyles() diff --git a/site/src/components/Navbar/Navbar.tsx b/site/src/components/Navbar/Navbar.tsx index 95355e5a1dbe7..56d763b4a66f1 100644 --- a/site/src/components/Navbar/Navbar.tsx +++ b/site/src/components/Navbar/Navbar.tsx @@ -22,7 +22,7 @@ export const Navbar: React.FC = () => { featureVisibility[FeatureNames.AuditLog] && Boolean(permissions?.viewAuditLog) const canViewDeployment = - experimental && Boolean(permissions?.viewDeploymentFlags) + experimental && Boolean(permissions?.viewDeploymentConfig) const onSignOut = () => authSend("SIGN_OUT") return ( diff --git a/site/src/pages/DeploySettingsPage/AuthSettingsPage.tsx b/site/src/pages/DeploySettingsPage/AuthSettingsPage.tsx index 008f9f25cbe22..150c0fb006afb 100644 --- a/site/src/pages/DeploySettingsPage/AuthSettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/AuthSettingsPage.tsx @@ -12,7 +12,7 @@ import { Helmet } from "react-helmet-async" import { pageTitle } from "util/page" const AuthSettingsPage: React.FC = () => { - const { deploymentFlags } = useDeploySettings() + const { deploymentConfig: deploymentConfig } = useDeploySettings() return ( <> @@ -32,7 +32,7 @@ const AuthSettingsPage: React.FC = () => { /> - {deploymentFlags.oidc_client_id.value ? ( + {deploymentConfig.oidc_client_id.value ? ( ) : ( @@ -41,12 +41,12 @@ const AuthSettingsPage: React.FC = () => { @@ -60,7 +60,7 @@ const AuthSettingsPage: React.FC = () => { /> - {deploymentFlags.oauth2_github_client_id.value ? ( + {deploymentConfig.oauth2_github_client_id.value ? ( ) : ( @@ -69,17 +69,17 @@ const AuthSettingsPage: React.FC = () => { diff --git a/site/src/pages/DeploySettingsPage/GeneralSettingsPage.tsx b/site/src/pages/DeploySettingsPage/GeneralSettingsPage.tsx index ebf523fb0e5a7..59fd23e7a3308 100644 --- a/site/src/pages/DeploySettingsPage/GeneralSettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/GeneralSettingsPage.tsx @@ -6,7 +6,7 @@ import { Helmet } from "react-helmet-async" import { pageTitle } from "util/page" const GeneralSettingsPage: React.FC = () => { - const { deploymentFlags } = useDeploySettings() + const { deploymentConfig: deploymentConfig } = useDeploySettings() return ( <> @@ -22,9 +22,9 @@ const GeneralSettingsPage: React.FC = () => { diff --git a/site/src/pages/DeploySettingsPage/NetworkSettingsPage.tsx b/site/src/pages/DeploySettingsPage/NetworkSettingsPage.tsx index 7bcf9cdede202..23e87a284e4b2 100644 --- a/site/src/pages/DeploySettingsPage/NetworkSettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/NetworkSettingsPage.tsx @@ -6,7 +6,7 @@ import { Helmet } from "react-helmet-async" import { pageTitle } from "util/page" const NetworkSettingsPage: React.FC = () => { - const { deploymentFlags } = useDeploySettings() + const { deploymentConfig: deploymentConfig } = useDeploySettings() return ( <> @@ -22,10 +22,10 @@ const NetworkSettingsPage: React.FC = () => { diff --git a/site/src/pages/DeploySettingsPage/SecuritySettingsPage.tsx b/site/src/pages/DeploySettingsPage/SecuritySettingsPage.tsx index 987c61b93c85f..0a7e3afdccae9 100644 --- a/site/src/pages/DeploySettingsPage/SecuritySettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/SecuritySettingsPage.tsx @@ -16,7 +16,7 @@ import { pageTitle } from "util/page" import { XServiceContext } from "xServices/StateContext" const SecuritySettingsPage: React.FC = () => { - const { deploymentFlags } = useDeploySettings() + const { deploymentConfig: deploymentConfig } = useDeploySettings() const xServices = useContext(XServiceContext) const [entitlementsState] = useActor(xServices.entitlementsXService) @@ -34,8 +34,8 @@ const SecuritySettingsPage: React.FC = () => { @@ -89,10 +89,10 @@ const SecuritySettingsPage: React.FC = () => { diff --git a/site/src/xServices/StateContext.tsx b/site/src/xServices/StateContext.tsx index 565ca3023a5b9..8df5003aff85e 100644 --- a/site/src/xServices/StateContext.tsx +++ b/site/src/xServices/StateContext.tsx @@ -3,7 +3,7 @@ import { createContext, FC, ReactNode } from "react" import { ActorRefFrom } from "xstate" import { authMachine } from "./auth/authXService" import { buildInfoMachine } from "./buildInfo/buildInfoXService" -import { deploymentFlagsMachine } from "./deploymentFlags/deploymentFlagsMachine" +import { deploymentConfigMachine } from "./deploymentConfig/deploymentConfigMachine" import { entitlementsMachine } from "./entitlements/entitlementsXService" import { siteRolesMachine } from "./roles/siteRolesXService" @@ -13,7 +13,7 @@ interface XServiceContextType { entitlementsXService: ActorRefFrom siteRolesXService: ActorRefFrom // Since the info here is used by multiple deployment settings page and we don't want to refetch them every time - deploymentFlagsXService: ActorRefFrom + deploymentConfigXService: ActorRefFrom } /** @@ -34,7 +34,7 @@ export const XServiceProvider: FC<{ children: ReactNode }> = ({ children }) => { buildInfoXService: useInterpret(buildInfoMachine), entitlementsXService: useInterpret(entitlementsMachine), siteRolesXService: useInterpret(siteRolesMachine), - deploymentFlagsXService: useInterpret(deploymentFlagsMachine), + deploymentConfigXService: useInterpret(deploymentConfigMachine), }} > {children} diff --git a/site/src/xServices/auth/authXService.ts b/site/src/xServices/auth/authXService.ts index af25518f8bcaa..79439dbc298a4 100644 --- a/site/src/xServices/auth/authXService.ts +++ b/site/src/xServices/auth/authXService.ts @@ -16,7 +16,7 @@ export const checks = { createTemplates: "createTemplates", deleteTemplates: "deleteTemplates", viewAuditLog: "viewAuditLog", - viewDeploymentFlags: "viewDeploymentFlags", + viewDeploymentConfig: "viewDeploymentConfig", createGroup: "createGroup", } as const @@ -57,7 +57,7 @@ export const permissionsToCheck = { }, action: "read", }, - [checks.viewDeploymentFlags]: { + [checks.viewDeploymentConfig]: { object: { resource_type: "deployment_flags", }, diff --git a/site/src/xServices/deploymentFlags/deploymentFlagsMachine.ts b/site/src/xServices/deploymentFlags/deploymentFlagsMachine.ts deleted file mode 100644 index aa18e9d179ce1..0000000000000 --- a/site/src/xServices/deploymentFlags/deploymentFlagsMachine.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { getDeploymentFlags } from "api/api" -import { DeploymentFlags } from "api/typesGenerated" -import { createMachine, assign } from "xstate" - -export const deploymentFlagsMachine = createMachine( - { - id: "deploymentFlagsMachine", - predictableActionArguments: true, - initial: "idle", - schema: { - context: {} as { - deploymentFlags?: DeploymentFlags - getDeploymentFlagsError?: unknown - }, - events: {} as { type: "LOAD" }, - services: {} as { - getDeploymentFlags: { - data: DeploymentFlags - } - }, - }, - tsTypes: {} as import("./deploymentFlagsMachine.typegen").Typegen0, - states: { - idle: { - on: { - LOAD: { - target: "loading", - }, - }, - }, - loading: { - invoke: { - src: "getDeploymentFlags", - onDone: { - target: "loaded", - actions: ["assignDeploymentFlags"], - }, - onError: { - target: "idle", - actions: ["assignGetDeploymentFlagsError"], - }, - }, - }, - loaded: { - type: "final", - }, - }, - }, - { - services: { - getDeploymentFlags, - }, - actions: { - assignDeploymentFlags: assign({ - deploymentFlags: (_, { data }) => data, - }), - assignGetDeploymentFlagsError: assign({ - getDeploymentFlagsError: (_, { data }) => data, - }), - }, - }, -) From d9473632b8dd8d95efe971cd54372dd50c04e9c8 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 02:30:43 +0000 Subject: [PATCH 33/43] fix ts --- .../deploymentConfigMachine.ts | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 site/src/xServices/deploymentConfig/deploymentConfigMachine.ts diff --git a/site/src/xServices/deploymentConfig/deploymentConfigMachine.ts b/site/src/xServices/deploymentConfig/deploymentConfigMachine.ts new file mode 100644 index 0000000000000..d5fd0f18bdc3b --- /dev/null +++ b/site/src/xServices/deploymentConfig/deploymentConfigMachine.ts @@ -0,0 +1,62 @@ +import { getDeploymentConfig } from "api/api" +import { DeploymentConfig } from "api/typesGenerated" +import { createMachine, assign } from "xstate" + +export const deploymentConfigMachine = createMachine( + { + id: "deploymentConfigMachine", + predictableActionArguments: true, + initial: "idle", + schema: { + context: {} as { + deploymentConfig?: DeploymentConfig + getDeploymentConfigError?: unknown + }, + events: {} as { type: "LOAD" }, + services: {} as { + getDeploymentConfig: { + data: DeploymentConfig + } + }, + }, + tsTypes: {} as import("./deploymentConfigMachine.typegen").Typegen0, + states: { + idle: { + on: { + LOAD: { + target: "loading", + }, + }, + }, + loading: { + invoke: { + src: "getDeploymentConfig", + onDone: { + target: "loaded", + actions: ["assignDeploymentConfig"], + }, + onError: { + target: "idle", + actions: ["assignGetDeploymentConfigError"], + }, + }, + }, + loaded: { + type: "final", + }, + }, + }, + { + services: { + getDeploymentConfig: getDeploymentConfig, + }, + actions: { + assignDeploymentConfig: assign({ + deploymentConfig: (_, { data }) => data, + }), + assignGetDeploymentConfigError: assign({ + getDeploymentConfigError: (_, { data }) => data, + }), + }, + }, +) From 9520dafc30aec352b188a1ee772f2dfddcdc504b Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 02:37:04 +0000 Subject: [PATCH 34/43] fix merge --- cli/server.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/server.go b/cli/server.go index a6fd3795bb1ae..ee3cbf41e6fda 100644 --- a/cli/server.go +++ b/cli/server.go @@ -214,22 +214,22 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co // If the access URL is empty, we attempt to run a reverse-proxy // tunnel to make the initial setup really simple. - if dflags.AccessURL.Value == "" { + if cfg.AccessURL.Value == "" { cmd.Printf("Opening tunnel so workspaces can connect to your deployment. For production scenarios, specify an external access URL\n") tunnel, tunnelErr, err = devtunnel.New(ctxTunnel, logger.Named("devtunnel")) if err != nil { return xerrors.Errorf("create tunnel: %w", err) } - dflags.AccessURL.Value = tunnel.URL + cfg.AccessURL.Value = tunnel.URL - if dflags.WildcardAccessURL.Value == "" { + if cfg.WildcardAccessURL.Value == "" { u, err := parseURL(ctx, tunnel.URL) if err != nil { return xerrors.Errorf("parse tunnel url: %w", err) } // Suffixed wildcard access URL. - dflags.WildcardAccessURL.Value = fmt.Sprintf("*--%s", u.Hostname()) + cfg.WildcardAccessURL.Value = fmt.Sprintf("*--%s", u.Hostname()) } } From 555aaacd30f62748101ed7f6efe6ee39ae172f15 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 02:39:33 +0000 Subject: [PATCH 35/43] make fmt --- .../components/DeploySettingsLayout/DeploySettingsLayout.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/site/src/components/DeploySettingsLayout/DeploySettingsLayout.tsx b/site/src/components/DeploySettingsLayout/DeploySettingsLayout.tsx index c53eda63a35bb..f9294d4108f7d 100644 --- a/site/src/components/DeploySettingsLayout/DeploySettingsLayout.tsx +++ b/site/src/components/DeploySettingsLayout/DeploySettingsLayout.tsx @@ -49,7 +49,9 @@ export const DeploySettingsLayout: React.FC = ({
{deploymentConfig ? ( - + {children} ) : ( From ef682b96ccbbb8f324130ce6c6d8cd5a6e95b61a Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 02:50:00 +0000 Subject: [PATCH 36/43] remove verbose from flag set --- site/src/api/typesGenerated.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 6fa61d9db2836..92c915317647d 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -297,7 +297,6 @@ export interface DeploymentConfig { readonly auto_import_templates: DeploymentConfigField readonly metrics_cache_refresh_interval: DeploymentConfigField readonly agent_stat_refresh_interval: DeploymentConfigField - readonly verbose: DeploymentConfigField readonly audit_logging: DeploymentConfigField readonly browser_only: DeploymentConfigField readonly user_workspace_quota: DeploymentConfigField From ee1ac0b84122e8b1d500166965364fc80d61a9fa Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 15:19:12 +0000 Subject: [PATCH 37/43] add json flags --- codersdk/deploymentconfig.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/codersdk/deploymentconfig.go b/codersdk/deploymentconfig.go index b46adb117ee07..1163830a8d983 100644 --- a/codersdk/deploymentconfig.go +++ b/codersdk/deploymentconfig.go @@ -70,14 +70,14 @@ type Flaggable interface { } type DeploymentConfigField[T Flaggable] struct { - Key string - Name string - Usage string - Flag string - Shorthand string - Enterprise bool - Hidden bool - Value T + Key string `json:"key"` + Name string `json:"name"` + Usage string `json:"usage"` + Flag string `json:"flag"` + Shorthand string `json:"shorthand"` + Enterprise bool `json:"enterprise"` + Hidden bool `json:"hidden"` + Value T `json:"value"` } // DeploymentConfig returns the deployment config for the coder server. From 301e4e737849201ecfe0a357120b1933c9f842fc Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 15:23:14 +0000 Subject: [PATCH 38/43] remove server.yaml --- server.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 server.yaml diff --git a/server.yaml b/server.yaml deleted file mode 100644 index e69de29bb2d1d..0000000000000 From 8e2e9009167d0c13644d16a509e67e58f44c4815 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 17:18:46 +0000 Subject: [PATCH 39/43] fix ts --- site/src/api/typesGenerated.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 92c915317647d..676c4e0be950f 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -304,14 +304,14 @@ export interface DeploymentConfig { // From codersdk/deploymentconfig.go export interface DeploymentConfigField { - readonly Key: string - readonly Name: string - readonly Usage: string - readonly Flag: string - readonly Shorthand: string - readonly Enterprise: boolean - readonly Hidden: boolean - readonly Value: T + readonly key: string + readonly name: string + readonly usage: string + readonly flag: string + readonly shorthand: string + readonly enterprise: boolean + readonly hidden: boolean + readonly value: T } // From codersdk/features.go From 0d548ede47eb2f6d8e614a844043308704469954 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 18:59:50 +0000 Subject: [PATCH 40/43] ensure as much parity as possible --- cli/deployment/config.go | 30 +++++++++++++++--------------- cli/server.go | 8 ++++---- coderd/deploymentconfig_test.go | 4 ++-- codersdk/deploymentconfig.go | 8 ++++---- enterprise/cli/server.go | 2 +- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 42634b2ca6b6c..a6cbeae0691ee 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -113,8 +113,8 @@ func newConfig() codersdk.DeploymentConfig { Flag: "pprof-address", Value: "127.0.0.1:6060", }, - CacheDir: codersdk.DeploymentConfigField[string]{ - Key: "cache_dir", + CacheDirectory: codersdk.DeploymentConfigField[string]{ + Key: "cache_directory", Usage: "The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd.", Flag: "cache-dir", Value: defaultCacheDir(), @@ -125,44 +125,44 @@ func newConfig() codersdk.DeploymentConfig { Flag: "in-memory", Hidden: true, }, - ProvisionerDaemonCount: codersdk.DeploymentConfigField[int]{ - Key: "provisioner.daemon_count", + ProvisionerDaemons: codersdk.DeploymentConfigField[int]{ + Key: "provisioner.daemons", Usage: "Number of provisioner daemons to create on start. If builds are stuck in queued state for a long time, consider increasing this.", Flag: "provisioner-daemons", Value: 3, }, PostgresURL: codersdk.DeploymentConfigField[string]{ - Key: "postgres_url", + Key: "pg_connection_url", Usage: "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\".", Flag: "postgres-url", }, OAuth2GithubClientID: codersdk.DeploymentConfigField[string]{ - Key: "oauth2github.client_id", + Key: "oauth2.github.client_id", Usage: "Client ID for Login with GitHub.", Flag: "oauth2-github-client-id", }, OAuth2GithubClientSecret: codersdk.DeploymentConfigField[string]{ - Key: "oauth2github.client_secret", + Key: "oauth2.github.client_secret", Usage: "Client secret for Login with GitHub.", Flag: "oauth2-github-client-secret", }, OAuth2GithubAllowedOrganizations: codersdk.DeploymentConfigField[[]string]{ - Key: "oauth2github.allowed_organizations", + Key: "oauth2.github.allowed_organizations", Usage: "Organizations the user must be a member of to Login with GitHub.", Flag: "oauth2-github-allowed-orgs", }, OAuth2GithubAllowedTeams: codersdk.DeploymentConfigField[[]string]{ - Key: "oauth2github.allowed_teams", + Key: "oauth2.github.allowed_teams", Usage: "Teams inside organizations the user must be a member of to Login with GitHub. Structured as: /.", Flag: "oauth2-github-allowed-teams", }, OAuth2GithubAllowSignups: codersdk.DeploymentConfigField[bool]{ - Key: "oauth2github.allow_signups", + Key: "oauth2.github.allow_signups", Usage: "Whether new users can sign up with GitHub.", Flag: "oauth2-github-allow-signups", }, OAuth2GithubEnterpriseBaseURL: codersdk.DeploymentConfigField[string]{ - Key: "oauth2github.enterprise_base_url", + Key: "oauth2.github.enterprise_base_url", Usage: "Base URL of a GitHub Enterprise deployment to use for Login with GitHub.", Flag: "oauth2-github-enterprise-base-url", }, @@ -204,8 +204,8 @@ func newConfig() codersdk.DeploymentConfig { Flag: "telemetry", Value: flag.Lookup("test.v") == nil, }, - TelemetryTraceEnable: codersdk.DeploymentConfigField[bool]{ - Key: "telemetry.trace.enable", + TelemetryTrace: codersdk.DeploymentConfigField[bool]{ + Key: "telemetry.trace", Usage: "Whether Opentelemetry traces are sent to Coder. Coder collects anonymized application tracing to help improve our product. Disabling telemetry also disables this option.", Flag: "telemetry-trace", Value: flag.Lookup("test.v") == nil, @@ -298,8 +298,8 @@ func newConfig() codersdk.DeploymentConfig { Flag: "browser-only", Enterprise: true, }, - SCIMAuthHeader: codersdk.DeploymentConfigField[string]{ - Key: "scim_auth_header", + SCIMAPIKey: codersdk.DeploymentConfigField[string]{ + Key: "scim_api_key", Usage: "Enables SCIM and sets the authentication header for the built-in SCIM server. New users are automatically created with OIDC authentication.", Flag: "scim-auth-header", Enterprise: true, diff --git a/cli/server.go b/cli/server.go index ee3cbf41e6fda..8f3ce62921c80 100644 --- a/cli/server.go +++ b/cli/server.go @@ -120,7 +120,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co // Only override if telemetryTraceEnable was specifically set. // By default we want it to be controlled by telemetryEnable. if cmd.Flags().Changed("telemetry-trace") { - shouldCoderTrace = cfg.TelemetryTraceEnable.Value + shouldCoderTrace = cfg.TelemetryTrace.Value } if cfg.TraceEnable.Value || shouldCoderTrace { @@ -333,7 +333,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co Database: databasefake.New(), DERPMap: derpMap, Pubsub: database.NewPubsubInMemory(), - CacheDir: cfg.CacheDir.Value, + CacheDir: cfg.CacheDirectory.Value, GoogleTokenValidator: googleTokenValidator, SecureAuthCookie: cfg.SecureAuthCookie.Value, SSHKeygenAlgorithm: sshKeygenAlgorithm, @@ -541,8 +541,8 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co _ = daemon.Close() } }() - for i := 0; i < cfg.ProvisionerDaemonCount.Value; i++ { - daemon, err := newProvisionerDaemon(ctx, coderAPI, logger, cfg.CacheDir.Value, errCh, false) + for i := 0; i < cfg.ProvisionerDaemons.Value; i++ { + daemon, err := newProvisionerDaemon(ctx, coderAPI, logger, cfg.CacheDirectory.Value, errCh, false) if err != nil { return xerrors.Errorf("create provisioner daemon: %w", err) } diff --git a/coderd/deploymentconfig_test.go b/coderd/deploymentconfig_test.go index 7a3aa16c7aa98..2f03e655c671d 100644 --- a/coderd/deploymentconfig_test.go +++ b/coderd/deploymentconfig_test.go @@ -29,7 +29,7 @@ func TestDeploymentConfig(t *testing.T) { cfg.OAuth2GithubClientSecret.Value = hi cfg.OIDCClientSecret.Value = hi cfg.PostgresURL.Value = hi - cfg.SCIMAuthHeader.Value = hi + cfg.SCIMAPIKey.Value = hi client := coderdtest.New(t, &coderdtest.Options{ DeploymentConfig: &cfg, @@ -43,5 +43,5 @@ func TestDeploymentConfig(t *testing.T) { require.Empty(t, scrubbed.OAuth2GithubClientSecret.Value) require.Empty(t, scrubbed.OIDCClientSecret.Value) require.Empty(t, scrubbed.PostgresURL.Value) - require.Empty(t, scrubbed.SCIMAuthHeader.Value) + require.Empty(t, scrubbed.SCIMAPIKey.Value) } diff --git a/codersdk/deploymentconfig.go b/codersdk/deploymentconfig.go index 1163830a8d983..174152e4a29f2 100644 --- a/codersdk/deploymentconfig.go +++ b/codersdk/deploymentconfig.go @@ -28,9 +28,9 @@ type DeploymentConfig struct { PrometheusAddress DeploymentConfigField[string] `json:"prometheus_address"` PprofEnable DeploymentConfigField[bool] `json:"pprof_enabled"` PprofAddress DeploymentConfigField[string] `json:"pprof_address"` - CacheDir DeploymentConfigField[string] `json:"cache_dir"` + CacheDirectory DeploymentConfigField[string] `json:"cache_directory"` InMemoryDatabase DeploymentConfigField[bool] `json:"in_memory_database"` - ProvisionerDaemonCount DeploymentConfigField[int] `json:"provisioner_daemon_count"` + ProvisionerDaemons DeploymentConfigField[int] `json:"provisioner_daemon_count"` PostgresURL DeploymentConfigField[string] `json:"-"` OAuth2GithubClientID DeploymentConfigField[string] `json:"oauth2_github_client_id"` OAuth2GithubClientSecret DeploymentConfigField[string] `json:"-"` @@ -45,7 +45,7 @@ type DeploymentConfig struct { OIDCIssuerURL DeploymentConfigField[string] `json:"oidc_issuer_url"` OIDCScopes DeploymentConfigField[[]string] `json:"oidc_scopes"` TelemetryEnable DeploymentConfigField[bool] `json:"telemetry_enable"` - TelemetryTraceEnable DeploymentConfigField[bool] `json:"telemetry_trace_enable"` + TelemetryTrace DeploymentConfigField[bool] `json:"telemetry_trace_enable"` TelemetryURL DeploymentConfigField[string] `json:"telemetry_url"` TLSEnable DeploymentConfigField[bool] `json:"tls_enable"` TLSCertFiles DeploymentConfigField[[]string] `json:"tls_cert_files"` @@ -61,7 +61,7 @@ type DeploymentConfig struct { AgentStatRefreshInterval DeploymentConfigField[time.Duration] `json:"agent_stat_refresh_interval"` AuditLogging DeploymentConfigField[bool] `json:"audit_logging"` BrowserOnly DeploymentConfigField[bool] `json:"browser_only"` - SCIMAuthHeader DeploymentConfigField[string] `json:"-"` + SCIMAPIKey DeploymentConfigField[string] `json:"-"` UserWorkspaceQuota DeploymentConfigField[int] `json:"user_workspace_quota"` } diff --git a/enterprise/cli/server.go b/enterprise/cli/server.go index 04bbbe037907b..cd04a3be93972 100644 --- a/enterprise/cli/server.go +++ b/enterprise/cli/server.go @@ -60,7 +60,7 @@ func server() *cobra.Command { o := &coderd.Options{ AuditLogging: options.DeploymentConfig.AuditLogging.Value, BrowserOnly: options.DeploymentConfig.BrowserOnly.Value, - SCIMAPIKey: []byte(options.DeploymentConfig.SCIMAuthHeader.Value), + SCIMAPIKey: []byte(options.DeploymentConfig.SCIMAPIKey.Value), UserWorkspaceQuota: options.DeploymentConfig.UserWorkspaceQuota.Value, RBAC: true, DERPServerRelayAddress: options.DeploymentConfig.DERPServerRelayAddress.Value, From c85657b4ee2b8e3f70e63a5a68fb99d053388fda Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 19:03:28 +0000 Subject: [PATCH 41/43] revert tls breaking changes --- cli/deployment/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index a6cbeae0691ee..42a875496b090 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -223,7 +223,7 @@ func newConfig() codersdk.DeploymentConfig { Flag: "tls-enable", }, TLSCertFiles: codersdk.DeploymentConfigField[[]string]{ - Key: "tls.cert_files", + Key: "tls.cert_file", Usage: "Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file.", Flag: "tls-cert-file", }, @@ -239,7 +239,7 @@ func newConfig() codersdk.DeploymentConfig { Value: "request", }, TLSKeyFiles: codersdk.DeploymentConfigField[[]string]{ - Key: "tls.key_files", + Key: "tls.key_file", Usage: "Paths to the private keys for each of the certificates. It requires a PEM-encoded file.", Flag: "tls-key-file", }, From 903db7f6e71ff1216097071b7a742fc35c6579b3 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 19:05:33 +0000 Subject: [PATCH 42/43] make gen --- site/src/api/typesGenerated.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 676c4e0be950f..0729f24e784f8 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -269,7 +269,7 @@ export interface DeploymentConfig { readonly prometheus_address: DeploymentConfigField readonly pprof_enabled: DeploymentConfigField readonly pprof_address: DeploymentConfigField - readonly cache_dir: DeploymentConfigField + readonly cache_directory: DeploymentConfigField readonly in_memory_database: DeploymentConfigField readonly provisioner_daemon_count: DeploymentConfigField readonly oauth2_github_client_id: DeploymentConfigField From 8fae06a10f61d3b32e59e00765c82d89f405955e Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 21 Oct 2022 19:17:15 +0000 Subject: [PATCH 43/43] fix docs --- docs/install/kubernetes.md | 2 +- helm/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/install/kubernetes.md b/docs/install/kubernetes.md index ef5d5fb9e90d2..81958e964878c 100644 --- a/docs/install/kubernetes.md +++ b/docs/install/kubernetes.md @@ -102,7 +102,7 @@ to log in and manage templates. # This env variable controls whether or not to auto-import the # "kubernetes" template on first startup. This will not work unless # coder.serviceAccount.workspacePerms is true. - - name: CODER_TEMPLATE_AUTOIMPORT + - name: CODER_AUTO_IMPORT_TEMPLATES value: "kubernetes" #tls: diff --git a/helm/README.md b/helm/README.md index 0fa51303c71a4..95b4ecffe3ed8 100644 --- a/helm/README.md +++ b/helm/README.md @@ -47,10 +47,10 @@ coder: # This env variable controls whether or not to auto-import the "kubernetes" # template on first startup. This will not work unless # coder.serviceAccount.workspacePerms is true. - - name: CODER_TEMPLATE_AUTOIMPORT + - name: CODER_AUTO_IMPORT_TEMPLATES value: "kubernetes" tls: - secretNames: + secretNames: - my-tls-secret-name ```