diff --git a/cli/deployment/config.go b/cli/deployment/config.go index de2a9c1c7049e..6f606f6d3d023 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -19,356 +19,328 @@ import ( "github.com/coder/coder/codersdk" ) -func newConfig() codersdk.DeploymentConfig { - return codersdk.DeploymentConfig{ - AccessURL: codersdk.DeploymentConfigField[string]{ +func newConfig() *codersdk.DeploymentConfig { + return &codersdk.DeploymentConfig{ + AccessURL: &codersdk.DeploymentConfigField[string]{ Name: "Access URL", - 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]{ + WildcardAccessURL: &codersdk.DeploymentConfigField[string]{ Name: "Wildcard Access URL", - 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]{ + Address: &codersdk.DeploymentConfigField[string]{ Name: "Address", - Key: "address", Usage: "Bind address of the server.", Flag: "address", Shorthand: "a", - Value: "127.0.0.1:3000", - }, - AutobuildPollInterval: codersdk.DeploymentConfigField[time.Duration]{ - Name: "Autobuild Poll Interval", - 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]{ - Name: "DERP Server Enable", - 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]{ - Name: "DERP Server Region ID", - 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]{ - Name: "DERP Server Region Code", - 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]{ - Name: "DERP Server Region Name", - 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]{ - Name: "DERP Server STUN Addresses", - 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"}, - }, - DERPServerRelayURL: codersdk.DeploymentConfigField[string]{ - Name: "DERP Server Relay URL", - Key: "derp.server.relay_url", - Usage: "An HTTP URL that is accessible by other replicas to relay DERP traffic. Required for high availability.", - Flag: "derp-server-relay-url", - Enterprise: true, - }, - DERPConfigURL: codersdk.DeploymentConfigField[string]{ - Name: "DERP Config URL", - 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]{ - Name: "DERP Config Path", - 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]{ - Name: "Prometheus Enable", - Key: "prometheus.enable", - Usage: "Serve prometheus metrics on the address defined by prometheus address.", - Flag: "prometheus-enable", - }, - PrometheusAddress: codersdk.DeploymentConfigField[string]{ - Name: "Prometheus Address", - Key: "prometheus.address", - Usage: "The bind address to serve prometheus metrics.", - Flag: "prometheus-address", - Value: "127.0.0.1:2112", - }, - PprofEnable: codersdk.DeploymentConfigField[bool]{ - Name: "Pprof Enable", - Key: "pprof.enable", - Usage: "Serve pprof metrics on the address defined by pprof address.", - Flag: "pprof-enable", - }, - PprofAddress: codersdk.DeploymentConfigField[string]{ - Name: "Pprof Address", - Key: "pprof.address", - Usage: "The bind address to serve pprof.", - Flag: "pprof-address", - Value: "127.0.0.1:6060", - }, - ProxyTrustedHeaders: codersdk.DeploymentConfigField[[]string]{ + Default: "127.0.0.1:3000", + }, + AutobuildPollInterval: &codersdk.DeploymentConfigField[time.Duration]{ + Name: "Autobuild Poll Interval", + Usage: "Interval to poll for scheduled workspace builds.", + Flag: "autobuild-poll-interval", + Hidden: true, + Default: time.Minute, + }, + DERP: &codersdk.DERP{ + Server: &codersdk.DERPServerConfig{ + Enable: &codersdk.DeploymentConfigField[bool]{ + Name: "DERP Server Enable", + Usage: "Whether to enable or disable the embedded DERP relay server.", + Flag: "derp-server-enable", + Default: true, + }, + RegionID: &codersdk.DeploymentConfigField[int]{ + Name: "DERP Server Region ID", + Usage: "Region ID to use for the embedded DERP server.", + Flag: "derp-server-region-id", + Default: 999, + }, + RegionCode: &codersdk.DeploymentConfigField[string]{ + Name: "DERP Server Region Code", + Usage: "Region code to use for the embedded DERP server.", + Flag: "derp-server-region-code", + Default: "coder", + }, + RegionName: &codersdk.DeploymentConfigField[string]{ + Name: "DERP Server Region Name", + Usage: "Region name that for the embedded DERP server.", + Flag: "derp-server-region-name", + Default: "Coder Embedded Relay", + }, + STUNAddresses: &codersdk.DeploymentConfigField[[]string]{ + Name: "DERP Server STUN Addresses", + 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"}, + }, + RelayURL: &codersdk.DeploymentConfigField[string]{ + Name: "DERP Server Relay URL", + Usage: "An HTTP URL that is accessible by other replicas to relay DERP traffic. Required for high availability.", + Flag: "derp-server-relay-url", + Enterprise: true, + }, + }, + Config: &codersdk.DERPConfig{ + URL: &codersdk.DeploymentConfigField[string]{ + Name: "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", + }, + Path: &codersdk.DeploymentConfigField[string]{ + Name: "DERP Config Path", + Usage: "Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/", + Flag: "derp-config-path", + }, + }, + }, + Prometheus: &codersdk.PrometheusConfig{ + Enable: &codersdk.DeploymentConfigField[bool]{ + Name: "Prometheus Enable", + Usage: "Serve prometheus metrics on the address defined by prometheus address.", + Flag: "prometheus-enable", + }, + Address: &codersdk.DeploymentConfigField[string]{ + Name: "Prometheus Address", + Usage: "The bind address to serve prometheus metrics.", + Flag: "prometheus-address", + Default: "127.0.0.1:2112", + }, + }, + Pprof: &codersdk.PprofConfig{ + Enable: &codersdk.DeploymentConfigField[bool]{ + Name: "Pprof Enable", + Usage: "Serve pprof metrics on the address defined by pprof address.", + Flag: "pprof-enable", + }, + Address: &codersdk.DeploymentConfigField[string]{ + Name: "Pprof Address", + Usage: "The bind address to serve pprof.", + Flag: "pprof-address", + Default: "127.0.0.1:6060", + }, + }, + ProxyTrustedHeaders: &codersdk.DeploymentConfigField[[]string]{ Name: "Proxy Trusted Headers", - Key: "proxy.trusted_headers", Flag: "proxy-trusted-headers", Usage: "Headers to trust for forwarding IP addresses. e.g. Cf-Connecting-IP True-Client-Ip, X-Forwarded-for", }, - ProxyTrustedOrigins: codersdk.DeploymentConfigField[[]string]{ + ProxyTrustedOrigins: &codersdk.DeploymentConfigField[[]string]{ Name: "Proxy Trusted Origins", - Key: "proxy.trusted_origins", Flag: "proxy-trusted-origins", Usage: "Origin addresses to respect \"proxy-trusted-headers\". e.g. example.com", }, - CacheDirectory: codersdk.DeploymentConfigField[string]{ - Name: "Cache Directory", - 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(), + CacheDirectory: &codersdk.DeploymentConfigField[string]{ + Name: "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", + Default: defaultCacheDir(), }, - InMemoryDatabase: codersdk.DeploymentConfigField[bool]{ + InMemoryDatabase: &codersdk.DeploymentConfigField[bool]{ Name: "In Memory Database", - Key: "in_memory_database", Usage: "Controls whether data will be stored in an in-memory database.", Flag: "in-memory", Hidden: true, }, - ProvisionerDaemons: codersdk.DeploymentConfigField[int]{ - Name: "Provisioner Daemons", - 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]{ - Name: "Postgres Connection 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]{ - Name: "OAuth2 GitHub Client ID", - Key: "oauth2.github.client_id", - Usage: "Client ID for Login with GitHub.", - Flag: "oauth2-github-client-id", - }, - OAuth2GithubClientSecret: codersdk.DeploymentConfigField[string]{ - Name: "OAuth2 GitHub Client Secret", - Key: "oauth2.github.client_secret", - Usage: "Client secret for Login with GitHub.", - Flag: "oauth2-github-client-secret", - }, - OAuth2GithubAllowedOrgs: codersdk.DeploymentConfigField[[]string]{ - Name: "OAuth2 GitHub Allowed Orgs", - Key: "oauth2.github.allowed_orgs", - Usage: "Organizations the user must be a member of to Login with GitHub.", - Flag: "oauth2-github-allowed-orgs", - }, - OAuth2GithubAllowedTeams: codersdk.DeploymentConfigField[[]string]{ - Name: "OAuth2 GitHub 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]{ - Name: "OAuth2 GitHub 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]{ - Name: "OAuth2 GitHub 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", - }, - OIDCAllowSignups: codersdk.DeploymentConfigField[bool]{ - Name: "OIDC Allow Signups", - Key: "oidc.allow_signups", - Usage: "Whether new users can sign up with OIDC.", - Flag: "oidc-allow-signups", - Value: true, - }, - OIDCClientID: codersdk.DeploymentConfigField[string]{ - Name: "OIDC Client ID", - Key: "oidc.client_id", - Usage: "Client ID to use for Login with OIDC.", - Flag: "oidc-client-id", - }, - OIDCClientSecret: codersdk.DeploymentConfigField[string]{ - Name: "OIDC Client Secret", - Key: "oidc.client_secret", - Usage: "Client secret to use for Login with OIDC.", - Flag: "oidc-client-secret", - }, - OIDCEmailDomain: codersdk.DeploymentConfigField[string]{ - Name: "OIDC Email Domain", - Key: "oidc.email_domain", - Usage: "Email domain that clients logging in with OIDC must match.", - Flag: "oidc-email-domain", - }, - OIDCIssuerURL: codersdk.DeploymentConfigField[string]{ - Name: "OIDC Issuer URL", - Key: "oidc.issuer_url", - Usage: "Issuer URL to use for Login with OIDC.", - Flag: "oidc-issuer-url", - }, - OIDCScopes: codersdk.DeploymentConfigField[[]string]{ - Name: "OIDC Scopes", - Key: "oidc.scopes", - Usage: "Scopes to grant when authenticating with OIDC.", - Flag: "oidc-scopes", - Value: []string{oidc.ScopeOpenID, "profile", "email"}, - }, - TelemetryEnable: codersdk.DeploymentConfigField[bool]{ - Name: "Telemetry Enable", - 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, - }, - TelemetryTrace: codersdk.DeploymentConfigField[bool]{ - Name: "Telemetry Trace", - 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, - }, - TelemetryURL: codersdk.DeploymentConfigField[string]{ - Name: "Telemetry URL", - Key: "telemetry.url", - Usage: "URL to send telemetry.", - Flag: "telemetry-url", - Hidden: true, - Value: "https://telemetry.coder.com", - }, - TLSEnable: codersdk.DeploymentConfigField[bool]{ - Name: "TLS Enable", - Key: "tls.enable", - Usage: "Whether TLS will be enabled.", - Flag: "tls-enable", - }, - TLSCertFiles: codersdk.DeploymentConfigField[[]string]{ - Name: "TLS Certificate 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", + ProvisionerDaemons: &codersdk.DeploymentConfigField[int]{ + Name: "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", + Default: 3, + }, + PostgresURL: &codersdk.DeploymentConfigField[string]{ + Name: "Postgres 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", + Secret: true, + }, + OAuth2: &codersdk.OAuth2Config{ + Github: &codersdk.OAuth2GithubConfig{ + ClientID: &codersdk.DeploymentConfigField[string]{ + Name: "OAuth2 GitHub Client ID", + Usage: "Client ID for Login with GitHub.", + Flag: "oauth2-github-client-id", + }, + ClientSecret: &codersdk.DeploymentConfigField[string]{ + Name: "OAuth2 GitHub Client Secret", + Usage: "Client secret for Login with GitHub.", + Flag: "oauth2-github-client-secret", + Secret: true, + }, + AllowedOrgs: &codersdk.DeploymentConfigField[[]string]{ + Name: "OAuth2 GitHub Allowed Orgs", + Usage: "Organizations the user must be a member of to Login with GitHub.", + Flag: "oauth2-github-allowed-orgs", + }, + AllowedTeams: &codersdk.DeploymentConfigField[[]string]{ + Name: "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", + }, + AllowSignups: &codersdk.DeploymentConfigField[bool]{ + Name: "OAuth2 GitHub Allow Signups", + Usage: "Whether new users can sign up with GitHub.", + Flag: "oauth2-github-allow-signups", + }, + EnterpriseBaseURL: &codersdk.DeploymentConfigField[string]{ + Name: "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", + }, + }, + }, + OIDC: &codersdk.OIDCConfig{ + AllowSignups: &codersdk.DeploymentConfigField[bool]{ + Name: "OIDC Allow Signups", + Usage: "Whether new users can sign up with OIDC.", + Flag: "oidc-allow-signups", + Default: true, + }, + ClientID: &codersdk.DeploymentConfigField[string]{ + Name: "OIDC Client ID", + Usage: "Client ID to use for Login with OIDC.", + Flag: "oidc-client-id", + }, + ClientSecret: &codersdk.DeploymentConfigField[string]{ + Name: "OIDC Client Secret", + Usage: "Client secret to use for Login with OIDC.", + Flag: "oidc-client-secret", + Secret: true, + }, + EmailDomain: &codersdk.DeploymentConfigField[string]{ + Name: "OIDC Email Domain", + Usage: "Email domain that clients logging in with OIDC must match.", + Flag: "oidc-email-domain", + }, + IssuerURL: &codersdk.DeploymentConfigField[string]{ + Name: "OIDC Issuer URL", + Usage: "Issuer URL to use for Login with OIDC.", + Flag: "oidc-issuer-url", + }, + Scopes: &codersdk.DeploymentConfigField[[]string]{ + Name: "OIDC Scopes", + Usage: "Scopes to grant when authenticating with OIDC.", + Flag: "oidc-scopes", + Default: []string{oidc.ScopeOpenID, "profile", "email"}, + }, }, - TLSClientCAFile: codersdk.DeploymentConfigField[string]{ - Name: "TLS Client CA Files", - 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]{ - Name: "TLS Client Auth", - 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]{ - Name: "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", - }, - TLSMinVersion: codersdk.DeploymentConfigField[string]{ - Name: "TLS Minimum Version", - 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]{ + + Telemetry: &codersdk.TelemetryConfig{ + Enable: &codersdk.DeploymentConfigField[bool]{ + Name: "Telemetry Enable", + 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, + }, + Trace: &codersdk.DeploymentConfigField[bool]{ + Name: "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", + Default: flag.Lookup("test.v") == nil, + }, + URL: &codersdk.DeploymentConfigField[string]{ + Name: "Telemetry URL", + Usage: "URL to send telemetry.", + Flag: "telemetry-url", + Hidden: true, + Default: "https://telemetry.coder.com", + }, + }, + TLS: &codersdk.TLSConfig{ + Enable: &codersdk.DeploymentConfigField[bool]{ + Name: "TLS Enable", + Usage: "Whether TLS will be enabled.", + Flag: "tls-enable", + }, + CertFiles: &codersdk.DeploymentConfigField[[]string]{ + Name: "TLS Certificate 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", + }, + ClientCAFile: &codersdk.DeploymentConfigField[string]{ + Name: "TLS Client CA Files", + Usage: "PEM-encoded Certificate Authority file used for checking the authenticity of client", + Flag: "tls-client-ca-file", + }, + ClientAuth: &codersdk.DeploymentConfigField[string]{ + Name: "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", + }, + KeyFiles: &codersdk.DeploymentConfigField[[]string]{ + Name: "TLS Key Files", + Usage: "Paths to the private keys for each of the certificates. It requires a PEM-encoded file.", + Flag: "tls-key-file", + }, + MinVersion: &codersdk.DeploymentConfigField[string]{ + Name: "TLS Minimum Version", + Usage: "Minimum supported version of TLS. Accepted values are \"tls10\", \"tls11\", \"tls12\" or \"tls13\"", + Flag: "tls-min-version", + Default: "tls12", + }, + }, + TraceEnable: &codersdk.DeploymentConfigField[bool]{ Name: "Trace Enable", - Key: "trace", Usage: "Whether application tracing data is collected.", Flag: "trace", }, - SecureAuthCookie: codersdk.DeploymentConfigField[bool]{ + SecureAuthCookie: &codersdk.DeploymentConfigField[bool]{ Name: "Secure Auth Cookie", - Key: "secure_auth_cookie", Usage: "Controls if the 'Secure' property is set on browser session cookies.", Flag: "secure-auth-cookie", }, - SSHKeygenAlgorithm: codersdk.DeploymentConfigField[string]{ - Name: "SSH Keygen Algorithm", - 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", + SSHKeygenAlgorithm: &codersdk.DeploymentConfigField[string]{ + Name: "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", }, - AutoImportTemplates: codersdk.DeploymentConfigField[[]string]{ + AutoImportTemplates: &codersdk.DeploymentConfigField[[]string]{ Name: "Auto Import Templates", - 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]{ - Name: "Metrics Cache Refresh Interval", - 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]{ - Name: "Agent Stat Refresh Interval", - Key: "agent_stat_refresh_interval", - Usage: "How frequently agent stats are recorded", - Flag: "agent-stats-refresh-interval", - Hidden: true, - Value: 10 * time.Minute, - }, - AuditLogging: codersdk.DeploymentConfigField[bool]{ + MetricsCacheRefreshInterval: &codersdk.DeploymentConfigField[time.Duration]{ + Name: "Metrics Cache Refresh Interval", + Usage: "How frequently metrics are refreshed", + Flag: "metrics-cache-refresh-interval", + Hidden: true, + Default: time.Hour, + }, + AgentStatRefreshInterval: &codersdk.DeploymentConfigField[time.Duration]{ + Name: "Agent Stat Refresh Interval", + Usage: "How frequently agent stats are recorded", + Flag: "agent-stats-refresh-interval", + Hidden: true, + Default: 10 * time.Minute, + }, + AuditLogging: &codersdk.DeploymentConfigField[bool]{ Name: "Audit Logging", - Key: "audit_logging", Usage: "Specifies whether audit logging is enabled.", Flag: "audit-logging", - Value: true, + Default: true, Enterprise: true, }, - BrowserOnly: codersdk.DeploymentConfigField[bool]{ + BrowserOnly: &codersdk.DeploymentConfigField[bool]{ Name: "Browser Only", - Key: "browser_only", Usage: "Whether Coder only allows connections to workspaces via the browser.", Flag: "browser-only", Enterprise: true, }, - SCIMAPIKey: codersdk.DeploymentConfigField[string]{ + SCIMAPIKey: &codersdk.DeploymentConfigField[string]{ Name: "SCIM API Key", - 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, + Secret: true, }, - UserWorkspaceQuota: codersdk.DeploymentConfigField[int]{ + UserWorkspaceQuota: &codersdk.DeploymentConfigField[int]{ Name: "User Workspace Quota", - Key: "user_workspace_quota", Usage: "Enables and sets a limit on how many workspaces each user can create.", Flag: "user-workspace-quota", Enterprise: true, @@ -377,11 +349,11 @@ func newConfig() codersdk.DeploymentConfig { } //nolint:revive -func Config(flagset *pflag.FlagSet, vip *viper.Viper) (codersdk.DeploymentConfig, error) { +func Config(flagset *pflag.FlagSet, vip *viper.Viper) (*codersdk.DeploymentConfig, error) { dc := newConfig() flg, err := flagset.GetString(config.FlagName) if err != nil { - return dc, xerrors.Errorf("get global config from flag: %w", err) + return nil, xerrors.Errorf("get global config from flag: %w", err) } vip.SetEnvPrefix("coder") vip.AutomaticEnv() @@ -394,104 +366,189 @@ func Config(flagset *pflag.FlagSet, vip *viper.Viper) (codersdk.DeploymentConfig } } - dcv := reflect.ValueOf(&dc).Elem() - t := dcv.Type() - for i := 0; i < t.NumField(); i++ { - fve := dcv.Field(i) - key := fve.FieldByName("Key").String() - value := fve.FieldByName("Value").Interface() + setConfig("", vip, &dc) + + return dc, nil +} + +func setConfig(prefix string, vip *viper.Viper, target interface{}) { + val := reflect.Indirect(reflect.ValueOf(target)) + typ := val.Type() + if typ.Kind() != reflect.Struct { + val = val.Elem() + typ = val.Type() + } + + // Manually bind to env to support CODER_$INDEX_$FIELD format for structured slices. + _ = vip.BindEnv(prefix, formatEnv(prefix)) + if strings.HasPrefix(typ.Name(), "DeploymentConfigField[") { + value := val.FieldByName("Value").Interface() switch value.(type) { case string: - fve.FieldByName("Value").SetString(vip.GetString(key)) + val.FieldByName("Value").SetString(vip.GetString(prefix)) case bool: - fve.FieldByName("Value").SetBool(vip.GetBool(key)) + val.FieldByName("Value").SetBool(vip.GetBool(prefix)) case int: - fve.FieldByName("Value").SetInt(int64(vip.GetInt(key))) + val.FieldByName("Value").SetInt(int64(vip.GetInt(prefix))) case time.Duration: - fve.FieldByName("Value").SetInt(int64(vip.GetDuration(key))) + val.FieldByName("Value").SetInt(int64(vip.GetDuration(prefix))) case []string: // As of October 21st, 2022 we supported delimiting a string // with a comma, but Viper only supports with a space. This // is a small hack around it! - rawSlice := reflect.ValueOf(vip.GetStringSlice(key)).Interface() + rawSlice := reflect.ValueOf(vip.GetStringSlice(prefix)).Interface() slice, ok := rawSlice.([]string) if !ok { - return dc, xerrors.Errorf("string slice is of type %T", rawSlice) + panic(fmt.Sprintf("string slice is of type %T", rawSlice)) } value := make([]string, 0, len(slice)) for _, entry := range slice { value = append(value, strings.Split(entry, ",")...) } - fve.FieldByName("Value").Set(reflect.ValueOf(value)) + val.FieldByName("Value").Set(reflect.ValueOf(value)) default: - return dc, xerrors.Errorf("unsupported type %T", value) + panic(fmt.Sprintf("unsupported type %T", value)) } + return } - return dc, nil + for i := 0; i < typ.NumField(); i++ { + fv := val.Field(i) + ft := fv.Type() + tag := typ.Field(i).Tag.Get("json") + var key string + if prefix == "" { + key = tag + } else { + key = fmt.Sprintf("%s.%s", prefix, tag) + } + switch ft.Kind() { + case reflect.Ptr: + setConfig(key, vip, fv.Interface()) + case reflect.Slice: + for j := 0; j < fv.Len(); j++ { + key := fmt.Sprintf("%s.%d", key, j) + setConfig(key, vip, fv.Index(j).Interface()) + } + default: + panic(fmt.Sprintf("unsupported type %T", ft)) + } + } } func NewViper() *viper.Viper { dc := newConfig() - v := viper.New() - v.SetEnvPrefix("coder") - v.AutomaticEnv() - v.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_")) + vip := viper.New() + vip.SetEnvPrefix("coder") + vip.AutomaticEnv() + vip.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_")) + + setViperDefaults("", vip, dc) + + return vip +} - 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() - v.SetDefault(key, value) +func setViperDefaults(prefix string, vip *viper.Viper, target interface{}) { + val := reflect.ValueOf(target).Elem() + val = reflect.Indirect(val) + typ := val.Type() + if strings.HasPrefix(typ.Name(), "DeploymentConfigField") { + value := val.FieldByName("Default").Interface() + vip.SetDefault(prefix, value) + return } - return v + for i := 0; i < typ.NumField(); i++ { + fv := val.Field(i) + ft := fv.Type() + tag := typ.Field(i).Tag.Get("json") + var key string + if prefix == "" { + key = tag + } else { + key = fmt.Sprintf("%s.%s", prefix, tag) + } + switch ft.Kind() { + case reflect.Ptr: + setViperDefaults(key, vip, fv.Interface()) + case reflect.Slice: + // we currently don't support default values on structured slices + continue + default: + panic(fmt.Sprintf("unsupported type %T", ft)) + } + } } //nolint:revive func AttachFlags(flagset *pflag.FlagSet, vip *viper.Viper, enterprise bool) { - dc := newConfig() - dcv := reflect.ValueOf(dc) - t := dcv.Type() - for i := 0; i < t.NumField(); i++ { - fv := dcv.Field(i) - isEnt := fv.FieldByName("Enterprise").Bool() + setFlags("", flagset, vip, newConfig(), enterprise) +} + +//nolint:revive +func setFlags(prefix string, flagset *pflag.FlagSet, vip *viper.Viper, target interface{}, enterprise bool) { + val := reflect.Indirect(reflect.ValueOf(target)) + typ := val.Type() + if strings.HasPrefix(typ.Name(), "DeploymentConfigField") { + isEnt := val.FieldByName("Enterprise").Bool() if enterprise != isEnt { - continue + return } - key := fv.FieldByName("Key").String() - flg := fv.FieldByName("Flag").String() + flg := val.FieldByName("Flag").String() if flg == "" { - continue + return } - 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() + usage := val.FieldByName("Usage").String() + usage = fmt.Sprintf("%s\n%s", usage, cliui.Styles.Placeholder.Render("Consumes $"+formatEnv(prefix))) + shorthand := val.FieldByName("Shorthand").String() + hidden := val.FieldByName("Hidden").Bool() + value := val.FieldByName("Default").Interface() switch value.(type) { case string: - _ = flagset.StringP(flg, shorthand, vip.GetString(key), usage) + _ = flagset.StringP(flg, shorthand, vip.GetString(prefix), usage) case bool: - _ = flagset.BoolP(flg, shorthand, vip.GetBool(key), usage) + _ = flagset.BoolP(flg, shorthand, vip.GetBool(prefix), usage) case int: - _ = flagset.IntP(flg, shorthand, vip.GetInt(key), usage) + _ = flagset.IntP(flg, shorthand, vip.GetInt(prefix), usage) case time.Duration: - _ = flagset.DurationP(flg, shorthand, vip.GetDuration(key), usage) + _ = flagset.DurationP(flg, shorthand, vip.GetDuration(prefix), usage) case []string: - _ = flagset.StringSliceP(flg, shorthand, vip.GetStringSlice(key), usage) + _ = flagset.StringSliceP(flg, shorthand, vip.GetStringSlice(prefix), usage) default: - continue + panic(fmt.Sprintf("unsupported type %T", typ)) } - _ = vip.BindPFlag(key, flagset.Lookup(flg)) + _ = vip.BindPFlag(prefix, flagset.Lookup(flg)) if hidden { _ = flagset.MarkHidden(flg) } + + return + } + + for i := 0; i < typ.NumField(); i++ { + fv := val.Field(i) + ft := fv.Type() + tag := typ.Field(i).Tag.Get("json") + var key string + if prefix == "" { + key = tag + } else { + key = fmt.Sprintf("%s.%s", prefix, tag) + } + switch ft.Kind() { + case reflect.Ptr: + setFlags(key, flagset, vip, fv.Interface(), enterprise) + case reflect.Slice: + for j := 0; j < fv.Len(); j++ { + key := fmt.Sprintf("%s.%d", key, j) + setFlags(key, flagset, vip, fv.Index(j).Interface(), enterprise) + } + default: + panic(fmt.Sprintf("unsupported type %T", ft)) + } } } diff --git a/cli/deployment/config_test.go b/cli/deployment/config_test.go index da67f486c8153..aeb50d4914b5a 100644 --- a/cli/deployment/config_test.go +++ b/cli/deployment/config_test.go @@ -21,7 +21,7 @@ func TestConfig(t *testing.T) { for _, tc := range []struct { Name string Env map[string]string - Valid func(config codersdk.DeploymentConfig) + Valid func(config *codersdk.DeploymentConfig) }{{ Name: "Deployment", Env: map[string]string{ @@ -39,19 +39,19 @@ func TestConfig(t *testing.T) { "CODER_TELEMETRY_TRACE": "false", "CODER_WILDCARD_ACCESS_URL": "something-wildcard.com", }, - Valid: func(config codersdk.DeploymentConfig) { + Valid: func(config *codersdk.DeploymentConfig) { require.Equal(t, config.Address.Value, "0.0.0.0:8443") require.Equal(t, config.AccessURL.Value, "https://dev.coder.com") require.Equal(t, config.PostgresURL.Value, "some-url") - require.Equal(t, config.PprofAddress.Value, "something") - require.Equal(t, config.PprofEnable.Value, true) - require.Equal(t, config.PrometheusAddress.Value, "hello-world") - require.Equal(t, config.PrometheusEnable.Value, true) + require.Equal(t, config.Pprof.Address.Value, "something") + require.Equal(t, config.Pprof.Enable.Value, true) + require.Equal(t, config.Prometheus.Address.Value, "hello-world") + require.Equal(t, config.Prometheus.Enable.Value, true) require.Equal(t, config.ProvisionerDaemons.Value, 5) require.Equal(t, config.SecureAuthCookie.Value, true) require.Equal(t, config.SSHKeygenAlgorithm.Value, "potato") - require.Equal(t, config.TelemetryEnable.Value, false) - require.Equal(t, config.TelemetryTrace.Value, false) + require.Equal(t, config.Telemetry.Enable.Value, false) + require.Equal(t, config.Telemetry.Trace.Value, false) require.Equal(t, config.WildcardAccessURL.Value, "something-wildcard.com") }, }, { @@ -66,15 +66,15 @@ func TestConfig(t *testing.T) { "CODER_DERP_SERVER_RELAY_URL": "1.1.1.1", "CODER_DERP_SERVER_STUN_ADDRESSES": "google.org", }, - Valid: func(config codersdk.DeploymentConfig) { - require.Equal(t, config.DERPConfigPath.Value, "/example/path") - require.Equal(t, config.DERPConfigURL.Value, "https://google.com") - require.Equal(t, config.DERPServerEnable.Value, false) - require.Equal(t, config.DERPServerRegionCode.Value, "something") - require.Equal(t, config.DERPServerRegionID.Value, 123) - require.Equal(t, config.DERPServerRegionName.Value, "Code-Land") - require.Equal(t, config.DERPServerRelayURL.Value, "1.1.1.1") - require.Equal(t, config.DERPServerSTUNAddresses.Value, []string{"google.org"}) + Valid: func(config *codersdk.DeploymentConfig) { + require.Equal(t, config.DERP.Config.Path.Value, "/example/path") + require.Equal(t, config.DERP.Config.URL.Value, "https://google.com") + require.Equal(t, config.DERP.Server.Enable.Value, false) + require.Equal(t, config.DERP.Server.RegionCode.Value, "something") + require.Equal(t, config.DERP.Server.RegionID.Value, 123) + require.Equal(t, config.DERP.Server.RegionName.Value, "Code-Land") + require.Equal(t, config.DERP.Server.RelayURL.Value, "1.1.1.1") + require.Equal(t, config.DERP.Server.STUNAddresses.Value, []string{"google.org"}) }, }, { Name: "Enterprise", @@ -84,7 +84,7 @@ func TestConfig(t *testing.T) { "CODER_SCIM_API_KEY": "some-key", "CODER_USER_WORKSPACE_QUOTA": "10", }, - Valid: func(config codersdk.DeploymentConfig) { + Valid: func(config *codersdk.DeploymentConfig) { require.Equal(t, config.AuditLogging.Value, false) require.Equal(t, config.BrowserOnly.Value, true) require.Equal(t, config.SCIMAPIKey.Value, "some-key") @@ -100,19 +100,19 @@ func TestConfig(t *testing.T) { "CODER_TLS_ENABLE": "true", "CODER_TLS_MIN_VERSION": "tls10", }, - Valid: func(config codersdk.DeploymentConfig) { - require.Len(t, config.TLSCertFiles.Value, 2) - require.Equal(t, config.TLSCertFiles.Value[0], "/etc/acme-sh/dev.coder.com") - require.Equal(t, config.TLSCertFiles.Value[1], "/etc/acme-sh/*.dev.coder.com") + Valid: func(config *codersdk.DeploymentConfig) { + require.Len(t, config.TLS.CertFiles.Value, 2) + require.Equal(t, config.TLS.CertFiles.Value[0], "/etc/acme-sh/dev.coder.com") + require.Equal(t, config.TLS.CertFiles.Value[1], "/etc/acme-sh/*.dev.coder.com") - require.Len(t, config.TLSKeyFiles.Value, 2) - require.Equal(t, config.TLSKeyFiles.Value[0], "/etc/acme-sh/dev.coder.com") - require.Equal(t, config.TLSKeyFiles.Value[1], "/etc/acme-sh/*.dev.coder.com") + require.Len(t, config.TLS.KeyFiles.Value, 2) + require.Equal(t, config.TLS.KeyFiles.Value[0], "/etc/acme-sh/dev.coder.com") + require.Equal(t, config.TLS.KeyFiles.Value[1], "/etc/acme-sh/*.dev.coder.com") - require.Equal(t, config.TLSClientAuth.Value, "/some/path") - require.Equal(t, config.TLSClientCAFile.Value, "/some/path") - require.Equal(t, config.TLSEnable.Value, true) - require.Equal(t, config.TLSMinVersion.Value, "tls10") + require.Equal(t, config.TLS.ClientAuth.Value, "/some/path") + require.Equal(t, config.TLS.ClientCAFile.Value, "/some/path") + require.Equal(t, config.TLS.Enable.Value, true) + require.Equal(t, config.TLS.MinVersion.Value, "tls10") }, }, { Name: "OIDC", @@ -124,13 +124,13 @@ func TestConfig(t *testing.T) { "CODER_OIDC_ALLOW_SIGNUPS": "false", "CODER_OIDC_SCOPES": "something,here", }, - Valid: func(config codersdk.DeploymentConfig) { - require.Equal(t, config.OIDCIssuerURL.Value, "https://accounts.google.com") - require.Equal(t, config.OIDCEmailDomain.Value, "coder.com") - require.Equal(t, config.OIDCClientID.Value, "client") - require.Equal(t, config.OIDCClientSecret.Value, "secret") - require.Equal(t, config.OIDCAllowSignups.Value, false) - require.Equal(t, config.OIDCScopes.Value, []string{"something", "here"}) + Valid: func(config *codersdk.DeploymentConfig) { + require.Equal(t, config.OIDC.IssuerURL.Value, "https://accounts.google.com") + require.Equal(t, config.OIDC.EmailDomain.Value, "coder.com") + require.Equal(t, config.OIDC.ClientID.Value, "client") + require.Equal(t, config.OIDC.ClientSecret.Value, "secret") + require.Equal(t, config.OIDC.AllowSignups.Value, false) + require.Equal(t, config.OIDC.Scopes.Value, []string{"something", "here"}) }, }, { Name: "GitHub", @@ -141,12 +141,12 @@ func TestConfig(t *testing.T) { "CODER_OAUTH2_GITHUB_ALLOWED_TEAMS": "coder", "CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS": "true", }, - Valid: func(config codersdk.DeploymentConfig) { - require.Equal(t, config.OAuth2GithubClientID.Value, "client") - require.Equal(t, config.OAuth2GithubClientSecret.Value, "secret") - require.Equal(t, []string{"coder"}, config.OAuth2GithubAllowedOrgs.Value) - require.Equal(t, []string{"coder"}, config.OAuth2GithubAllowedTeams.Value) - require.Equal(t, config.OAuth2GithubAllowSignups.Value, true) + Valid: func(config *codersdk.DeploymentConfig) { + require.Equal(t, config.OAuth2.Github.ClientID.Value, "client") + require.Equal(t, config.OAuth2.Github.ClientSecret.Value, "secret") + require.Equal(t, []string{"coder"}, config.OAuth2.Github.AllowedOrgs.Value) + require.Equal(t, []string{"coder"}, config.OAuth2.Github.AllowedTeams.Value) + require.Equal(t, config.OAuth2.Github.AllowSignups.Value, true) }, }} { tc := tc diff --git a/cli/server.go b/cli/server.go index e937c2ac80aeb..c46f1114fbbe3 100644 --- a/cli/server.go +++ b/cli/server.go @@ -117,11 +117,11 @@ 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.TelemetryEnable.Value && !isTest() + shouldCoderTrace := cfg.Telemetry.Enable.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.TelemetryTrace.Value + shouldCoderTrace = cfg.Telemetry.Trace.Value } if cfg.TraceEnable.Value || shouldCoderTrace { @@ -174,13 +174,13 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co defer listener.Close() var tlsConfig *tls.Config - if cfg.TLSEnable.Value { + if cfg.TLS.Enable.Value { tlsConfig, err = configureTLS( - cfg.TLSMinVersion.Value, - cfg.TLSClientAuth.Value, - cfg.TLSCertFiles.Value, - cfg.TLSKeyFiles.Value, - cfg.TLSClientCAFile.Value, + cfg.TLS.MinVersion.Value, + cfg.TLS.ClientAuth.Value, + cfg.TLS.CertFiles.Value, + cfg.TLS.KeyFiles.Value, + cfg.TLS.ClientCAFile.Value, ) if err != nil { return xerrors.Errorf("configure tls: %w", err) @@ -202,7 +202,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co Scheme: "http", Host: tcpAddr.String(), } - if cfg.TLSEnable.Value { + if cfg.TLS.Enable.Value { localURL.Scheme = "https" } @@ -297,22 +297,22 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co defaultRegion := &tailcfg.DERPRegion{ EmbeddedRelay: true, - RegionID: cfg.DERPServerRegionID.Value, - RegionCode: cfg.DERPServerRegionCode.Value, - RegionName: cfg.DERPServerRegionName.Value, + RegionID: cfg.DERP.Server.RegionID.Value, + RegionCode: cfg.DERP.Server.RegionCode.Value, + RegionName: cfg.DERP.Server.RegionName.Value, Nodes: []*tailcfg.DERPNode{{ - Name: fmt.Sprintf("%db", cfg.DERPServerRegionID.Value), - RegionID: cfg.DERPServerRegionID.Value, + Name: fmt.Sprintf("%db", cfg.DERP.Server.RegionID.Value), + RegionID: cfg.DERP.Server.RegionID.Value, HostName: accessURLParsed.Hostname(), DERPPort: accessURLPort, STUNPort: -1, ForceHTTP: accessURLParsed.Scheme == "http", }}, } - if !cfg.DERPServerEnable.Value { + if !cfg.DERP.Server.Enable.Value { defaultRegion = nil } - derpMap, err := tailnet.NewDERPMap(ctx, defaultRegion, cfg.DERPServerSTUNAddresses.Value, cfg.DERPConfigURL.Value, cfg.DERPConfigPath.Value) + derpMap, err := tailnet.NewDERPMap(ctx, defaultRegion, cfg.DERP.Server.STUNAddresses.Value, cfg.DERP.Config.URL.Value, cfg.DERP.Config.Path.Value) if err != nil { return xerrors.Errorf("create derp map: %w", err) } @@ -350,35 +350,35 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co MetricsCacheRefreshInterval: cfg.MetricsCacheRefreshInterval.Value, AgentStatsRefreshInterval: cfg.AgentStatRefreshInterval.Value, Experimental: ExperimentalEnabled(cmd), - DeploymentConfig: &cfg, + DeploymentConfig: cfg, } if tlsConfig != nil { options.TLSCertificates = tlsConfig.Certificates } - if cfg.OAuth2GithubClientSecret.Value != "" { + if cfg.OAuth2.Github.ClientSecret.Value != "" { options.GithubOAuth2Config, err = configureGithubOAuth2(accessURLParsed, - cfg.OAuth2GithubClientID.Value, - cfg.OAuth2GithubClientSecret.Value, - cfg.OAuth2GithubAllowSignups.Value, - cfg.OAuth2GithubAllowedOrgs.Value, - cfg.OAuth2GithubAllowedTeams.Value, - cfg.OAuth2GithubEnterpriseBaseURL.Value, + cfg.OAuth2.Github.ClientID.Value, + cfg.OAuth2.Github.ClientSecret.Value, + cfg.OAuth2.Github.AllowSignups.Value, + cfg.OAuth2.Github.AllowedOrgs.Value, + cfg.OAuth2.Github.AllowedTeams.Value, + cfg.OAuth2.Github.EnterpriseBaseURL.Value, ) if err != nil { return xerrors.Errorf("configure github oauth2: %w", err) } } - if cfg.OIDCClientSecret.Value != "" { - if cfg.OIDCClientID.Value == "" { + if cfg.OIDC.ClientSecret.Value != "" { + if cfg.OIDC.ClientID.Value == "" { return xerrors.Errorf("OIDC client ID be set!") } - if cfg.OIDCIssuerURL.Value == "" { + if cfg.OIDC.IssuerURL.Value == "" { return xerrors.Errorf("OIDC issuer URL must be set!") } - oidcProvider, err := oidc.NewProvider(ctx, cfg.OIDCIssuerURL.Value) + oidcProvider, err := oidc.NewProvider(ctx, cfg.OIDC.IssuerURL.Value) if err != nil { return xerrors.Errorf("configure oidc provider: %w", err) } @@ -388,17 +388,17 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co } options.OIDCConfig = &coderd.OIDCConfig{ OAuth2Config: &oauth2.Config{ - ClientID: cfg.OIDCClientID.Value, - ClientSecret: cfg.OIDCClientSecret.Value, + ClientID: cfg.OIDC.ClientID.Value, + ClientSecret: cfg.OIDC.ClientSecret.Value, RedirectURL: redirectURL.String(), Endpoint: oidcProvider.Endpoint(), - Scopes: cfg.OIDCScopes.Value, + Scopes: cfg.OIDC.Scopes.Value, }, Verifier: oidcProvider.Verifier(&oidc.Config{ - ClientID: cfg.OIDCClientID.Value, + ClientID: cfg.OIDC.ClientID.Value, }), - EmailDomain: cfg.OIDCEmailDomain.Value, - AllowSignups: cfg.OIDCAllowSignups.Value, + EmailDomain: cfg.OIDC.EmailDomain.Value, + AllowSignups: cfg.OIDC.AllowSignups.Value, } } @@ -461,26 +461,26 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co } // Parse the raw telemetry URL! - telemetryURL, err := parseURL(ctx, cfg.TelemetryURL.Value) + telemetryURL, err := parseURL(ctx, cfg.Telemetry.URL.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.Value && !cmd.Flags().Changed(cfg.TelemetryEnable.Flag) { - cfg.TelemetryEnable.Value = false + if cfg.InMemoryDatabase.Value && !cmd.Flags().Changed(cfg.Telemetry.Enable.Flag) { + cfg.Telemetry.Enable.Value = false } - if cfg.TelemetryEnable.Value { + if cfg.Telemetry.Enable.Value { options.Telemetry, err = telemetry.New(telemetry.Options{ BuiltinPostgres: builtinPostgres, DeploymentID: deploymentID, Database: options.Database, Logger: logger.Named("telemetry"), URL: telemetryURL, - GitHubOAuth: cfg.OAuth2GithubClientID.Value != "", - OIDCAuth: cfg.OIDCClientID.Value != "", - OIDCIssuerURL: cfg.OIDCIssuerURL.Value, - Prometheus: cfg.PrometheusEnable.Value, - STUN: len(cfg.DERPServerSTUNAddresses.Value) != 0, + GitHubOAuth: cfg.OAuth2.Github.ClientID.Value != "", + OIDCAuth: cfg.OIDC.ClientID.Value != "", + OIDCIssuerURL: cfg.OIDC.IssuerURL.Value, + Prometheus: cfg.Prometheus.Enable.Value, + STUN: len(cfg.DERP.Server.STUNAddresses.Value) != 0, Tunnel: tunnel != nil, }) if err != nil { @@ -491,11 +491,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.PprofEnable.Value { + if cfg.Pprof.Enable.Value { //nolint:revive - defer serveHandler(ctx, logger, nil, cfg.PprofAddress.Value, "pprof")() + defer serveHandler(ctx, logger, nil, cfg.Pprof.Address.Value, "pprof")() } - if cfg.PrometheusEnable.Value { + if cfg.Prometheus.Enable.Value { options.PrometheusRegistry = prometheus.NewRegistry() closeUsersFunc, err := prometheusmetrics.ActiveUsers(ctx, options.PrometheusRegistry, options.Database, 0) if err != nil { @@ -512,7 +512,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.PrometheusAddress.Value, "prometheus")() + ), cfg.Prometheus.Address.Value, "prometheus")() } // We use a separate coderAPICloser so the Enterprise API @@ -524,7 +524,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co } client := codersdk.New(localURL) - if cfg.TLSEnable.Value { + if cfg.TLS.Enable.Value { // Secure transport isn't needed for locally communicating! client.HTTPClient.Transport = &http.Transport{ TLSClientConfig: &tls.Config{ diff --git a/coderd/deploymentconfig_test.go b/coderd/deploymentconfig_test.go index 2f03e655c671d..a0031ac6c391f 100644 --- a/coderd/deploymentconfig_test.go +++ b/coderd/deploymentconfig_test.go @@ -26,13 +26,13 @@ func TestDeploymentConfig(t *testing.T) { // values should be returned cfg.AccessURL.Value = hi // values should not be returned - cfg.OAuth2GithubClientSecret.Value = hi - cfg.OIDCClientSecret.Value = hi + cfg.OAuth2.Github.ClientSecret.Value = hi + cfg.OIDC.ClientSecret.Value = hi cfg.PostgresURL.Value = hi cfg.SCIMAPIKey.Value = hi client := coderdtest.New(t, &coderdtest.Options{ - DeploymentConfig: &cfg, + DeploymentConfig: cfg, }) _ = coderdtest.CreateFirstUser(t, client) scrubbed, err := client.DeploymentConfig(ctx) @@ -40,8 +40,8 @@ func TestDeploymentConfig(t *testing.T) { // 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.OAuth2.Github.ClientSecret.Value) + require.Empty(t, scrubbed.OIDC.ClientSecret.Value) require.Empty(t, scrubbed.PostgresURL.Value) require.Empty(t, scrubbed.SCIMAPIKey.Value) } diff --git a/codersdk/deploymentconfig.go b/codersdk/deploymentconfig.go index 5933ec7c36d03..a33f79718ffec 100644 --- a/codersdk/deploymentconfig.go +++ b/codersdk/deploymentconfig.go @@ -10,61 +10,100 @@ import ( ) // 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"` - DERPServerRelayURL 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"` - ProxyTrustedHeaders DeploymentConfigField[[]string] `json:"proxy_trusted_headers"` - ProxyTrustedOrigins DeploymentConfigField[[]string] `json:"proxy_trusted_origins"` - CacheDirectory DeploymentConfigField[string] `json:"cache_directory"` - InMemoryDatabase DeploymentConfigField[bool] `json:"in_memory_database"` - ProvisionerDaemons DeploymentConfigField[int] `json:"provisioner_daemon_count"` - PostgresURL DeploymentConfigField[string] `json:"-"` - OAuth2GithubClientID DeploymentConfigField[string] `json:"oauth2_github_client_id"` - OAuth2GithubClientSecret DeploymentConfigField[string] `json:"-"` - OAuth2GithubAllowedOrgs DeploymentConfigField[[]string] `json:"oauth2_github_allowed_orgs"` - 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"` - 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"` - 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"` - AuditLogging DeploymentConfigField[bool] `json:"audit_logging"` - BrowserOnly DeploymentConfigField[bool] `json:"browser_only"` - SCIMAPIKey DeploymentConfigField[string] `json:"-"` - UserWorkspaceQuota DeploymentConfigField[int] `json:"user_workspace_quota"` + AccessURL *DeploymentConfigField[string] `json:"access_url" typescript:",notnull"` + WildcardAccessURL *DeploymentConfigField[string] `json:"wildcard_access_url" typescript:",notnull"` + Address *DeploymentConfigField[string] `json:"address" typescript:",notnull"` + AutobuildPollInterval *DeploymentConfigField[time.Duration] `json:"autobuild_poll_interval" typescript:",notnull"` + DERP *DERP `json:"derp" typescript:",notnull"` + Prometheus *PrometheusConfig `json:"prometheus" typescript:",notnull"` + Pprof *PprofConfig `json:"pprof" typescript:",notnull"` + ProxyTrustedHeaders *DeploymentConfigField[[]string] `json:"proxy_trusted_headers" typescript:",notnull"` + ProxyTrustedOrigins *DeploymentConfigField[[]string] `json:"proxy_trusted_origins" typescript:",notnull"` + CacheDirectory *DeploymentConfigField[string] `json:"cache_directory" typescript:",notnull"` + InMemoryDatabase *DeploymentConfigField[bool] `json:"in_memory_database" typescript:",notnull"` + ProvisionerDaemons *DeploymentConfigField[int] `json:"provisioner_daemons" typescript:",notnull"` + PostgresURL *DeploymentConfigField[string] `json:"pg_connection_url" typescript:",notnull"` + OAuth2 *OAuth2Config `json:"oauth2" typescript:",notnull"` + OIDC *OIDCConfig `json:"oidc" typescript:",notnull"` + Telemetry *TelemetryConfig `json:"telemetry" typescript:",notnull"` + TLS *TLSConfig `json:"tls" typescript:",notnull"` + TraceEnable *DeploymentConfigField[bool] `json:"trace_enable" typescript:",notnull"` + SecureAuthCookie *DeploymentConfigField[bool] `json:"secure_auth_cookie" typescript:",notnull"` + SSHKeygenAlgorithm *DeploymentConfigField[string] `json:"ssh_keygen_algorithm" typescript:",notnull"` + AutoImportTemplates *DeploymentConfigField[[]string] `json:"auto_import_templates" typescript:",notnull"` + MetricsCacheRefreshInterval *DeploymentConfigField[time.Duration] `json:"metrics_cache_refresh_interval" typescript:",notnull"` + AgentStatRefreshInterval *DeploymentConfigField[time.Duration] `json:"agent_stat_refresh_interval" typescript:",notnull"` + AuditLogging *DeploymentConfigField[bool] `json:"audit_logging" typescript:",notnull"` + BrowserOnly *DeploymentConfigField[bool] `json:"browser_only" typescript:",notnull"` + SCIMAPIKey *DeploymentConfigField[string] `json:"scim_api_key" typescript:",notnull"` + UserWorkspaceQuota *DeploymentConfigField[int] `json:"user_workspace_quota" typescript:",notnull"` +} + +type DERP struct { + Server *DERPServerConfig `json:"server" typescript:",notnull"` + Config *DERPConfig `json:"config" typescript:",notnull"` +} + +type DERPServerConfig struct { + Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"` + RegionID *DeploymentConfigField[int] `json:"region_id" typescript:",notnull"` + RegionCode *DeploymentConfigField[string] `json:"region_code" typescript:",notnull"` + RegionName *DeploymentConfigField[string] `json:"region_name" typescript:",notnull"` + STUNAddresses *DeploymentConfigField[[]string] `json:"stun_addresses" typescript:",notnull"` + RelayURL *DeploymentConfigField[string] `json:"relay_url" typescript:",notnull"` +} + +type DERPConfig struct { + URL *DeploymentConfigField[string] `json:"url" typescript:",notnull"` + Path *DeploymentConfigField[string] `json:"path" typescript:",notnull"` +} + +type PrometheusConfig struct { + Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"` + Address *DeploymentConfigField[string] `json:"address" typescript:",notnull"` +} + +type PprofConfig struct { + Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"` + Address *DeploymentConfigField[string] `json:"address" typescript:",notnull"` +} + +type OAuth2Config struct { + Github *OAuth2GithubConfig `json:"github" typescript:",notnull"` +} + +type OAuth2GithubConfig struct { + ClientID *DeploymentConfigField[string] `json:"client_id" typescript:",notnull"` + ClientSecret *DeploymentConfigField[string] `json:"client_secret" typescript:",notnull"` + AllowedOrgs *DeploymentConfigField[[]string] `json:"allowed_orgs" typescript:",notnull"` + AllowedTeams *DeploymentConfigField[[]string] `json:"allowed_teams" typescript:",notnull"` + AllowSignups *DeploymentConfigField[bool] `json:"allow_signups" typescript:",notnull"` + EnterpriseBaseURL *DeploymentConfigField[string] `json:"enterprise_base_url" typescript:",notnull"` +} + +type OIDCConfig struct { + AllowSignups *DeploymentConfigField[bool] `json:"allow_signups" typescript:",notnull"` + ClientID *DeploymentConfigField[string] `json:"client_id" typescript:",notnull"` + ClientSecret *DeploymentConfigField[string] `json:"client_secret" typescript:",notnull"` + EmailDomain *DeploymentConfigField[string] `json:"email_domain" typescript:",notnull"` + IssuerURL *DeploymentConfigField[string] `json:"issuer_url" typescript:",notnull"` + Scopes *DeploymentConfigField[[]string] `json:"scopes" typescript:",notnull"` +} + +type TelemetryConfig struct { + Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"` + Trace *DeploymentConfigField[bool] `json:"trace" typescript:",notnull"` + URL *DeploymentConfigField[string] `json:"url" typescript:",notnull"` +} + +type TLSConfig struct { + Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"` + CertFiles *DeploymentConfigField[[]string] `json:"cert_file" typescript:",notnull"` + ClientAuth *DeploymentConfigField[string] `json:"client_auth" typescript:",notnull"` + ClientCAFile *DeploymentConfigField[string] `json:"client_ca_file" typescript:",notnull"` + KeyFiles *DeploymentConfigField[[]string] `json:"key_file" typescript:",notnull"` + MinVersion *DeploymentConfigField[string] `json:"min_version" typescript:",notnull"` } type Flaggable interface { @@ -72,17 +111,48 @@ type Flaggable interface { } type DeploymentConfigField[T Flaggable] struct { - Key string `json:"key"` - // Name appears in the deployment UI. Name string `json:"name"` Usage string `json:"usage"` Flag string `json:"flag"` Shorthand string `json:"shorthand"` Enterprise bool `json:"enterprise"` Hidden bool `json:"hidden"` + Secret bool `json:"secret"` + Default T `json:"default"` Value T `json:"value"` } +// MarshalJSON removes the Value field from the JSON output of any fields marked Secret. +// nolint:revive +func (f *DeploymentConfigField[T]) MarshalJSON() ([]byte, error) { + copy := struct { + Name string `json:"name"` + Usage string `json:"usage"` + Flag string `json:"flag"` + Shorthand string `json:"shorthand"` + Enterprise bool `json:"enterprise"` + Hidden bool `json:"hidden"` + Secret bool `json:"secret"` + Default T `json:"default"` + Value T `json:"value"` + }{ + Name: f.Name, + Usage: f.Usage, + Flag: f.Flag, + Shorthand: f.Shorthand, + Enterprise: f.Enterprise, + Hidden: f.Hidden, + Secret: f.Secret, + } + + if !f.Secret { + copy.Default = f.Default + copy.Value = f.Value + } + + return json.Marshal(copy) +} + // 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) diff --git a/enterprise/cli/server.go b/enterprise/cli/server.go index 7700cd3b7e6ca..46595ca3fdd00 100644 --- a/enterprise/cli/server.go +++ b/enterprise/cli/server.go @@ -26,8 +26,8 @@ import ( func server() *cobra.Command { vip := deployment.NewViper() cmd := agpl.Server(vip, func(ctx context.Context, options *agplcoderd.Options) (*agplcoderd.API, io.Closer, error) { - if options.DeploymentConfig.DERPServerRelayURL.Value != "" { - _, err := url.Parse(options.DeploymentConfig.DERPServerRelayURL.Value) + if options.DeploymentConfig.DERP.Server.RelayURL.Value != "" { + _, err := url.Parse(options.DeploymentConfig.DERP.Server.RelayURL.Value) if err != nil { return nil, nil, xerrors.Errorf("derp-server-relay-address must be a valid HTTP URL: %w", err) } @@ -63,8 +63,8 @@ func server() *cobra.Command { SCIMAPIKey: []byte(options.DeploymentConfig.SCIMAPIKey.Value), UserWorkspaceQuota: options.DeploymentConfig.UserWorkspaceQuota.Value, RBAC: true, - DERPServerRelayAddress: options.DeploymentConfig.DERPServerRelayURL.Value, - DERPServerRegionID: options.DeploymentConfig.DERPServerRegionID.Value, + DERPServerRelayAddress: options.DeploymentConfig.DERP.Server.RelayURL.Value, + DERPServerRegionID: options.DeploymentConfig.DERP.Server.RegionID.Value, Options: options, } diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 8cf296785becc..e9345e9ffafe2 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -245,54 +245,53 @@ export interface DAUEntry { readonly amount: number } +// From codersdk/deploymentconfig.go +export interface DERP { + readonly server: DERPServerConfig + readonly config: DERPConfig +} + +// From codersdk/deploymentconfig.go +export interface DERPConfig { + readonly url: DeploymentConfigField + readonly path: DeploymentConfigField +} + // From codersdk/workspaceagents.go export interface DERPRegion { readonly preferred: boolean readonly latency_ms: number } +// From codersdk/deploymentconfig.go +export interface DERPServerConfig { + readonly enable: DeploymentConfigField + readonly region_id: DeploymentConfigField + readonly region_code: DeploymentConfigField + readonly region_name: DeploymentConfigField + readonly stun_addresses: DeploymentConfigField + readonly relay_url: DeploymentConfigField +} + // 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 derp: DERP + readonly prometheus: PrometheusConfig + readonly pprof: PprofConfig readonly proxy_trusted_headers: DeploymentConfigField readonly proxy_trusted_origins: DeploymentConfigField readonly cache_directory: DeploymentConfigField readonly in_memory_database: DeploymentConfigField - readonly provisioner_daemon_count: DeploymentConfigField - readonly oauth2_github_client_id: DeploymentConfigField - readonly oauth2_github_allowed_orgs: 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 provisioner_daemons: DeploymentConfigField + readonly pg_connection_url: DeploymentConfigField + readonly oauth2: OAuth2Config + readonly oidc: OIDCConfig + readonly telemetry: TelemetryConfig + readonly tls: TLSConfig readonly trace_enable: DeploymentConfigField readonly secure_auth_cookie: DeploymentConfigField readonly ssh_keygen_algorithm: DeploymentConfigField @@ -301,18 +300,20 @@ export interface DeploymentConfig { readonly agent_stat_refresh_interval: DeploymentConfigField readonly audit_logging: DeploymentConfigField readonly browser_only: DeploymentConfigField + readonly scim_api_key: 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 secret: boolean + readonly default: T readonly value: T } @@ -399,6 +400,31 @@ export interface LoginWithPasswordResponse { readonly session_token: string } +// From codersdk/deploymentconfig.go +export interface OAuth2Config { + readonly github: OAuth2GithubConfig +} + +// From codersdk/deploymentconfig.go +export interface OAuth2GithubConfig { + readonly client_id: DeploymentConfigField + readonly client_secret: DeploymentConfigField + readonly allowed_orgs: DeploymentConfigField + readonly allowed_teams: DeploymentConfigField + readonly allow_signups: DeploymentConfigField + readonly enterprise_base_url: DeploymentConfigField +} + +// From codersdk/deploymentconfig.go +export interface OIDCConfig { + readonly allow_signups: DeploymentConfigField + readonly client_id: DeploymentConfigField + readonly client_secret: DeploymentConfigField + readonly email_domain: DeploymentConfigField + readonly issuer_url: DeploymentConfigField + readonly scopes: DeploymentConfigField +} + // From codersdk/organizations.go export interface Organization { readonly id: string @@ -464,6 +490,18 @@ export interface PatchGroupRequest { readonly avatar_url?: string } +// From codersdk/deploymentconfig.go +export interface PprofConfig { + readonly enable: DeploymentConfigField + readonly address: DeploymentConfigField +} + +// From codersdk/deploymentconfig.go +export interface PrometheusConfig { + readonly enable: DeploymentConfigField + readonly address: DeploymentConfigField +} + // From codersdk/provisionerdaemons.go export interface ProvisionerDaemon { readonly id: string @@ -532,6 +570,23 @@ export interface ServerSentEvent { readonly data: any } +// From codersdk/deploymentconfig.go +export interface TLSConfig { + readonly enable: DeploymentConfigField + readonly cert_file: DeploymentConfigField + readonly client_auth: DeploymentConfigField + readonly client_ca_file: DeploymentConfigField + readonly key_file: DeploymentConfigField + readonly min_version: DeploymentConfigField +} + +// From codersdk/deploymentconfig.go +export interface TelemetryConfig { + readonly enable: DeploymentConfigField + readonly trace: DeploymentConfigField + readonly url: DeploymentConfigField +} + // From codersdk/templates.go export interface Template { readonly id: string diff --git a/site/src/components/DeploySettingsLayout/OptionsTable.tsx b/site/src/components/DeploySettingsLayout/OptionsTable.tsx index 8c47eb9b58aa0..e613b0348a634 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 { DeploymentConfig } from "api/typesGenerated" +import { DeploymentConfigField, Flaggable } from "api/typesGenerated" import { OptionDescription, OptionName, @@ -13,9 +13,9 @@ import { } from "components/DeploySettingsLayout/Option" import React from "react" -const OptionsTable: React.FC<{ options: Partial }> = ({ - options, -}) => { +const OptionsTable: React.FC<{ + options: Record> +}> = ({ options }) => { const styles = useStyles() return ( diff --git a/site/src/pages/DeploySettingsPage/AuthSettingsPage.tsx b/site/src/pages/DeploySettingsPage/AuthSettingsPage.tsx index 4a76c04e2797a..22bf85b0505a2 100644 --- a/site/src/pages/DeploySettingsPage/AuthSettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/AuthSettingsPage.tsx @@ -32,7 +32,7 @@ const AuthSettingsPage: React.FC = () => { /> - {deploymentConfig.oidc_client_id.value ? ( + {deploymentConfig.oidc.client_id.value ? ( ) : ( @@ -41,11 +41,11 @@ const AuthSettingsPage: React.FC = () => { @@ -59,7 +59,7 @@ const AuthSettingsPage: React.FC = () => { /> - {deploymentConfig.oauth2_github_client_id.value ? ( + {deploymentConfig.oauth2.github.client_id.value ? ( ) : ( @@ -68,15 +68,12 @@ const AuthSettingsPage: React.FC = () => { diff --git a/site/src/pages/DeploySettingsPage/NetworkSettingsPage.tsx b/site/src/pages/DeploySettingsPage/NetworkSettingsPage.tsx index 23e87a284e4b2..931dba1b7f2a8 100644 --- a/site/src/pages/DeploySettingsPage/NetworkSettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/NetworkSettingsPage.tsx @@ -22,10 +22,11 @@ const NetworkSettingsPage: React.FC = () => { diff --git a/site/src/pages/DeploySettingsPage/SecuritySettingsPage.tsx b/site/src/pages/DeploySettingsPage/SecuritySettingsPage.tsx index 0a7e3afdccae9..3a14de7711c66 100644 --- a/site/src/pages/DeploySettingsPage/SecuritySettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/SecuritySettingsPage.tsx @@ -89,10 +89,10 @@ const SecuritySettingsPage: React.FC = () => {