mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
chore: Make deployment admin page show better durations (#6856)
* chore: Make deployment admin page show better durations Also fix group mappings
This commit is contained in:
@ -190,19 +190,6 @@ func (d *Duration) String() string {
|
|||||||
return time.Duration(*d).String()
|
return time.Duration(*d).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Duration) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(d.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Duration) UnmarshalJSON(b []byte) error {
|
|
||||||
var s string
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return d.Set(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Duration) Type() string {
|
func (Duration) Type() string {
|
||||||
return "duration"
|
return "duration"
|
||||||
}
|
}
|
||||||
|
2
cli/testdata/coder_server_--help.golden
vendored
2
cli/testdata/coder_server_--help.golden
vendored
@ -170,7 +170,7 @@ backed by Tailscale and WireGuard.
|
|||||||
--http-address string, $CODER_HTTP_ADDRESS (default: 127.0.0.1:3000)
|
--http-address string, $CODER_HTTP_ADDRESS (default: 127.0.0.1:3000)
|
||||||
HTTP bind address of the server. Unset to disable the HTTP endpoint.
|
HTTP bind address of the server. Unset to disable the HTTP endpoint.
|
||||||
|
|
||||||
--max-token-lifetime duration, $CODER_MAX_TOKEN_LIFETIME (default: 2562047h47m16.854775807s)
|
--max-token-lifetime duration, $CODER_MAX_TOKEN_LIFETIME (default: 876600h0m0s)
|
||||||
The maximum lifetime duration users can specify when creating an API
|
The maximum lifetime duration users can specify when creating an API
|
||||||
token.
|
token.
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -354,19 +353,10 @@ func (api *API) tokenConfig(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxTokenLifetime time.Duration
|
|
||||||
// if --max-token-lifetime is unset (default value is math.MaxInt64)
|
|
||||||
// send back a falsy value
|
|
||||||
if values.MaxTokenLifetime.Value() == time.Duration(math.MaxInt64) {
|
|
||||||
maxTokenLifetime = 0
|
|
||||||
} else {
|
|
||||||
maxTokenLifetime = values.MaxTokenLifetime.Value()
|
|
||||||
}
|
|
||||||
|
|
||||||
httpapi.Write(
|
httpapi.Write(
|
||||||
r.Context(), rw, http.StatusOK,
|
r.Context(), rw, http.StatusOK,
|
||||||
codersdk.TokenConfig{
|
codersdk.TokenConfig{
|
||||||
MaxTokenLifetime: maxTokenLifetime,
|
MaxTokenLifetime: values.MaxTokenLifetime.Value(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"math"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -1113,7 +1112,10 @@ when required by your organization's security policy.`,
|
|||||||
Description: "The maximum lifetime duration users can specify when creating an API token.",
|
Description: "The maximum lifetime duration users can specify when creating an API token.",
|
||||||
Flag: "max-token-lifetime",
|
Flag: "max-token-lifetime",
|
||||||
Env: "CODER_MAX_TOKEN_LIFETIME",
|
Env: "CODER_MAX_TOKEN_LIFETIME",
|
||||||
Default: time.Duration(math.MaxInt64).String(),
|
// The default value is essentially "forever", so just use 100 years.
|
||||||
|
// We have to add in the 25 leap days for the frontend to show the
|
||||||
|
// "100 years" correctly.
|
||||||
|
Default: ((100 * 365 * time.Hour * 24) + (25 * time.Hour * 24)).String(),
|
||||||
Value: &c.MaxTokenLifetime,
|
Value: &c.MaxTokenLifetime,
|
||||||
Group: &deploymentGroupNetworkingHTTP,
|
Group: &deploymentGroupNetworkingHTTP,
|
||||||
YAML: "maxTokenLifetime",
|
YAML: "maxTokenLifetime",
|
||||||
|
@ -233,7 +233,7 @@ Output Stackdriver compatible logs to a given file.
|
|||||||
| ----------- | -------------------------------------- |
|
| ----------- | -------------------------------------- |
|
||||||
| Type | <code>duration</code> |
|
| Type | <code>duration</code> |
|
||||||
| Environment | <code>$CODER_MAX_TOKEN_LIFETIME</code> |
|
| Environment | <code>$CODER_MAX_TOKEN_LIFETIME</code> |
|
||||||
| Default | <code>2562047h47m16.854775807s</code> |
|
| Default | <code>876600h0m0s</code> |
|
||||||
|
|
||||||
The maximum lifetime duration users can specify when creating an API token.
|
The maximum lifetime duration users can specify when creating an API token.
|
||||||
|
|
||||||
|
73
site/src/components/DeploySettingsLayout/Options.test.tsx
Normal file
73
site/src/components/DeploySettingsLayout/Options.test.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { optionValue } from "./OptionsTable"
|
||||||
|
import { DeploymentOption } from "api/types"
|
||||||
|
|
||||||
|
const defaultOption: DeploymentOption = {
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
flag: "",
|
||||||
|
flag_shorthand: "",
|
||||||
|
value: "",
|
||||||
|
hidden: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("optionValue", () => {
|
||||||
|
it.each<{
|
||||||
|
option: DeploymentOption
|
||||||
|
expected: string | string[] | unknown
|
||||||
|
}>([
|
||||||
|
{
|
||||||
|
option: {
|
||||||
|
...defaultOption,
|
||||||
|
name: "Max Token Lifetime",
|
||||||
|
value: 3600 * 1e9,
|
||||||
|
},
|
||||||
|
expected: "1 hour",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
option: {
|
||||||
|
...defaultOption,
|
||||||
|
name: "Max Token Lifetime",
|
||||||
|
value: 24 * 3600 * 1e9,
|
||||||
|
},
|
||||||
|
expected: "1 day",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
option: {
|
||||||
|
...defaultOption,
|
||||||
|
name: "Session Duration",
|
||||||
|
value: 3600 * 1e9,
|
||||||
|
},
|
||||||
|
expected: "1 hour",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
option: {
|
||||||
|
...defaultOption,
|
||||||
|
name: "Session Duration",
|
||||||
|
value: 24 * 3600 * 1e9,
|
||||||
|
},
|
||||||
|
expected: "1 day",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
option: {
|
||||||
|
...defaultOption,
|
||||||
|
name: "Strict-Transport-Security",
|
||||||
|
value: 1000,
|
||||||
|
},
|
||||||
|
expected: "1000s",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
option: {
|
||||||
|
...defaultOption,
|
||||||
|
name: "OIDC Group Mapping",
|
||||||
|
value: {
|
||||||
|
"123": "foo",
|
||||||
|
"456": "bar",
|
||||||
|
"789": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: [`"123"->"foo"`, `"456"->"bar"`, `"789"->"baz"`],
|
||||||
|
},
|
||||||
|
])(`[$option.name]optionValue($option.value)`, ({ option, expected }) => {
|
||||||
|
expect(optionValue(option)).toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
@ -13,6 +13,7 @@ import {
|
|||||||
} from "components/DeploySettingsLayout/Option"
|
} from "components/DeploySettingsLayout/Option"
|
||||||
import { FC } from "react"
|
import { FC } from "react"
|
||||||
import { DisabledBadge } from "./Badges"
|
import { DisabledBadge } from "./Badges"
|
||||||
|
import { intervalToDuration, formatDuration } from "date-fns"
|
||||||
|
|
||||||
const OptionsTable: FC<{
|
const OptionsTable: FC<{
|
||||||
options: DeploymentOption[]
|
options: DeploymentOption[]
|
||||||
@ -34,7 +35,11 @@ const OptionsTable: FC<{
|
|||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{Object.values(options).map((option) => {
|
{Object.values(options).map((option) => {
|
||||||
if (option.value === null || option.value === "") {
|
if (
|
||||||
|
option.value === null ||
|
||||||
|
option.value === "" ||
|
||||||
|
option.value === undefined
|
||||||
|
) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@ -45,7 +50,7 @@ const OptionsTable: FC<{
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<OptionValue>{option.value}</OptionValue>
|
<OptionValue>{optionValue(option)}</OptionValue>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)
|
)
|
||||||
@ -56,6 +61,31 @@ const OptionsTable: FC<{
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optionValue is a helper function to format the value of a specific deployment options
|
||||||
|
export function optionValue(
|
||||||
|
option: DeploymentOption,
|
||||||
|
): string[] | string | unknown {
|
||||||
|
switch (option.name) {
|
||||||
|
case "Max Token Lifetime":
|
||||||
|
case "Session Duration":
|
||||||
|
// intervalToDuration takes ms, so convert nanoseconds to ms
|
||||||
|
return formatDuration(
|
||||||
|
intervalToDuration({ start: 0, end: (option.value as number) / 1e6 }),
|
||||||
|
)
|
||||||
|
case "Strict-Transport-Security":
|
||||||
|
if (option.value === 0) {
|
||||||
|
return "Disabled"
|
||||||
|
}
|
||||||
|
return (option.value as number).toString() + "s"
|
||||||
|
case "OIDC Group Mapping":
|
||||||
|
return Object.entries(option.value as Record<string, string>).map(
|
||||||
|
([key, value]) => `"${key}"->"${value}"`,
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return option.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
table: {
|
table: {
|
||||||
"& td": {
|
"& td": {
|
||||||
|
@ -12,10 +12,6 @@ describe("unit/CreateTokenForm", () => {
|
|||||||
maxTokenLifetime: number
|
maxTokenLifetime: number
|
||||||
expected: LifetimeDay[]
|
expected: LifetimeDay[]
|
||||||
}>([
|
}>([
|
||||||
{
|
|
||||||
maxTokenLifetime: 0,
|
|
||||||
expected: lifetimeDayPresets,
|
|
||||||
},
|
|
||||||
{ maxTokenLifetime: 6 * 24 * NANO_HOUR, expected: [] },
|
{ maxTokenLifetime: 6 * 24 * NANO_HOUR, expected: [] },
|
||||||
{
|
{
|
||||||
maxTokenLifetime: 20 * 24 * NANO_HOUR,
|
maxTokenLifetime: 20 * 24 * NANO_HOUR,
|
||||||
|
Reference in New Issue
Block a user