Add Portainer-style container name replace on deploy.
Release rolling / release (push) Has been cancelled
Release rolling / release (push) Has been cancelled
When deploying a container whose name already exists, prompt to Replace (stop and remove the old container) or Cancel. Server accepts replace:true to perform the swap; clients pre-check names and retry on conflict races.
This commit is contained in:
+27
-4
@@ -344,9 +344,12 @@ export function isJobDrawerActive() {
|
||||
export async function deployContainerWithSteps(args) {
|
||||
/** @type {{ pulledOk: boolean }} */
|
||||
const state = { pulledOk: false }
|
||||
const replace = args.replace === true
|
||||
|
||||
return runJob(
|
||||
`Deploy ${args.containerName || 'container'}`,
|
||||
replace
|
||||
? `Replace ${args.containerName || 'container'}`
|
||||
: `Deploy ${args.containerName || 'container'}`,
|
||||
[
|
||||
{
|
||||
id: 'validate',
|
||||
@@ -359,6 +362,7 @@ export async function deployContainerWithSteps(args) {
|
||||
}
|
||||
log(`Name=${args.containerName}`)
|
||||
log(`Image=${args.image}`)
|
||||
if (replace) log('Mode: replace existing container with same name')
|
||||
if (args.ports?.length) log(`Ports: ${args.ports.join(', ')}`)
|
||||
if (args.volumes?.length) log(`Volumes: ${args.volumes.join(', ')}`)
|
||||
if (args.networkMode) log(`Network mode: ${args.networkMode}`)
|
||||
@@ -391,18 +395,37 @@ export async function deployContainerWithSteps(args) {
|
||||
},
|
||||
{
|
||||
id: 'create',
|
||||
label: 'Create & start container',
|
||||
label: replace ? 'Replace, create & start' : 'Create & start container',
|
||||
run: async ({ log }) => {
|
||||
log('Creating and starting container…')
|
||||
log(
|
||||
replace
|
||||
? 'Removing previous container (if present), then creating…'
|
||||
: 'Creating and starting container…'
|
||||
)
|
||||
// Only skip server pull when client pull already succeeded
|
||||
const payload = { ...args, skipPull: state.pulledOk === true }
|
||||
const payload = {
|
||||
...args,
|
||||
skipPull: state.pulledOk === true,
|
||||
replace: replace === true,
|
||||
}
|
||||
try {
|
||||
const res = await manager.request(Methods.deployContainer, payload)
|
||||
log(res?.message || `Container "${args.containerName}" is running`)
|
||||
if (res?.id) log(`ID: ${String(res.id).slice(0, 12)}`)
|
||||
if (replace) log('Previous container was replaced')
|
||||
return res
|
||||
} catch (err) {
|
||||
const info = explainError(err, 'deployContainer')
|
||||
// Keep conflict code so outer deploy can offer Replace
|
||||
if (
|
||||
err?.code === 'CONTAINER_NAME_CONFLICT' ||
|
||||
info.code === 'CONTAINER_NAME_CONFLICT'
|
||||
) {
|
||||
const e = new Error(err.message || info.message)
|
||||
e.code = 'CONTAINER_NAME_CONFLICT'
|
||||
e.cause = err
|
||||
throw e
|
||||
}
|
||||
// Re-throw with full descriptive text for the job log formatter
|
||||
const full = [info.title, info.message, info.recovery ? `How to fix: ${info.recovery}` : '']
|
||||
.filter(Boolean)
|
||||
|
||||
Reference in New Issue
Block a user