mirror of https://github.com/coder/coder.git
feat: allow setting port share protocol (#12383)
Co-authored-by: Garrett Delfosse <garrett@coder.com>
This commit is contained in:
parent
23ff807a27
commit
46a2ff1061
|
@ -12619,8 +12619,28 @@ const docTemplate = `{
|
|||
"port": {
|
||||
"type": "integer"
|
||||
},
|
||||
"protocol": {
|
||||
"enum": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareProtocol"
|
||||
}
|
||||
]
|
||||
},
|
||||
"share_level": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareLevel"
|
||||
"enum": [
|
||||
"owner",
|
||||
"authenticated",
|
||||
"public"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareLevel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -13366,8 +13386,28 @@ const docTemplate = `{
|
|||
"port": {
|
||||
"type": "integer"
|
||||
},
|
||||
"protocol": {
|
||||
"enum": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareProtocol"
|
||||
}
|
||||
]
|
||||
},
|
||||
"share_level": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareLevel"
|
||||
"enum": [
|
||||
"owner",
|
||||
"authenticated",
|
||||
"public"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareLevel"
|
||||
}
|
||||
]
|
||||
},
|
||||
"workspace_id": {
|
||||
"type": "string",
|
||||
|
@ -13388,6 +13428,17 @@ const docTemplate = `{
|
|||
"WorkspaceAgentPortShareLevelPublic"
|
||||
]
|
||||
},
|
||||
"codersdk.WorkspaceAgentPortShareProtocol": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"WorkspaceAgentPortShareProtocolHTTP",
|
||||
"WorkspaceAgentPortShareProtocolHTTPS"
|
||||
]
|
||||
},
|
||||
"codersdk.WorkspaceAgentPortShares": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -11442,8 +11442,21 @@
|
|||
"port": {
|
||||
"type": "integer"
|
||||
},
|
||||
"protocol": {
|
||||
"enum": ["http", "https"],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareProtocol"
|
||||
}
|
||||
]
|
||||
},
|
||||
"share_level": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareLevel"
|
||||
"enum": ["owner", "authenticated", "public"],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareLevel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -12164,8 +12177,21 @@
|
|||
"port": {
|
||||
"type": "integer"
|
||||
},
|
||||
"protocol": {
|
||||
"enum": ["http", "https"],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareProtocol"
|
||||
}
|
||||
]
|
||||
},
|
||||
"share_level": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareLevel"
|
||||
"enum": ["owner", "authenticated", "public"],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareLevel"
|
||||
}
|
||||
]
|
||||
},
|
||||
"workspace_id": {
|
||||
"type": "string",
|
||||
|
@ -12182,6 +12208,14 @@
|
|||
"WorkspaceAgentPortShareLevelPublic"
|
||||
]
|
||||
},
|
||||
"codersdk.WorkspaceAgentPortShareProtocol": {
|
||||
"type": "string",
|
||||
"enum": ["http", "https"],
|
||||
"x-enum-varnames": [
|
||||
"WorkspaceAgentPortShareProtocolHTTP",
|
||||
"WorkspaceAgentPortShareProtocolHTTPS"
|
||||
]
|
||||
},
|
||||
"codersdk.WorkspaceAgentPortShares": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -1608,6 +1608,7 @@ func (s *MethodTestSuite) TestWorkspacePortSharing() {
|
|||
AgentName: ps.AgentName,
|
||||
Port: ps.Port,
|
||||
ShareLevel: ps.ShareLevel,
|
||||
Protocol: ps.Protocol,
|
||||
}).Asserts(ws, rbac.ActionUpdate).Returns(ps)
|
||||
}))
|
||||
s.Run("GetWorkspaceAgentPortShare", s.Subtest(func(db database.Store, check *expects) {
|
||||
|
|
|
@ -140,6 +140,7 @@ func WorkspaceAgentPortShare(t testing.TB, db database.Store, orig database.Work
|
|||
AgentName: takeFirst(orig.AgentName, namesgenerator.GetRandomName(1)),
|
||||
Port: takeFirst(orig.Port, 8080),
|
||||
ShareLevel: takeFirst(orig.ShareLevel, database.AppSharingLevelPublic),
|
||||
Protocol: takeFirst(orig.Protocol, database.PortShareProtocolHttp),
|
||||
})
|
||||
require.NoError(t, err, "insert workspace agent")
|
||||
return ps
|
||||
|
|
|
@ -86,7 +86,7 @@ func New() database.Store {
|
|||
UpdatedAt: dbtime.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to create default organization: %w", err))
|
||||
panic(xerrors.Errorf("failed to create default organization: %w", err))
|
||||
}
|
||||
q.defaultProxyDisplayName = "Default"
|
||||
q.defaultProxyIconURL = "/emojis/1f3e1.png"
|
||||
|
@ -7933,6 +7933,7 @@ func (q *FakeQuerier) UpsertWorkspaceAgentPortShare(_ context.Context, arg datab
|
|||
for i, share := range q.workspaceAgentPortShares {
|
||||
if share.WorkspaceID == arg.WorkspaceID && share.Port == arg.Port && share.AgentName == arg.AgentName {
|
||||
share.ShareLevel = arg.ShareLevel
|
||||
share.Protocol = arg.Protocol
|
||||
q.workspaceAgentPortShares[i] = share
|
||||
return share, nil
|
||||
}
|
||||
|
@ -7944,6 +7945,7 @@ func (q *FakeQuerier) UpsertWorkspaceAgentPortShare(_ context.Context, arg datab
|
|||
AgentName: arg.AgentName,
|
||||
Port: arg.Port,
|
||||
ShareLevel: arg.ShareLevel,
|
||||
Protocol: arg.Protocol,
|
||||
}
|
||||
q.workspaceAgentPortShares = append(q.workspaceAgentPortShares, psl)
|
||||
|
||||
|
|
|
@ -95,6 +95,11 @@ CREATE TYPE parameter_type_system AS ENUM (
|
|||
'hcl'
|
||||
);
|
||||
|
||||
CREATE TYPE port_share_protocol AS ENUM (
|
||||
'http',
|
||||
'https'
|
||||
);
|
||||
|
||||
CREATE TYPE provisioner_job_status AS ENUM (
|
||||
'pending',
|
||||
'running',
|
||||
|
@ -1027,7 +1032,8 @@ CREATE TABLE workspace_agent_port_share (
|
|||
workspace_id uuid NOT NULL,
|
||||
agent_name text NOT NULL,
|
||||
port integer NOT NULL,
|
||||
share_level app_sharing_level NOT NULL
|
||||
share_level app_sharing_level NOT NULL,
|
||||
protocol port_share_protocol DEFAULT 'http'::port_share_protocol NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE workspace_agent_scripts (
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE workspace_agent_port_share DROP COLUMN protocol;
|
||||
|
||||
DROP TYPE port_share_protocol;
|
|
@ -0,0 +1,4 @@
|
|||
CREATE TYPE port_share_protocol AS ENUM ('http', 'https');
|
||||
|
||||
ALTER TABLE workspace_agent_port_share
|
||||
ADD COLUMN protocol port_share_protocol NOT NULL DEFAULT 'http'::port_share_protocol;
|
|
@ -898,6 +898,64 @@ func AllParameterTypeSystemValues() []ParameterTypeSystem {
|
|||
}
|
||||
}
|
||||
|
||||
type PortShareProtocol string
|
||||
|
||||
const (
|
||||
PortShareProtocolHttp PortShareProtocol = "http"
|
||||
PortShareProtocolHttps PortShareProtocol = "https"
|
||||
)
|
||||
|
||||
func (e *PortShareProtocol) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = PortShareProtocol(s)
|
||||
case string:
|
||||
*e = PortShareProtocol(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for PortShareProtocol: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullPortShareProtocol struct {
|
||||
PortShareProtocol PortShareProtocol `json:"port_share_protocol"`
|
||||
Valid bool `json:"valid"` // Valid is true if PortShareProtocol is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullPortShareProtocol) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.PortShareProtocol, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.PortShareProtocol.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullPortShareProtocol) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.PortShareProtocol), nil
|
||||
}
|
||||
|
||||
func (e PortShareProtocol) Valid() bool {
|
||||
switch e {
|
||||
case PortShareProtocolHttp,
|
||||
PortShareProtocolHttps:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AllPortShareProtocolValues() []PortShareProtocol {
|
||||
return []PortShareProtocol{
|
||||
PortShareProtocolHttp,
|
||||
PortShareProtocolHttps,
|
||||
}
|
||||
}
|
||||
|
||||
// Computed status of a provisioner job. Jobs could be stuck in a hung state, these states do not guarantee any transition to another state.
|
||||
type ProvisionerJobStatus string
|
||||
|
||||
|
@ -2312,10 +2370,11 @@ type WorkspaceAgentMetadatum struct {
|
|||
}
|
||||
|
||||
type WorkspaceAgentPortShare struct {
|
||||
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
||||
AgentName string `db:"agent_name" json:"agent_name"`
|
||||
Port int32 `db:"port" json:"port"`
|
||||
ShareLevel AppSharingLevel `db:"share_level" json:"share_level"`
|
||||
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
||||
AgentName string `db:"agent_name" json:"agent_name"`
|
||||
Port int32 `db:"port" json:"port"`
|
||||
ShareLevel AppSharingLevel `db:"share_level" json:"share_level"`
|
||||
Protocol PortShareProtocol `db:"protocol" json:"protocol"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentScript struct {
|
||||
|
|
|
@ -8469,7 +8469,12 @@ func (q *sqlQuerier) UpdateUserStatus(ctx context.Context, arg UpdateUserStatusP
|
|||
}
|
||||
|
||||
const deleteWorkspaceAgentPortShare = `-- name: DeleteWorkspaceAgentPortShare :exec
|
||||
DELETE FROM workspace_agent_port_share WHERE workspace_id = $1 AND agent_name = $2 AND port = $3
|
||||
DELETE FROM
|
||||
workspace_agent_port_share
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
AND agent_name = $2
|
||||
AND port = $3
|
||||
`
|
||||
|
||||
type DeleteWorkspaceAgentPortShareParams struct {
|
||||
|
@ -8484,7 +8489,17 @@ func (q *sqlQuerier) DeleteWorkspaceAgentPortShare(ctx context.Context, arg Dele
|
|||
}
|
||||
|
||||
const deleteWorkspaceAgentPortSharesByTemplate = `-- name: DeleteWorkspaceAgentPortSharesByTemplate :exec
|
||||
DELETE FROM workspace_agent_port_share WHERE workspace_id IN (SELECT id FROM workspaces WHERE template_id = $1)
|
||||
DELETE FROM
|
||||
workspace_agent_port_share
|
||||
WHERE
|
||||
workspace_id IN (
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
workspaces
|
||||
WHERE
|
||||
template_id = $1
|
||||
)
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) DeleteWorkspaceAgentPortSharesByTemplate(ctx context.Context, templateID uuid.UUID) error {
|
||||
|
@ -8493,7 +8508,14 @@ func (q *sqlQuerier) DeleteWorkspaceAgentPortSharesByTemplate(ctx context.Contex
|
|||
}
|
||||
|
||||
const getWorkspaceAgentPortShare = `-- name: GetWorkspaceAgentPortShare :one
|
||||
SELECT workspace_id, agent_name, port, share_level FROM workspace_agent_port_share WHERE workspace_id = $1 AND agent_name = $2 AND port = $3
|
||||
SELECT
|
||||
workspace_id, agent_name, port, share_level, protocol
|
||||
FROM
|
||||
workspace_agent_port_share
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
AND agent_name = $2
|
||||
AND port = $3
|
||||
`
|
||||
|
||||
type GetWorkspaceAgentPortShareParams struct {
|
||||
|
@ -8510,12 +8532,18 @@ func (q *sqlQuerier) GetWorkspaceAgentPortShare(ctx context.Context, arg GetWork
|
|||
&i.AgentName,
|
||||
&i.Port,
|
||||
&i.ShareLevel,
|
||||
&i.Protocol,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listWorkspaceAgentPortShares = `-- name: ListWorkspaceAgentPortShares :many
|
||||
SELECT workspace_id, agent_name, port, share_level FROM workspace_agent_port_share WHERE workspace_id = $1
|
||||
SELECT
|
||||
workspace_id, agent_name, port, share_level, protocol
|
||||
FROM
|
||||
workspace_agent_port_share
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) ListWorkspaceAgentPortShares(ctx context.Context, workspaceID uuid.UUID) ([]WorkspaceAgentPortShare, error) {
|
||||
|
@ -8532,6 +8560,7 @@ func (q *sqlQuerier) ListWorkspaceAgentPortShares(ctx context.Context, workspace
|
|||
&i.AgentName,
|
||||
&i.Port,
|
||||
&i.ShareLevel,
|
||||
&i.Protocol,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -8547,7 +8576,20 @@ func (q *sqlQuerier) ListWorkspaceAgentPortShares(ctx context.Context, workspace
|
|||
}
|
||||
|
||||
const reduceWorkspaceAgentShareLevelToAuthenticatedByTemplate = `-- name: ReduceWorkspaceAgentShareLevelToAuthenticatedByTemplate :exec
|
||||
UPDATE workspace_agent_port_share SET share_level = 'authenticated' WHERE share_level = 'public' AND workspace_id IN (SELECT id FROM workspaces WHERE template_id = $1)
|
||||
UPDATE
|
||||
workspace_agent_port_share
|
||||
SET
|
||||
share_level = 'authenticated'
|
||||
WHERE
|
||||
share_level = 'public'
|
||||
AND workspace_id IN (
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
workspaces
|
||||
WHERE
|
||||
template_id = $1
|
||||
)
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) ReduceWorkspaceAgentShareLevelToAuthenticatedByTemplate(ctx context.Context, templateID uuid.UUID) error {
|
||||
|
@ -8556,16 +8598,38 @@ func (q *sqlQuerier) ReduceWorkspaceAgentShareLevelToAuthenticatedByTemplate(ctx
|
|||
}
|
||||
|
||||
const upsertWorkspaceAgentPortShare = `-- name: UpsertWorkspaceAgentPortShare :one
|
||||
INSERT INTO workspace_agent_port_share (workspace_id, agent_name, port, share_level)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (workspace_id, agent_name, port) DO UPDATE SET share_level = $4 RETURNING workspace_id, agent_name, port, share_level
|
||||
INSERT INTO
|
||||
workspace_agent_port_share (
|
||||
workspace_id,
|
||||
agent_name,
|
||||
port,
|
||||
share_level,
|
||||
protocol
|
||||
)
|
||||
VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5
|
||||
)
|
||||
ON CONFLICT (
|
||||
workspace_id,
|
||||
agent_name,
|
||||
port
|
||||
)
|
||||
DO UPDATE SET
|
||||
share_level = $4,
|
||||
protocol = $5
|
||||
RETURNING workspace_id, agent_name, port, share_level, protocol
|
||||
`
|
||||
|
||||
type UpsertWorkspaceAgentPortShareParams struct {
|
||||
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
||||
AgentName string `db:"agent_name" json:"agent_name"`
|
||||
Port int32 `db:"port" json:"port"`
|
||||
ShareLevel AppSharingLevel `db:"share_level" json:"share_level"`
|
||||
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
||||
AgentName string `db:"agent_name" json:"agent_name"`
|
||||
Port int32 `db:"port" json:"port"`
|
||||
ShareLevel AppSharingLevel `db:"share_level" json:"share_level"`
|
||||
Protocol PortShareProtocol `db:"protocol" json:"protocol"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpsertWorkspaceAgentPortShare(ctx context.Context, arg UpsertWorkspaceAgentPortShareParams) (WorkspaceAgentPortShare, error) {
|
||||
|
@ -8574,6 +8638,7 @@ func (q *sqlQuerier) UpsertWorkspaceAgentPortShare(ctx context.Context, arg Upse
|
|||
arg.AgentName,
|
||||
arg.Port,
|
||||
arg.ShareLevel,
|
||||
arg.Protocol,
|
||||
)
|
||||
var i WorkspaceAgentPortShare
|
||||
err := row.Scan(
|
||||
|
@ -8581,6 +8646,7 @@ func (q *sqlQuerier) UpsertWorkspaceAgentPortShare(ctx context.Context, arg Upse
|
|||
&i.AgentName,
|
||||
&i.Port,
|
||||
&i.ShareLevel,
|
||||
&i.Protocol,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
|
@ -1,19 +1,80 @@
|
|||
-- name: GetWorkspaceAgentPortShare :one
|
||||
SELECT * FROM workspace_agent_port_share WHERE workspace_id = $1 AND agent_name = $2 AND port = $3;
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
workspace_agent_port_share
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
AND agent_name = $2
|
||||
AND port = $3;
|
||||
|
||||
-- name: ListWorkspaceAgentPortShares :many
|
||||
SELECT * FROM workspace_agent_port_share WHERE workspace_id = $1;
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
workspace_agent_port_share
|
||||
WHERE
|
||||
workspace_id = $1;
|
||||
|
||||
-- name: DeleteWorkspaceAgentPortShare :exec
|
||||
DELETE FROM workspace_agent_port_share WHERE workspace_id = $1 AND agent_name = $2 AND port = $3;
|
||||
DELETE FROM
|
||||
workspace_agent_port_share
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
AND agent_name = $2
|
||||
AND port = $3;
|
||||
|
||||
-- name: UpsertWorkspaceAgentPortShare :one
|
||||
INSERT INTO workspace_agent_port_share (workspace_id, agent_name, port, share_level)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (workspace_id, agent_name, port) DO UPDATE SET share_level = $4 RETURNING *;
|
||||
INSERT INTO
|
||||
workspace_agent_port_share (
|
||||
workspace_id,
|
||||
agent_name,
|
||||
port,
|
||||
share_level,
|
||||
protocol
|
||||
)
|
||||
VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5
|
||||
)
|
||||
ON CONFLICT (
|
||||
workspace_id,
|
||||
agent_name,
|
||||
port
|
||||
)
|
||||
DO UPDATE SET
|
||||
share_level = $4,
|
||||
protocol = $5
|
||||
RETURNING *;
|
||||
|
||||
-- name: ReduceWorkspaceAgentShareLevelToAuthenticatedByTemplate :exec
|
||||
UPDATE workspace_agent_port_share SET share_level = 'authenticated' WHERE share_level = 'public' AND workspace_id IN (SELECT id FROM workspaces WHERE template_id = $1);
|
||||
UPDATE
|
||||
workspace_agent_port_share
|
||||
SET
|
||||
share_level = 'authenticated'
|
||||
WHERE
|
||||
share_level = 'public'
|
||||
AND workspace_id IN (
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
workspaces
|
||||
WHERE
|
||||
template_id = $1
|
||||
);
|
||||
|
||||
-- name: DeleteWorkspaceAgentPortSharesByTemplate :exec
|
||||
DELETE FROM workspace_agent_port_share WHERE workspace_id IN (SELECT id FROM workspaces WHERE template_id = $1);
|
||||
DELETE FROM
|
||||
workspace_agent_port_share
|
||||
WHERE
|
||||
workspace_id IN (
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
workspaces
|
||||
WHERE
|
||||
template_id = $1
|
||||
);
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"database/sql"
|
||||
"errors"
|
||||
"net/http"
|
||||
"slices"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
|
@ -55,6 +56,12 @@ func (api *API) postWorkspaceAgentPortShare(rw http.ResponseWriter, r *http.Requ
|
|||
})
|
||||
return
|
||||
}
|
||||
if !req.Protocol.ValidPortProtocol() {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Port protocol not allowed.",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
template, err := api.Database.GetTemplateByID(ctx, workspace.TemplateID)
|
||||
if err != nil {
|
||||
|
@ -95,6 +102,7 @@ func (api *API) postWorkspaceAgentPortShare(rw http.ResponseWriter, r *http.Requ
|
|||
AgentName: req.AgentName,
|
||||
Port: req.Port,
|
||||
ShareLevel: database.AppSharingLevel(req.ShareLevel),
|
||||
Protocol: database.PortShareProtocol(req.Protocol),
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
|
@ -179,6 +187,9 @@ func convertPortShares(shares []database.WorkspaceAgentPortShare) []codersdk.Wor
|
|||
for _, share := range shares {
|
||||
converted = append(converted, convertPortShare(share))
|
||||
}
|
||||
slices.SortFunc(converted, func(i, j codersdk.WorkspaceAgentPortShare) int {
|
||||
return (int)(i.Port - j.Port)
|
||||
})
|
||||
return converted
|
||||
}
|
||||
|
||||
|
@ -188,5 +199,6 @@ func convertPortShare(share database.WorkspaceAgentPortShare) codersdk.Workspace
|
|||
AgentName: share.AgentName,
|
||||
Port: share.Port,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevel(share.ShareLevel),
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocol(share.Protocol),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ func TestPostWorkspaceAgentPortShare(t *testing.T) {
|
|||
AgentName: agents[0].Name,
|
||||
Port: 8080,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevel("owner"),
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
|
||||
})
|
||||
require.Error(t, err)
|
||||
|
||||
|
@ -51,6 +52,16 @@ func TestPostWorkspaceAgentPortShare(t *testing.T) {
|
|||
AgentName: agents[0].Name,
|
||||
Port: 8080,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevel("invalid"),
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
|
||||
})
|
||||
require.Error(t, err)
|
||||
|
||||
// invalid protocol should fail
|
||||
_, err = client.UpsertWorkspaceAgentPortShare(ctx, r.Workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
|
||||
AgentName: agents[0].Name,
|
||||
Port: 8080,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocol("invalid"),
|
||||
})
|
||||
require.Error(t, err)
|
||||
|
||||
|
@ -59,6 +70,7 @@ func TestPostWorkspaceAgentPortShare(t *testing.T) {
|
|||
AgentName: agents[0].Name,
|
||||
Port: 0,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
|
||||
})
|
||||
require.Error(t, err)
|
||||
_, err = client.UpsertWorkspaceAgentPortShare(ctx, r.Workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
|
||||
|
@ -73,18 +85,54 @@ func TestPostWorkspaceAgentPortShare(t *testing.T) {
|
|||
AgentName: agents[0].Name,
|
||||
Port: 8080,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTPS,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, codersdk.WorkspaceAgentPortShareLevelPublic, ps.ShareLevel)
|
||||
require.EqualValues(t, codersdk.WorkspaceAgentPortShareProtocolHTTPS, ps.Protocol)
|
||||
|
||||
// update share level
|
||||
// list
|
||||
list, err := client.GetWorkspaceAgentPortShares(ctx, r.Workspace.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, list.Shares, 1)
|
||||
require.EqualValues(t, agents[0].Name, list.Shares[0].AgentName)
|
||||
require.EqualValues(t, 8080, list.Shares[0].Port)
|
||||
require.EqualValues(t, codersdk.WorkspaceAgentPortShareLevelPublic, list.Shares[0].ShareLevel)
|
||||
require.EqualValues(t, codersdk.WorkspaceAgentPortShareProtocolHTTPS, list.Shares[0].Protocol)
|
||||
|
||||
// update share level and protocol
|
||||
ps, err = client.UpsertWorkspaceAgentPortShare(ctx, r.Workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
|
||||
AgentName: agents[0].Name,
|
||||
Port: 8080,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelAuthenticated,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, codersdk.WorkspaceAgentPortShareLevelAuthenticated, ps.ShareLevel)
|
||||
require.EqualValues(t, codersdk.WorkspaceAgentPortShareProtocolHTTP, ps.Protocol)
|
||||
|
||||
// list
|
||||
list, err = client.GetWorkspaceAgentPortShares(ctx, r.Workspace.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, list.Shares, 1)
|
||||
require.EqualValues(t, agents[0].Name, list.Shares[0].AgentName)
|
||||
require.EqualValues(t, 8080, list.Shares[0].Port)
|
||||
require.EqualValues(t, codersdk.WorkspaceAgentPortShareLevelAuthenticated, list.Shares[0].ShareLevel)
|
||||
require.EqualValues(t, codersdk.WorkspaceAgentPortShareProtocolHTTP, list.Shares[0].Protocol)
|
||||
|
||||
// list 2 ordered by port
|
||||
ps, err = client.UpsertWorkspaceAgentPortShare(ctx, r.Workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
|
||||
AgentName: agents[0].Name,
|
||||
Port: 8081,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTPS,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
list, err = client.GetWorkspaceAgentPortShares(ctx, r.Workspace.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, list.Shares, 2)
|
||||
require.EqualValues(t, 8080, list.Shares[0].Port)
|
||||
require.EqualValues(t, 8081, list.Shares[1].Port)
|
||||
}
|
||||
|
||||
func TestGetWorkspaceAgentPortShares(t *testing.T) {
|
||||
|
@ -115,6 +163,7 @@ func TestGetWorkspaceAgentPortShares(t *testing.T) {
|
|||
AgentName: agents[0].Name,
|
||||
Port: 8080,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -155,6 +204,7 @@ func TestDeleteWorkspaceAgentPortShare(t *testing.T) {
|
|||
AgentName: agents[0].Name,
|
||||
Port: 8080,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, codersdk.WorkspaceAgentPortShareLevelPublic, ps.ShareLevel)
|
||||
|
|
|
@ -909,79 +909,6 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
|
|||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("PortSharingNoShare", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
userClient, _ := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember())
|
||||
userAppClient := appDetails.AppClient(t)
|
||||
userAppClient.SetSessionToken(userClient.SessionToken())
|
||||
|
||||
resp, err := requestWithRetries(ctx, t, userAppClient, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.Port).String(), nil)
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
require.Equal(t, http.StatusNotFound, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("PortSharingAuthenticatedOK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
// we are shadowing the parent since we are changing the state
|
||||
appDetails := setupProxyTest(t, nil)
|
||||
|
||||
port, err := strconv.ParseInt(appDetails.Apps.Port.AppSlugOrPort, 10, 32)
|
||||
require.NoError(t, err)
|
||||
// set the port we have to be shared with authenticated users
|
||||
_, err = appDetails.SDKClient.UpsertWorkspaceAgentPortShare(ctx, appDetails.Workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
|
||||
AgentName: proxyTestAgentName,
|
||||
Port: int32(port),
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelAuthenticated,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
userClient, _ := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember())
|
||||
userAppClient := appDetails.AppClient(t)
|
||||
userAppClient.SetSessionToken(userClient.SessionToken())
|
||||
|
||||
resp, err := requestWithRetries(ctx, t, userAppClient, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.Port).String(), nil)
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("PortSharingPublicOK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
// we are shadowing the parent since we are changing the state
|
||||
appDetails := setupProxyTest(t, nil)
|
||||
|
||||
port, err := strconv.ParseInt(appDetails.Apps.Port.AppSlugOrPort, 10, 32)
|
||||
require.NoError(t, err)
|
||||
// set the port we have to be shared with public
|
||||
_, err = appDetails.SDKClient.UpsertWorkspaceAgentPortShare(ctx, appDetails.Workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
|
||||
AgentName: proxyTestAgentName,
|
||||
Port: int32(port),
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
publicAppClient := appDetails.AppClient(t)
|
||||
publicAppClient.SetSessionToken("")
|
||||
|
||||
resp, err := requestWithRetries(ctx, t, publicAppClient, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.Port).String(), nil)
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("ProxyError", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -1119,6 +1046,108 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
|
|||
})
|
||||
})
|
||||
|
||||
t.Run("PortSharing", func(t *testing.T) {
|
||||
t.Run("NoShare", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
appDetails := setupProxyTest(t, nil)
|
||||
userClient, _ := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember())
|
||||
userAppClient := appDetails.AppClient(t)
|
||||
userAppClient.SetSessionToken(userClient.SessionToken())
|
||||
|
||||
resp, err := requestWithRetries(ctx, t, userAppClient, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.Port).String(), nil)
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
require.Equal(t, http.StatusNotFound, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("AuthenticatedOK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
appDetails := setupProxyTest(t, nil)
|
||||
port, err := strconv.ParseInt(appDetails.Apps.Port.AppSlugOrPort, 10, 32)
|
||||
require.NoError(t, err)
|
||||
// set the port we have to be shared with authenticated users
|
||||
_, err = appDetails.SDKClient.UpsertWorkspaceAgentPortShare(ctx, appDetails.Workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
|
||||
AgentName: proxyTestAgentName,
|
||||
Port: int32(port),
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelAuthenticated,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
userClient, _ := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember())
|
||||
userAppClient := appDetails.AppClient(t)
|
||||
userAppClient.SetSessionToken(userClient.SessionToken())
|
||||
|
||||
resp, err := requestWithRetries(ctx, t, userAppClient, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.Port).String(), nil)
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("PublicOK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
appDetails := setupProxyTest(t, nil)
|
||||
port, err := strconv.ParseInt(appDetails.Apps.Port.AppSlugOrPort, 10, 32)
|
||||
require.NoError(t, err)
|
||||
// set the port we have to be shared with public
|
||||
_, err = appDetails.SDKClient.UpsertWorkspaceAgentPortShare(ctx, appDetails.Workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
|
||||
AgentName: proxyTestAgentName,
|
||||
Port: int32(port),
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
publicAppClient := appDetails.AppClient(t)
|
||||
publicAppClient.SetSessionToken("")
|
||||
|
||||
resp, err := requestWithRetries(ctx, t, publicAppClient, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.Port).String(), nil)
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("HTTPS", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
appDetails := setupProxyTest(t, &DeploymentOptions{
|
||||
ServeHTTPS: true,
|
||||
})
|
||||
port, err := strconv.ParseInt(appDetails.Apps.Port.AppSlugOrPort, 10, 32)
|
||||
require.NoError(t, err)
|
||||
_, err = appDetails.SDKClient.UpsertWorkspaceAgentPortShare(ctx, appDetails.Workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
|
||||
AgentName: proxyTestAgentName,
|
||||
Port: int32(port),
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTPS,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
publicAppClient := appDetails.AppClient(t)
|
||||
publicAppClient.SetSessionToken("")
|
||||
|
||||
resp, err := requestWithRetries(ctx, t, publicAppClient, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.Port).String(), nil)
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("AppSharing", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -342,6 +342,10 @@ func (r Request) getDatabase(ctx context.Context, db database.Store) (*databaseR
|
|||
}
|
||||
// No port share found, so we keep default to owner.
|
||||
} else {
|
||||
if ps.Protocol == database.PortShareProtocolHttps {
|
||||
// Apply HTTPS protocol if specified.
|
||||
appURL = fmt.Sprintf("https://127.0.0.1:%d", portUint)
|
||||
}
|
||||
appSharingLevel = ps.ShareLevel
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -269,6 +269,3 @@ func Test_RequestValidate(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getDatabase is tested heavily in auth_test.go, so we don't have specific
|
||||
// tests for it here.
|
||||
|
|
|
@ -13,23 +13,29 @@ const (
|
|||
WorkspaceAgentPortShareLevelOwner WorkspaceAgentPortShareLevel = "owner"
|
||||
WorkspaceAgentPortShareLevelAuthenticated WorkspaceAgentPortShareLevel = "authenticated"
|
||||
WorkspaceAgentPortShareLevelPublic WorkspaceAgentPortShareLevel = "public"
|
||||
|
||||
WorkspaceAgentPortShareProtocolHTTP WorkspaceAgentPortShareProtocol = "http"
|
||||
WorkspaceAgentPortShareProtocolHTTPS WorkspaceAgentPortShareProtocol = "https"
|
||||
)
|
||||
|
||||
type (
|
||||
WorkspaceAgentPortShareLevel string
|
||||
WorkspaceAgentPortShareProtocol string
|
||||
UpsertWorkspaceAgentPortShareRequest struct {
|
||||
AgentName string `json:"agent_name"`
|
||||
Port int32 `json:"port"`
|
||||
ShareLevel WorkspaceAgentPortShareLevel `json:"share_level"`
|
||||
AgentName string `json:"agent_name"`
|
||||
Port int32 `json:"port"`
|
||||
ShareLevel WorkspaceAgentPortShareLevel `json:"share_level" enums:"owner,authenticated,public"`
|
||||
Protocol WorkspaceAgentPortShareProtocol `json:"protocol" enums:"http,https"`
|
||||
}
|
||||
WorkspaceAgentPortShares struct {
|
||||
Shares []WorkspaceAgentPortShare `json:"shares"`
|
||||
}
|
||||
WorkspaceAgentPortShare struct {
|
||||
WorkspaceID uuid.UUID `json:"workspace_id" format:"uuid"`
|
||||
AgentName string `json:"agent_name"`
|
||||
Port int32 `json:"port"`
|
||||
ShareLevel WorkspaceAgentPortShareLevel `json:"share_level"`
|
||||
WorkspaceID uuid.UUID `json:"workspace_id" format:"uuid"`
|
||||
AgentName string `json:"agent_name"`
|
||||
Port int32 `json:"port"`
|
||||
ShareLevel WorkspaceAgentPortShareLevel `json:"share_level" enums:"owner,authenticated,public"`
|
||||
Protocol WorkspaceAgentPortShareProtocol `json:"protocol" enums:"http,https"`
|
||||
}
|
||||
DeleteWorkspaceAgentPortShareRequest struct {
|
||||
AgentName string `json:"agent_name"`
|
||||
|
@ -48,6 +54,11 @@ func (l WorkspaceAgentPortShareLevel) ValidPortShareLevel() bool {
|
|||
l == WorkspaceAgentPortShareLevelPublic
|
||||
}
|
||||
|
||||
func (p WorkspaceAgentPortShareProtocol) ValidPortProtocol() bool {
|
||||
return p == WorkspaceAgentPortShareProtocolHTTP ||
|
||||
p == WorkspaceAgentPortShareProtocolHTTPS
|
||||
}
|
||||
|
||||
func (c *Client) GetWorkspaceAgentPortShares(ctx context.Context, workspaceID uuid.UUID) (WorkspaceAgentPortShares, error) {
|
||||
var shares WorkspaceAgentPortShares
|
||||
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspaces/%s/port-share", workspaceID), nil)
|
||||
|
|
|
@ -57,6 +57,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/port-share \
|
|||
{
|
||||
"agent_name": "string",
|
||||
"port": 0,
|
||||
"protocol": "http",
|
||||
"share_level": "owner"
|
||||
}
|
||||
```
|
||||
|
@ -76,6 +77,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/port-share \
|
|||
{
|
||||
"agent_name": "string",
|
||||
"port": 0,
|
||||
"protocol": "http",
|
||||
"share_level": "owner",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
|
|
|
@ -6613,17 +6613,29 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
{
|
||||
"agent_name": "string",
|
||||
"port": 0,
|
||||
"protocol": "http",
|
||||
"share_level": "owner"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------- | ------------------------------------------------------------------------------ | -------- | ------------ | ----------- |
|
||||
| `agent_name` | string | false | | |
|
||||
| `port` | integer | false | | |
|
||||
| `share_level` | [codersdk.WorkspaceAgentPortShareLevel](#codersdkworkspaceagentportsharelevel) | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------- | ------------------------------------------------------------------------------------ | -------- | ------------ | ----------- |
|
||||
| `agent_name` | string | false | | |
|
||||
| `port` | integer | false | | |
|
||||
| `protocol` | [codersdk.WorkspaceAgentPortShareProtocol](#codersdkworkspaceagentportshareprotocol) | false | | |
|
||||
| `share_level` | [codersdk.WorkspaceAgentPortShareLevel](#codersdkworkspaceagentportsharelevel) | false | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Property | Value |
|
||||
| ------------- | --------------- |
|
||||
| `protocol` | `http` |
|
||||
| `protocol` | `https` |
|
||||
| `share_level` | `owner` |
|
||||
| `share_level` | `authenticated` |
|
||||
| `share_level` | `public` |
|
||||
|
||||
## codersdk.User
|
||||
|
||||
|
@ -7579,6 +7591,7 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
{
|
||||
"agent_name": "string",
|
||||
"port": 0,
|
||||
"protocol": "http",
|
||||
"share_level": "owner",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
|
@ -7586,12 +7599,23 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------- | ------------------------------------------------------------------------------ | -------- | ------------ | ----------- |
|
||||
| `agent_name` | string | false | | |
|
||||
| `port` | integer | false | | |
|
||||
| `share_level` | [codersdk.WorkspaceAgentPortShareLevel](#codersdkworkspaceagentportsharelevel) | false | | |
|
||||
| `workspace_id` | string | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------- | ------------------------------------------------------------------------------------ | -------- | ------------ | ----------- |
|
||||
| `agent_name` | string | false | | |
|
||||
| `port` | integer | false | | |
|
||||
| `protocol` | [codersdk.WorkspaceAgentPortShareProtocol](#codersdkworkspaceagentportshareprotocol) | false | | |
|
||||
| `share_level` | [codersdk.WorkspaceAgentPortShareLevel](#codersdkworkspaceagentportsharelevel) | false | | |
|
||||
| `workspace_id` | string | false | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Property | Value |
|
||||
| ------------- | --------------- |
|
||||
| `protocol` | `http` |
|
||||
| `protocol` | `https` |
|
||||
| `share_level` | `owner` |
|
||||
| `share_level` | `authenticated` |
|
||||
| `share_level` | `public` |
|
||||
|
||||
## codersdk.WorkspaceAgentPortShareLevel
|
||||
|
||||
|
@ -7609,6 +7633,21 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
| `authenticated` |
|
||||
| `public` |
|
||||
|
||||
## codersdk.WorkspaceAgentPortShareProtocol
|
||||
|
||||
```json
|
||||
"http"
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Value |
|
||||
| ------- |
|
||||
| `http` |
|
||||
| `https` |
|
||||
|
||||
## codersdk.WorkspaceAgentPortShares
|
||||
|
||||
```json
|
||||
|
@ -7617,6 +7656,7 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
{
|
||||
"agent_name": "string",
|
||||
"port": 0,
|
||||
"protocol": "http",
|
||||
"share_level": "owner",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
|
|
|
@ -219,6 +219,7 @@ func TestTemplates(t *testing.T) {
|
|||
AgentName: ws.LatestBuild.Resources[0].Agents[0].Name,
|
||||
Port: 8080,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ func TestWorkspacePortShare(t *testing.T) {
|
|||
AgentName: r.sdkAgent.Name,
|
||||
Port: 8080,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
|
||||
})
|
||||
require.Error(t, err, "Port sharing level not allowed")
|
||||
|
||||
|
@ -57,6 +58,7 @@ func TestWorkspacePortShare(t *testing.T) {
|
|||
AgentName: r.sdkAgent.Name,
|
||||
Port: 8080,
|
||||
ShareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
|
||||
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, codersdk.WorkspaceAgentPortShareLevelPublic, ps.ShareLevel)
|
||||
|
|
|
@ -1504,6 +1504,7 @@ export interface UpsertWorkspaceAgentPortShareRequest {
|
|||
readonly agent_name: string;
|
||||
readonly port: number;
|
||||
readonly share_level: WorkspaceAgentPortShareLevel;
|
||||
readonly protocol: WorkspaceAgentPortShareProtocol;
|
||||
}
|
||||
|
||||
// From codersdk/users.go
|
||||
|
@ -1762,6 +1763,7 @@ export interface WorkspaceAgentPortShare {
|
|||
readonly agent_name: string;
|
||||
readonly port: number;
|
||||
readonly share_level: WorkspaceAgentPortShareLevel;
|
||||
readonly protocol: WorkspaceAgentPortShareProtocol;
|
||||
}
|
||||
|
||||
// From codersdk/workspaceagentportshare.go
|
||||
|
@ -2351,6 +2353,11 @@ export const WorkspaceAgentPortShareLevels: WorkspaceAgentPortShareLevel[] = [
|
|||
"public",
|
||||
];
|
||||
|
||||
// From codersdk/workspaceagentportshare.go
|
||||
export type WorkspaceAgentPortShareProtocol = "http" | "https";
|
||||
export const WorkspaceAgentPortShareProtocols: WorkspaceAgentPortShareProtocol[] =
|
||||
["http", "https"];
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
export type WorkspaceAgentStartupScriptBehavior = "blocking" | "non-blocking";
|
||||
export const WorkspaceAgentStartupScriptBehaviors: WorkspaceAgentStartupScriptBehavior[] =
|
||||
|
|
|
@ -26,10 +26,11 @@ import {
|
|||
} from "api/queries/workspaceportsharing";
|
||||
import {
|
||||
type Template,
|
||||
type UpsertWorkspaceAgentPortShareRequest,
|
||||
type WorkspaceAgent,
|
||||
type WorkspaceAgentListeningPort,
|
||||
type WorkspaceAgentPortShareLevel,
|
||||
type UpsertWorkspaceAgentPortShareRequest,
|
||||
type WorkspaceAgentPortShareProtocol,
|
||||
WorkspaceAppSharingLevels,
|
||||
} from "api/typesGenerated";
|
||||
import {
|
||||
|
@ -162,6 +163,7 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
|
|||
initialValues: {
|
||||
agent_name: agent.name,
|
||||
port: undefined,
|
||||
protocol: "http",
|
||||
share_level: "authenticated",
|
||||
},
|
||||
validationSchema,
|
||||
|
@ -325,6 +327,7 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
|
|||
await upsertSharedPortMutation.mutateAsync({
|
||||
agent_name: agent.name,
|
||||
port: port.port,
|
||||
protocol: "http",
|
||||
share_level: "authenticated",
|
||||
});
|
||||
await sharedPortsQuery.refetch();
|
||||
|
@ -384,8 +387,31 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
|
|||
)}
|
||||
{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">
|
||||
<FormControl
|
||||
size="small"
|
||||
css={styles.shareLevelFormControl}
|
||||
>
|
||||
<Select
|
||||
css={styles.shareLevelSelect}
|
||||
value={share.share_level}
|
||||
|
@ -393,6 +419,7 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
|
|||
await upsertSharedPortMutation.mutateAsync({
|
||||
agent_name: agent.name,
|
||||
port: share.port,
|
||||
protocol: share.protocol,
|
||||
share_level: event.target
|
||||
.value as WorkspaceAgentPortShareLevel,
|
||||
});
|
||||
|
@ -452,6 +479,17 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
|
|||
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}
|
||||
|
@ -486,7 +524,7 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
|
|||
const classNames = {
|
||||
paper: (css, theme) => css`
|
||||
padding: 0;
|
||||
width: 304px;
|
||||
width: 404px;
|
||||
color: ${theme.palette.text.secondary};
|
||||
margin-top: 4px;
|
||||
`,
|
||||
|
@ -515,6 +553,7 @@ const styles = {
|
|||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
fontWeight: 500,
|
||||
minWidth: 80,
|
||||
}),
|
||||
|
||||
portNumber: (theme) => ({
|
||||
|
@ -563,4 +602,13 @@ const styles = {
|
|||
display: "block",
|
||||
width: "100%",
|
||||
}),
|
||||
sharedPortLink: () => ({
|
||||
minWidth: 80,
|
||||
}),
|
||||
protocolFormControl: () => ({
|
||||
minWidth: 90,
|
||||
}),
|
||||
shareLevelFormControl: () => ({
|
||||
minWidth: 140,
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
|
|
@ -15,7 +15,7 @@ const meta: Meta<typeof PortForwardPopoverView> = {
|
|||
(Story) => (
|
||||
<div
|
||||
css={(theme) => ({
|
||||
width: 304,
|
||||
width: 404,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
borderRadius: 8,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
|
|
|
@ -3276,18 +3276,21 @@ export const MockSharedPortsResponse: TypesGen.WorkspaceAgentPortShares = {
|
|||
agent_name: "a-workspace-agent",
|
||||
port: 4000,
|
||||
share_level: "authenticated",
|
||||
protocol: "http",
|
||||
},
|
||||
{
|
||||
workspace_id: MockWorkspace.id,
|
||||
agent_name: "a-workspace-agent",
|
||||
port: 8080,
|
||||
port: 65535,
|
||||
share_level: "authenticated",
|
||||
protocol: "https",
|
||||
},
|
||||
{
|
||||
workspace_id: MockWorkspace.id,
|
||||
agent_name: "a-workspace-agent",
|
||||
port: 8081,
|
||||
share_level: "public",
|
||||
protocol: "http",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue