ui(deploy): remove Exposed Ports field from Resources tab
The field was cosmetic — `containerConfig.exposedPorts` only fed Docker's `Config.ExposedPorts` metadata via `withExposedPorts(...)`. It never published a host port and Traefik routing uses `appPort` from the label builder, not this list. Users reading the label "Exposed Ports" reasonably expected it to expose their port externally; removing it until real multi-port Traefik routing lands (tracked in #149). Backend DTOs (`ContainerRequest.exposedPorts`, `ConfigMerger.intList ("exposedPorts")`) are left in place so existing containerConfig JSONB rows continue to deserialize. New writes from the UI will no longer include the field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -66,7 +66,7 @@ describe('ConfigPanel', () => {
|
|||||||
},
|
},
|
||||||
resources: {
|
resources: {
|
||||||
memoryLimit: '256', memoryReserve: '', cpuRequest: '500', cpuLimit: '',
|
memoryLimit: '256', memoryReserve: '', cpuRequest: '500', cpuLimit: '',
|
||||||
ports: [], appPort: '8080', replicas: '1', deployStrategy: 'blue-green',
|
appPort: '8080', replicas: '1', deployStrategy: 'blue-green',
|
||||||
stripPrefix: true, sslOffloading: true, runtimeType: 'auto', customArgs: '',
|
stripPrefix: true, sslOffloading: true, runtimeType: 'auto', customArgs: '',
|
||||||
extraNetworks: [],
|
extraNetworks: [],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ describe('snapshotToForm', () => {
|
|||||||
replicas: 3,
|
replicas: 3,
|
||||||
deploymentStrategy: 'rolling',
|
deploymentStrategy: 'rolling',
|
||||||
customEnvVars: { FOO: 'bar', BAZ: 'qux' },
|
customEnvVars: { FOO: 'bar', BAZ: 'qux' },
|
||||||
exposedPorts: [8080, 9090],
|
|
||||||
},
|
},
|
||||||
sensitiveKeys: ['SECRET_KEY'],
|
sensitiveKeys: ['SECRET_KEY'],
|
||||||
};
|
};
|
||||||
@@ -36,7 +35,6 @@ describe('snapshotToForm', () => {
|
|||||||
expect(result.resources.memoryLimit).toBe('1024');
|
expect(result.resources.memoryLimit).toBe('1024');
|
||||||
expect(result.resources.replicas).toBe('3');
|
expect(result.resources.replicas).toBe('3');
|
||||||
expect(result.resources.deployStrategy).toBe('rolling');
|
expect(result.resources.deployStrategy).toBe('rolling');
|
||||||
expect(result.resources.ports).toEqual([8080, 9090]);
|
|
||||||
expect(result.variables.envVars).toEqual([
|
expect(result.variables.envVars).toEqual([
|
||||||
{ key: 'FOO', value: 'bar' },
|
{ key: 'FOO', value: 'bar' },
|
||||||
{ key: 'BAZ', value: 'qux' },
|
{ key: 'BAZ', value: 'qux' },
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ export function snapshotToForm(
|
|||||||
memoryReserve: c.memoryReserveMb != null ? String(c.memoryReserveMb) : defaults.resources.memoryReserve,
|
memoryReserve: c.memoryReserveMb != null ? String(c.memoryReserveMb) : defaults.resources.memoryReserve,
|
||||||
cpuRequest: c.cpuRequest !== undefined ? String(c.cpuRequest) : defaults.resources.cpuRequest,
|
cpuRequest: c.cpuRequest !== undefined ? String(c.cpuRequest) : defaults.resources.cpuRequest,
|
||||||
cpuLimit: c.cpuLimit != null ? String(c.cpuLimit) : defaults.resources.cpuLimit,
|
cpuLimit: c.cpuLimit != null ? String(c.cpuLimit) : defaults.resources.cpuLimit,
|
||||||
ports: Array.isArray(c.exposedPorts) ? (c.exposedPorts as number[]) : defaults.resources.ports,
|
|
||||||
appPort: c.appPort !== undefined ? String(c.appPort) : defaults.resources.appPort,
|
appPort: c.appPort !== undefined ? String(c.appPort) : defaults.resources.appPort,
|
||||||
replicas: c.replicas !== undefined ? String(c.replicas) : defaults.resources.replicas,
|
replicas: c.replicas !== undefined ? String(c.replicas) : defaults.resources.replicas,
|
||||||
deployStrategy: (c.deploymentStrategy as string) ?? defaults.resources.deployStrategy,
|
deployStrategy: (c.deploymentStrategy as string) ?? defaults.resources.deployStrategy,
|
||||||
|
|||||||
@@ -11,24 +11,11 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ResourcesTab({ value, onChange, disabled, isProd = false }: Props) {
|
export function ResourcesTab({ value, onChange, disabled, isProd = false }: Props) {
|
||||||
const [newPort, setNewPort] = useState('');
|
|
||||||
const [newNetwork, setNewNetwork] = useState('');
|
const [newNetwork, setNewNetwork] = useState('');
|
||||||
|
|
||||||
const update = <K extends keyof ResourcesFormState>(key: K, v: ResourcesFormState[K]) =>
|
const update = <K extends keyof ResourcesFormState>(key: K, v: ResourcesFormState[K]) =>
|
||||||
onChange({ ...value, [key]: v });
|
onChange({ ...value, [key]: v });
|
||||||
|
|
||||||
function addPort() {
|
|
||||||
const p = parseInt(newPort);
|
|
||||||
if (p && !value.ports.includes(p)) {
|
|
||||||
onChange({ ...value, ports: [...value.ports, p] });
|
|
||||||
setNewPort('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removePort(port: number) {
|
|
||||||
if (!disabled) update('ports', value.ports.filter((x) => x !== port));
|
|
||||||
}
|
|
||||||
|
|
||||||
function addNetwork() {
|
function addNetwork() {
|
||||||
const v = newNetwork.trim();
|
const v = newNetwork.trim();
|
||||||
if (v && !value.extraNetworks.includes(v)) {
|
if (v && !value.extraNetworks.includes(v)) {
|
||||||
@@ -123,35 +110,6 @@ export function ResourcesTab({ value, onChange, disabled, isProd = false }: Prop
|
|||||||
<span className={styles.cellMeta}>millicores</span>
|
<span className={styles.cellMeta}>millicores</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span className={styles.configLabel}>Exposed Ports</span>
|
|
||||||
<div className={styles.portPills}>
|
|
||||||
{value.ports.map((p) => (
|
|
||||||
<span key={p} className={styles.portPill}>
|
|
||||||
{p}
|
|
||||||
<button
|
|
||||||
className={styles.portPillDelete}
|
|
||||||
disabled={disabled}
|
|
||||||
onClick={() => removePort(p)}
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
<input
|
|
||||||
className={styles.portAddInput}
|
|
||||||
disabled={disabled}
|
|
||||||
placeholder="+ port"
|
|
||||||
value={newPort}
|
|
||||||
onChange={(e) => setNewPort(e.target.value)}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
e.preventDefault();
|
|
||||||
addPort();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span className={styles.configLabel}>App Port</span>
|
<span className={styles.configLabel}>App Port</span>
|
||||||
<Input
|
<Input
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ const defaultResources: ResourcesFormState = {
|
|||||||
memoryReserve: '',
|
memoryReserve: '',
|
||||||
cpuRequest: '500',
|
cpuRequest: '500',
|
||||||
cpuLimit: '',
|
cpuLimit: '',
|
||||||
ports: [],
|
|
||||||
appPort: '8080',
|
appPort: '8080',
|
||||||
replicas: '1',
|
replicas: '1',
|
||||||
deployStrategy: 'blue-green',
|
deployStrategy: 'blue-green',
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export interface ResourcesFormState {
|
|||||||
memoryReserve: string;
|
memoryReserve: string;
|
||||||
cpuRequest: string;
|
cpuRequest: string;
|
||||||
cpuLimit: string;
|
cpuLimit: string;
|
||||||
ports: number[];
|
|
||||||
appPort: string;
|
appPort: string;
|
||||||
replicas: string;
|
replicas: string;
|
||||||
deployStrategy: string;
|
deployStrategy: string;
|
||||||
@@ -66,7 +65,7 @@ export const defaultForm: DeploymentPageFormState = {
|
|||||||
},
|
},
|
||||||
resources: {
|
resources: {
|
||||||
memoryLimit: '512', memoryReserve: '', cpuRequest: '500', cpuLimit: '',
|
memoryLimit: '512', memoryReserve: '', cpuRequest: '500', cpuLimit: '',
|
||||||
ports: [], appPort: '8080', replicas: '1', deployStrategy: 'blue-green',
|
appPort: '8080', replicas: '1', deployStrategy: 'blue-green',
|
||||||
stripPrefix: true, sslOffloading: true, runtimeType: 'auto', customArgs: '',
|
stripPrefix: true, sslOffloading: true, runtimeType: 'auto', customArgs: '',
|
||||||
extraNetworks: [],
|
extraNetworks: [],
|
||||||
},
|
},
|
||||||
@@ -108,7 +107,6 @@ export function useDeploymentPageState(
|
|||||||
memoryReserve: merged.memoryReserveMb != null ? String(merged.memoryReserveMb) : defaultForm.resources.memoryReserve,
|
memoryReserve: merged.memoryReserveMb != null ? String(merged.memoryReserveMb) : defaultForm.resources.memoryReserve,
|
||||||
cpuRequest: String(merged.cpuRequest ?? defaultForm.resources.cpuRequest),
|
cpuRequest: String(merged.cpuRequest ?? defaultForm.resources.cpuRequest),
|
||||||
cpuLimit: merged.cpuLimit != null ? String(merged.cpuLimit) : defaultForm.resources.cpuLimit,
|
cpuLimit: merged.cpuLimit != null ? String(merged.cpuLimit) : defaultForm.resources.cpuLimit,
|
||||||
ports: Array.isArray(merged.exposedPorts) ? (merged.exposedPorts as number[]) : defaultForm.resources.ports,
|
|
||||||
appPort: String(merged.appPort ?? defaultForm.resources.appPort),
|
appPort: String(merged.appPort ?? defaultForm.resources.appPort),
|
||||||
replicas: String(merged.replicas ?? defaultForm.resources.replicas),
|
replicas: String(merged.replicas ?? defaultForm.resources.replicas),
|
||||||
deployStrategy: String(merged.deploymentStrategy ?? defaultForm.resources.deployStrategy),
|
deployStrategy: String(merged.deploymentStrategy ?? defaultForm.resources.deployStrategy),
|
||||||
|
|||||||
@@ -200,7 +200,6 @@ export default function AppDeploymentPage() {
|
|||||||
memoryReserveMb: r.memoryReserve ? parseInt(r.memoryReserve) : null,
|
memoryReserveMb: r.memoryReserve ? parseInt(r.memoryReserve) : null,
|
||||||
cpuRequest: r.cpuRequest ? parseInt(r.cpuRequest) : null,
|
cpuRequest: r.cpuRequest ? parseInt(r.cpuRequest) : null,
|
||||||
cpuLimit: r.cpuLimit ? parseInt(r.cpuLimit) : null,
|
cpuLimit: r.cpuLimit ? parseInt(r.cpuLimit) : null,
|
||||||
exposedPorts: r.ports,
|
|
||||||
customEnvVars: Object.fromEntries(
|
customEnvVars: Object.fromEntries(
|
||||||
form.variables.envVars.filter((v) => v.key.trim()).map((v) => [v.key, v.value]),
|
form.variables.envVars.filter((v) => v.key.trim()).map((v) => [v.key, v.value]),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user