mirror of https://github.com/coder/coder.git
Merge 2a4ae4738d
into 93d8812284
This commit is contained in:
commit
94d7cd4721
|
@ -9517,7 +9517,6 @@ const docTemplate = `{
|
|||
"type": "string",
|
||||
"enum": [
|
||||
"example",
|
||||
"shared-ports",
|
||||
"auto-fill-parameters"
|
||||
],
|
||||
"x-enum-comments": {
|
||||
|
@ -9526,7 +9525,6 @@ const docTemplate = `{
|
|||
},
|
||||
"x-enum-varnames": [
|
||||
"ExperimentExample",
|
||||
"ExperimentSharedPorts",
|
||||
"ExperimentAutoFillParameters"
|
||||
]
|
||||
},
|
||||
|
|
|
@ -8516,16 +8516,12 @@
|
|||
},
|
||||
"codersdk.Experiment": {
|
||||
"type": "string",
|
||||
"enum": ["example", "shared-ports", "auto-fill-parameters"],
|
||||
"enum": ["example", "auto-fill-parameters"],
|
||||
"x-enum-comments": {
|
||||
"ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.",
|
||||
"ExperimentExample": "This isn't used for anything."
|
||||
},
|
||||
"x-enum-varnames": [
|
||||
"ExperimentExample",
|
||||
"ExperimentSharedPorts",
|
||||
"ExperimentAutoFillParameters"
|
||||
]
|
||||
"x-enum-varnames": ["ExperimentExample", "ExperimentAutoFillParameters"]
|
||||
},
|
||||
"codersdk.ExternalAuth": {
|
||||
"type": "object",
|
||||
|
|
|
@ -1055,9 +1055,6 @@ func New(options *Options) *API {
|
|||
r.Put("/autoupdates", api.putWorkspaceAutoupdates)
|
||||
r.Get("/resolve-autostart", api.resolveAutostart)
|
||||
r.Route("/port-share", func(r chi.Router) {
|
||||
r.Use(
|
||||
httpmw.RequireExperiment(api.Experiments, codersdk.ExperimentSharedPorts),
|
||||
)
|
||||
r.Get("/", api.workspaceAgentPortShares)
|
||||
r.Post("/", api.postWorkspaceAgentPortShare)
|
||||
r.Delete("/", api.deleteWorkspaceAgentPortShare)
|
||||
|
|
|
@ -511,6 +511,10 @@ func TestAgentStats(t *testing.T) {
|
|||
func TestExperimentsMetric(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if len(codersdk.ExperimentsAll) == 0 {
|
||||
t.Skip("No experiments are currently defined; skipping test.")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
experiments codersdk.Experiments
|
||||
|
@ -518,16 +522,16 @@ func TestExperimentsMetric(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
name: "Enabled experiment is exported in metrics",
|
||||
experiments: codersdk.Experiments{codersdk.ExperimentSharedPorts},
|
||||
experiments: codersdk.Experiments{codersdk.ExperimentExample},
|
||||
expected: map[codersdk.Experiment]float64{
|
||||
codersdk.ExperimentSharedPorts: 1,
|
||||
codersdk.ExperimentExample: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Disabled experiment is exported in metrics",
|
||||
experiments: codersdk.Experiments{},
|
||||
expected: map[codersdk.Experiment]float64{
|
||||
codersdk.ExperimentSharedPorts: 0,
|
||||
codersdk.ExperimentExample: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -549,7 +553,6 @@ func TestExperimentsMetric(t *testing.T) {
|
|||
out, err := reg.Gather()
|
||||
require.NoError(t, err)
|
||||
require.Lenf(t, out, 1, "unexpected number of registered metrics")
|
||||
|
||||
seen := make(map[codersdk.Experiment]float64)
|
||||
|
||||
for _, metric := range out[0].GetMetric() {
|
||||
|
|
|
@ -19,11 +19,7 @@ func TestPostWorkspaceAgentPortShare(t *testing.T) {
|
|||
t.Parallel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
dep := coderdtest.DeploymentValues(t)
|
||||
dep.Experiments = append(dep.Experiments, string(codersdk.ExperimentSharedPorts))
|
||||
ownerClient, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{
|
||||
DeploymentValues: dep,
|
||||
})
|
||||
ownerClient, db := coderdtest.NewWithDatabase(t, nil)
|
||||
owner := coderdtest.CreateFirstUser(t, ownerClient)
|
||||
client, user := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
|
||||
|
||||
|
@ -140,11 +136,7 @@ func TestGetWorkspaceAgentPortShares(t *testing.T) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
dep := coderdtest.DeploymentValues(t)
|
||||
dep.Experiments = append(dep.Experiments, string(codersdk.ExperimentSharedPorts))
|
||||
ownerClient, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{
|
||||
DeploymentValues: dep,
|
||||
})
|
||||
ownerClient, db := coderdtest.NewWithDatabase(t, nil)
|
||||
owner := coderdtest.CreateFirstUser(t, ownerClient)
|
||||
client, user := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
|
||||
|
||||
|
@ -180,11 +172,7 @@ func TestDeleteWorkspaceAgentPortShare(t *testing.T) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
dep := coderdtest.DeploymentValues(t)
|
||||
dep.Experiments = append(dep.Experiments, string(codersdk.ExperimentSharedPorts))
|
||||
ownerClient, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{
|
||||
DeploymentValues: dep,
|
||||
})
|
||||
ownerClient, db := coderdtest.NewWithDatabase(t, nil)
|
||||
owner := coderdtest.CreateFirstUser(t, ownerClient)
|
||||
client, user := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
|
||||
|
||||
|
|
|
@ -257,7 +257,6 @@ func TestWorkspaceApps(t *testing.T) {
|
|||
deploymentValues.DisablePathApps = serpent.Bool(opts.DisablePathApps)
|
||||
deploymentValues.Dangerous.AllowPathAppSharing = serpent.Bool(opts.DangerousAllowPathAppSharing)
|
||||
deploymentValues.Dangerous.AllowPathAppSiteOwnerAccess = serpent.Bool(opts.DangerousAllowPathAppSiteOwnerAccess)
|
||||
deploymentValues.Experiments = append(deploymentValues.Experiments, string(codersdk.ExperimentSharedPorts))
|
||||
|
||||
if opts.DisableSubdomainApps {
|
||||
opts.AppHost = ""
|
||||
|
|
|
@ -2192,8 +2192,7 @@ type Experiment string
|
|||
|
||||
const (
|
||||
// Add new experiments here!
|
||||
ExperimentExample Experiment = "example" // This isn't used for anything.
|
||||
ExperimentSharedPorts Experiment = "shared-ports"
|
||||
ExperimentExample Experiment = "example" // This isn't used for anything.
|
||||
ExperimentAutoFillParameters Experiment = "auto-fill-parameters" // This should not be taken out of experiments until we have redesigned the feature.
|
||||
)
|
||||
|
||||
|
@ -2201,9 +2200,7 @@ const (
|
|||
// users to opt-in to via --experimental='*'.
|
||||
// Experiments that are not ready for consumption by all users should
|
||||
// not be included here and will be essentially hidden.
|
||||
var ExperimentsAll = Experiments{
|
||||
ExperimentSharedPorts,
|
||||
}
|
||||
var ExperimentsAll = Experiments{}
|
||||
|
||||
// Experiments is a list of experiments.
|
||||
// Multiple experiments may be enabled at the same time.
|
||||
|
|
|
@ -2666,7 +2666,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
|||
| Value |
|
||||
| ---------------------- |
|
||||
| `example` |
|
||||
| `shared-ports` |
|
||||
| `auto-fill-parameters` |
|
||||
|
||||
## codersdk.ExternalAuth
|
||||
|
|
|
@ -17,12 +17,9 @@ import (
|
|||
func TestWorkspacePortShare(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dep := coderdtest.DeploymentValues(t)
|
||||
dep.Experiments = append(dep.Experiments, string(codersdk.ExperimentSharedPorts))
|
||||
ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
|
||||
Options: &coderdtest.Options{
|
||||
IncludeProvisionerDaemon: true,
|
||||
DeploymentValues: dep,
|
||||
},
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
|
|
|
@ -1898,12 +1898,8 @@ export const Entitlements: Entitlement[] = [
|
|||
];
|
||||
|
||||
// From codersdk/deployment.go
|
||||
export type Experiment = "auto-fill-parameters" | "example" | "shared-ports";
|
||||
export const Experiments: Experiment[] = [
|
||||
"auto-fill-parameters",
|
||||
"example",
|
||||
"shared-ports",
|
||||
];
|
||||
export type Experiment = "auto-fill-parameters" | "example";
|
||||
export const Experiments: Experiment[] = ["auto-fill-parameters", "example"];
|
||||
|
||||
// From codersdk/deployment.go
|
||||
export type FeatureName =
|
||||
|
|
|
@ -65,7 +65,7 @@ export interface PortForwardButtonProps {
|
|||
|
||||
export const PortForwardButton: FC<PortForwardButtonProps> = (props) => {
|
||||
const { agent } = props;
|
||||
const { entitlements, experiments } = useDashboard();
|
||||
const { entitlements } = useDashboard();
|
||||
const paper = useClassName(classNames.paper, []);
|
||||
|
||||
const portsQuery = useQuery({
|
||||
|
@ -103,7 +103,6 @@ export const PortForwardButton: FC<PortForwardButtonProps> = (props) => {
|
|||
<PortForwardPopoverView
|
||||
{...props}
|
||||
listeningPorts={portsQuery.data?.ports}
|
||||
portSharingExperimentEnabled={experiments.includes("shared-ports")}
|
||||
portSharingControlsEnabled={
|
||||
entitlements.features.control_shared_ports.enabled
|
||||
}
|
||||
|
@ -121,7 +120,6 @@ const getValidationSchema = (): Yup.AnyObjectSchema =>
|
|||
|
||||
interface PortForwardPopoverViewProps extends PortForwardButtonProps {
|
||||
listeningPorts?: readonly WorkspaceAgentListeningPort[];
|
||||
portSharingExperimentEnabled: boolean;
|
||||
portSharingControlsEnabled: boolean;
|
||||
}
|
||||
|
||||
|
@ -135,7 +133,6 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
|
|||
template,
|
||||
username,
|
||||
listeningPorts,
|
||||
portSharingExperimentEnabled,
|
||||
portSharingControlsEnabled,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
@ -200,9 +197,9 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
|
|||
filteredSharedPorts.every((sharedPort) => sharedPort.port !== port.port),
|
||||
);
|
||||
// only disable the form if shared port controls are entitled and the template doesn't allow sharing ports
|
||||
const canSharePorts =
|
||||
portSharingExperimentEnabled &&
|
||||
!(portSharingControlsEnabled && template.max_port_share_level === "owner");
|
||||
const canSharePorts = !(
|
||||
portSharingControlsEnabled && template.max_port_share_level === "owner"
|
||||
);
|
||||
const canSharePortsPublic =
|
||||
canSharePorts && template.max_port_share_level === "public";
|
||||
|
||||
|
@ -396,183 +393,179 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
|
|||
})}
|
||||
</Stack>
|
||||
</div>
|
||||
{portSharingExperimentEnabled && (
|
||||
<div
|
||||
css={{
|
||||
padding: 20,
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
}}
|
||||
>
|
||||
<HelpTooltipTitle>Shared Ports</HelpTooltipTitle>
|
||||
<HelpTooltipText css={{ color: theme.palette.text.secondary }}>
|
||||
{canSharePorts
|
||||
? "Ports can be shared with other Coder users or with the public. Changing the protocol may take up to 1 minute to take effect."
|
||||
: "This workspace template does not allow sharing ports. Contact a template administrator to enable port sharing."}
|
||||
</HelpTooltipText>
|
||||
{canSharePorts && (
|
||||
<div>
|
||||
{filteredSharedPorts?.map((share) => {
|
||||
const url = portForwardURL(
|
||||
host,
|
||||
share.port,
|
||||
agent.name,
|
||||
workspaceName,
|
||||
username,
|
||||
share.protocol,
|
||||
);
|
||||
const label = share.port;
|
||||
return (
|
||||
<Stack
|
||||
key={share.port}
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
<div
|
||||
css={{
|
||||
padding: 20,
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
}}
|
||||
>
|
||||
<HelpTooltipTitle>Shared Ports</HelpTooltipTitle>
|
||||
<HelpTooltipText css={{ color: theme.palette.text.secondary }}>
|
||||
{canSharePorts
|
||||
? "Ports can be shared with other Coder users or with the public. Changing the protocol may take up to 1 minute to take effect."
|
||||
: "This workspace template does not allow sharing ports. Contact a template administrator to enable port sharing."}
|
||||
</HelpTooltipText>
|
||||
{canSharePorts && (
|
||||
<div>
|
||||
{filteredSharedPorts?.map((share) => {
|
||||
const url = portForwardURL(
|
||||
host,
|
||||
share.port,
|
||||
agent.name,
|
||||
workspaceName,
|
||||
username,
|
||||
share.protocol,
|
||||
);
|
||||
const label = share.port;
|
||||
return (
|
||||
<Stack
|
||||
key={share.port}
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<Link
|
||||
underline="none"
|
||||
css={styles.portLink}
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Link
|
||||
underline="none"
|
||||
css={styles.portLink}
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
{share.share_level === "public" ? (
|
||||
<LockOpenIcon css={{ width: 14, height: 14 }} />
|
||||
) : (
|
||||
<LockIcon css={{ width: 14, height: 14 }} />
|
||||
)}
|
||||
{label}
|
||||
</Link>
|
||||
<FormControl size="small" css={styles.protocolFormControl}>
|
||||
<Select
|
||||
css={styles.shareLevelSelect}
|
||||
value={share.protocol}
|
||||
onChange={async (event) => {
|
||||
await upsertSharedPortMutation.mutateAsync({
|
||||
agent_name: agent.name,
|
||||
port: share.port,
|
||||
protocol: event.target
|
||||
.value as WorkspaceAgentPortShareProtocol,
|
||||
share_level: share.share_level,
|
||||
});
|
||||
await sharedPortsQuery.refetch();
|
||||
}}
|
||||
>
|
||||
<MenuItem value="http">HTTP</MenuItem>
|
||||
<MenuItem value="https">HTTPS</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<Stack direction="row" justifyContent="flex-end">
|
||||
<FormControl
|
||||
size="small"
|
||||
css={styles.shareLevelFormControl}
|
||||
>
|
||||
{share.share_level === "public" ? (
|
||||
<LockOpenIcon css={{ width: 14, height: 14 }} />
|
||||
) : (
|
||||
<LockIcon css={{ width: 14, height: 14 }} />
|
||||
)}
|
||||
{label}
|
||||
</Link>
|
||||
<FormControl size="small" css={styles.protocolFormControl}>
|
||||
<Select
|
||||
css={styles.shareLevelSelect}
|
||||
value={share.protocol}
|
||||
value={share.share_level}
|
||||
onChange={async (event) => {
|
||||
await upsertSharedPortMutation.mutateAsync({
|
||||
agent_name: agent.name,
|
||||
port: share.port,
|
||||
protocol: event.target
|
||||
.value as WorkspaceAgentPortShareProtocol,
|
||||
share_level: share.share_level,
|
||||
protocol: share.protocol,
|
||||
share_level: event.target
|
||||
.value as WorkspaceAgentPortShareLevel,
|
||||
});
|
||||
await sharedPortsQuery.refetch();
|
||||
}}
|
||||
>
|
||||
<MenuItem value="http">HTTP</MenuItem>
|
||||
<MenuItem value="https">HTTPS</MenuItem>
|
||||
<MenuItem value="authenticated">Authenticated</MenuItem>
|
||||
{canSharePortsPublic ? (
|
||||
<MenuItem value="public">Public</MenuItem>
|
||||
) : (
|
||||
disabledPublicMenuItem
|
||||
)}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<Stack direction="row" justifyContent="flex-end">
|
||||
<FormControl
|
||||
size="small"
|
||||
css={styles.shareLevelFormControl}
|
||||
>
|
||||
<Select
|
||||
css={styles.shareLevelSelect}
|
||||
value={share.share_level}
|
||||
onChange={async (event) => {
|
||||
await upsertSharedPortMutation.mutateAsync({
|
||||
agent_name: agent.name,
|
||||
port: share.port,
|
||||
protocol: share.protocol,
|
||||
share_level: event.target
|
||||
.value as WorkspaceAgentPortShareLevel,
|
||||
});
|
||||
await sharedPortsQuery.refetch();
|
||||
}}
|
||||
>
|
||||
<MenuItem value="authenticated">
|
||||
Authenticated
|
||||
</MenuItem>
|
||||
{canSharePortsPublic ? (
|
||||
<MenuItem value="public">Public</MenuItem>
|
||||
) : (
|
||||
disabledPublicMenuItem
|
||||
)}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<Button
|
||||
size="small"
|
||||
variant="text"
|
||||
css={styles.deleteButton}
|
||||
onClick={async () => {
|
||||
await deleteSharedPortMutation.mutateAsync({
|
||||
agent_name: agent.name,
|
||||
port: share.port,
|
||||
});
|
||||
await sharedPortsQuery.refetch();
|
||||
<Button
|
||||
size="small"
|
||||
variant="text"
|
||||
css={styles.deleteButton}
|
||||
onClick={async () => {
|
||||
await deleteSharedPortMutation.mutateAsync({
|
||||
agent_name: agent.name,
|
||||
port: share.port,
|
||||
});
|
||||
await sharedPortsQuery.refetch();
|
||||
}}
|
||||
>
|
||||
<CloseIcon
|
||||
css={{
|
||||
width: 14,
|
||||
height: 14,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
>
|
||||
<CloseIcon
|
||||
css={{
|
||||
width: 14,
|
||||
height: 14,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
</Stack>
|
||||
/>
|
||||
</Button>
|
||||
</Stack>
|
||||
);
|
||||
})}
|
||||
<form onSubmit={form.handleSubmit}>
|
||||
<Stack
|
||||
direction="column"
|
||||
gap={2}
|
||||
justifyContent="flex-end"
|
||||
sx={{
|
||||
marginTop: 2,
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
{...getFieldHelpers("port")}
|
||||
disabled={isSubmitting}
|
||||
label="Port"
|
||||
size="small"
|
||||
variant="outlined"
|
||||
type="number"
|
||||
value={form.values.port}
|
||||
/>
|
||||
<TextField
|
||||
{...getFieldHelpers("protocol")}
|
||||
disabled={isSubmitting}
|
||||
fullWidth
|
||||
select
|
||||
value={form.values.protocol}
|
||||
label="Protocol"
|
||||
>
|
||||
<MenuItem value="http">HTTP</MenuItem>
|
||||
<MenuItem value="https">HTTPS</MenuItem>
|
||||
</TextField>
|
||||
<TextField
|
||||
{...getFieldHelpers("share_level")}
|
||||
disabled={isSubmitting}
|
||||
fullWidth
|
||||
select
|
||||
value={form.values.share_level}
|
||||
label="Sharing Level"
|
||||
>
|
||||
<MenuItem value="authenticated">Authenticated</MenuItem>
|
||||
{canSharePortsPublic ? (
|
||||
<MenuItem value="public">Public</MenuItem>
|
||||
) : (
|
||||
disabledPublicMenuItem
|
||||
)}
|
||||
</TextField>
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
type="submit"
|
||||
loading={isSubmitting}
|
||||
disabled={!form.isValid}
|
||||
>
|
||||
Share Port
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
);
|
||||
})}
|
||||
<form onSubmit={form.handleSubmit}>
|
||||
<Stack
|
||||
direction="column"
|
||||
gap={2}
|
||||
justifyContent="flex-end"
|
||||
sx={{
|
||||
marginTop: 2,
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
{...getFieldHelpers("port")}
|
||||
disabled={isSubmitting}
|
||||
label="Port"
|
||||
size="small"
|
||||
variant="outlined"
|
||||
type="number"
|
||||
value={form.values.port}
|
||||
/>
|
||||
<TextField
|
||||
{...getFieldHelpers("protocol")}
|
||||
disabled={isSubmitting}
|
||||
fullWidth
|
||||
select
|
||||
value={form.values.protocol}
|
||||
label="Protocol"
|
||||
>
|
||||
<MenuItem value="http">HTTP</MenuItem>
|
||||
<MenuItem value="https">HTTPS</MenuItem>
|
||||
</TextField>
|
||||
<TextField
|
||||
{...getFieldHelpers("share_level")}
|
||||
disabled={isSubmitting}
|
||||
fullWidth
|
||||
select
|
||||
value={form.values.share_level}
|
||||
label="Sharing Level"
|
||||
>
|
||||
<MenuItem value="authenticated">Authenticated</MenuItem>
|
||||
{canSharePortsPublic ? (
|
||||
<MenuItem value="public">Public</MenuItem>
|
||||
) : (
|
||||
disabledPublicMenuItem
|
||||
)}
|
||||
</TextField>
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
type="submit"
|
||||
loading={isSubmitting}
|
||||
disabled={!form.isValid}
|
||||
>
|
||||
Share Port
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -29,7 +29,6 @@ const meta: Meta<typeof PortForwardPopoverView> = {
|
|||
agent: MockWorkspaceAgent,
|
||||
template: MockTemplate,
|
||||
workspaceID: MockWorkspace.id,
|
||||
portSharingExperimentEnabled: true,
|
||||
portSharingControlsEnabled: true,
|
||||
},
|
||||
};
|
||||
|
@ -83,13 +82,6 @@ export const Empty: Story = {
|
|||
},
|
||||
};
|
||||
|
||||
export const NoPortSharingExperiment: Story = {
|
||||
args: {
|
||||
listeningPorts: MockListeningPortsResponse.ports,
|
||||
portSharingExperimentEnabled: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const AGPLPortSharing: Story = {
|
||||
args: {
|
||||
listeningPorts: MockListeningPortsResponse.ports,
|
||||
|
|
|
@ -21,7 +21,6 @@ describe("Port Forward Popover View", () => {
|
|||
template={MockTemplate}
|
||||
workspaceID={MockWorkspace.id}
|
||||
listeningPorts={MockListeningPortsResponse.ports}
|
||||
portSharingExperimentEnabled
|
||||
portSharingControlsEnabled
|
||||
host="host"
|
||||
username="username"
|
||||
|
|
|
@ -61,7 +61,6 @@ export interface TemplateSettingsForm {
|
|||
initialTouched?: FormikTouched<UpdateTemplateMeta>;
|
||||
accessControlEnabled: boolean;
|
||||
advancedSchedulingEnabled: boolean;
|
||||
portSharingExperimentEnabled: boolean;
|
||||
portSharingControlsEnabled: boolean;
|
||||
}
|
||||
|
||||
|
@ -74,7 +73,6 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
|||
initialTouched,
|
||||
accessControlEnabled,
|
||||
advancedSchedulingEnabled,
|
||||
portSharingExperimentEnabled,
|
||||
portSharingControlsEnabled,
|
||||
}) => {
|
||||
const validationSchema = getValidationSchema();
|
||||
|
@ -258,45 +256,43 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
|||
</FormFields>
|
||||
</FormSection>
|
||||
|
||||
{portSharingExperimentEnabled && (
|
||||
<FormSection
|
||||
title="Port Sharing"
|
||||
description="Shared ports with the Public sharing level can be accessed by anyone,
|
||||
<FormSection
|
||||
title="Port Sharing"
|
||||
description="Shared ports with the Public sharing level can be accessed by anyone,
|
||||
while ports with the Authenticated sharing level can only be accessed
|
||||
by authenticated Coder users. Ports with the Owner sharing level can
|
||||
only be accessed by the workspace owner."
|
||||
>
|
||||
<FormFields>
|
||||
<TextField
|
||||
{...getFieldHelpers("max_port_share_level", {
|
||||
helperText:
|
||||
"The maximum level of port sharing allowed for workspaces.",
|
||||
})}
|
||||
disabled={isSubmitting || !portSharingControlsEnabled}
|
||||
fullWidth
|
||||
select
|
||||
value={
|
||||
portSharingControlsEnabled
|
||||
? form.values.max_port_share_level
|
||||
: "public"
|
||||
}
|
||||
label="Maximum Port Sharing Level"
|
||||
>
|
||||
<MenuItem value="owner">Owner</MenuItem>
|
||||
<MenuItem value="authenticated">Authenticated</MenuItem>
|
||||
<MenuItem value="public">Public</MenuItem>
|
||||
</TextField>
|
||||
{!portSharingControlsEnabled && (
|
||||
<Stack direction="row" spacing={2} alignItems="center">
|
||||
<EnterpriseBadge />
|
||||
<FormHelperText>
|
||||
Enterprise license required to control max port sharing level.
|
||||
</FormHelperText>
|
||||
</Stack>
|
||||
)}
|
||||
</FormFields>
|
||||
</FormSection>
|
||||
)}
|
||||
>
|
||||
<FormFields>
|
||||
<TextField
|
||||
{...getFieldHelpers("max_port_share_level", {
|
||||
helperText:
|
||||
"The maximum level of port sharing allowed for workspaces.",
|
||||
})}
|
||||
disabled={isSubmitting || !portSharingControlsEnabled}
|
||||
fullWidth
|
||||
select
|
||||
value={
|
||||
portSharingControlsEnabled
|
||||
? form.values.max_port_share_level
|
||||
: "public"
|
||||
}
|
||||
label="Maximum Port Sharing Level"
|
||||
>
|
||||
<MenuItem value="owner">Owner</MenuItem>
|
||||
<MenuItem value="authenticated">Authenticated</MenuItem>
|
||||
<MenuItem value="public">Public</MenuItem>
|
||||
</TextField>
|
||||
{!portSharingControlsEnabled && (
|
||||
<Stack direction="row" spacing={2} alignItems="center">
|
||||
<EnterpriseBadge />
|
||||
<FormHelperText>
|
||||
Enterprise license required to control max port sharing level.
|
||||
</FormHelperText>
|
||||
</Stack>
|
||||
)}
|
||||
</FormFields>
|
||||
</FormSection>
|
||||
|
||||
<FormFooter onCancel={onCancel} isLoading={isSubmitting} />
|
||||
</HorizontalForm>
|
||||
|
|
|
@ -18,11 +18,10 @@ export const TemplateSettingsPage: FC = () => {
|
|||
const { organizationId } = useAuthenticated();
|
||||
const { template } = useTemplateSettings();
|
||||
const queryClient = useQueryClient();
|
||||
const { entitlements, experiments } = useDashboard();
|
||||
const { entitlements } = useDashboard();
|
||||
const accessControlEnabled = entitlements.features.access_control.enabled;
|
||||
const advancedSchedulingEnabled =
|
||||
entitlements.features.advanced_template_scheduling.enabled;
|
||||
const sharedPortsExperimentEnabled = experiments.includes("shared-ports");
|
||||
const sharedPortControlsEnabled =
|
||||
entitlements.features.control_shared_ports.enabled;
|
||||
|
||||
|
@ -73,7 +72,6 @@ export const TemplateSettingsPage: FC = () => {
|
|||
}}
|
||||
accessControlEnabled={accessControlEnabled}
|
||||
advancedSchedulingEnabled={advancedSchedulingEnabled}
|
||||
sharedPortsExperimentEnabled={sharedPortsExperimentEnabled}
|
||||
sharedPortControlsEnabled={sharedPortControlsEnabled}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -14,7 +14,6 @@ export interface TemplateSettingsPageViewProps {
|
|||
>["initialTouched"];
|
||||
accessControlEnabled: boolean;
|
||||
advancedSchedulingEnabled: boolean;
|
||||
sharedPortsExperimentEnabled: boolean;
|
||||
sharedPortControlsEnabled: boolean;
|
||||
}
|
||||
|
||||
|
@ -27,7 +26,6 @@ export const TemplateSettingsPageView: FC<TemplateSettingsPageViewProps> = ({
|
|||
initialTouched,
|
||||
accessControlEnabled,
|
||||
advancedSchedulingEnabled,
|
||||
sharedPortsExperimentEnabled,
|
||||
sharedPortControlsEnabled,
|
||||
}) => {
|
||||
return (
|
||||
|
@ -45,7 +43,6 @@ export const TemplateSettingsPageView: FC<TemplateSettingsPageViewProps> = ({
|
|||
error={submitError}
|
||||
accessControlEnabled={accessControlEnabled}
|
||||
advancedSchedulingEnabled={advancedSchedulingEnabled}
|
||||
portSharingExperimentEnabled={sharedPortsExperimentEnabled}
|
||||
portSharingControlsEnabled={sharedPortControlsEnabled}
|
||||
/>
|
||||
</>
|
||||
|
|
Loading…
Reference in New Issue