Skip to content

Commit 003902b

Browse files
committed
fix(Docker): improve container state management
1 parent e1b1b18 commit 003902b

File tree

3 files changed

+27
-37
lines changed

3 files changed

+27
-37
lines changed

admin/app/services/docker_service.ts

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import Service from '#models/service'
22
import Docker from 'dockerode'
33
import logger from '@adonisjs/core/services/logger'
44
import { inject } from '@adonisjs/core'
5-
import { ServiceStatus } from '../../types/services.js'
65
import transmit from '@adonisjs/transmit/services/main'
76
import { doResumableDownloadWithRetry } from '../utils/downloads.js'
87
import { join } from 'path'
@@ -92,18 +91,16 @@ export class DockerService {
9291
}
9392
}
9493

94+
/**
95+
* Fetches the status of all Docker containers related to Nomad services. (those prefixed with 'nomad_')
96+
*/
9597
async getServicesStatus(): Promise<
9698
{
9799
service_name: string
98-
status: ServiceStatus
100+
status: string
99101
}[]
100102
> {
101103
try {
102-
const services = await Service.query().where('installed', true)
103-
if (!services || services.length === 0) {
104-
return []
105-
}
106-
107104
const containers = await this.docker.listContainers({ all: true })
108105
const containerMap = new Map<string, Docker.ContainerInfo>()
109106
containers.forEach((container) => {
@@ -113,22 +110,9 @@ export class DockerService {
113110
}
114111
})
115112

116-
const getStatus = (state: string): ServiceStatus => {
117-
switch (state) {
118-
case 'running':
119-
return 'running'
120-
case 'exited':
121-
case 'created':
122-
case 'paused':
123-
return 'stopped'
124-
default:
125-
return 'unknown'
126-
}
127-
}
128-
129113
return Array.from(containerMap.entries()).map(([name, container]) => ({
130114
service_name: name,
131-
status: getStatus(container.State),
115+
status: container.State,
132116
}))
133117
} catch (error) {
134118
console.error(`Error fetching services status: ${error.message}`)
@@ -189,13 +173,12 @@ export class DockerService {
189173
// }
190174

191175
const containerConfig = this._parseContainerConfig(service.container_config)
192-
176+
193177
// Execute installation asynchronously and handle cleanup
194-
this._createContainer(service, containerConfig)
195-
.catch(async (error) => {
196-
logger.error(`Installation failed for ${serviceName}: ${error.message}`)
197-
await this._cleanupFailedInstallation(serviceName)
198-
})
178+
this._createContainer(service, containerConfig).catch(async (error) => {
179+
logger.error(`Installation failed for ${serviceName}: ${error.message}`)
180+
await this._cleanupFailedInstallation(serviceName)
181+
})
199182

200183
return {
201184
success: true,
@@ -416,7 +399,9 @@ export class DockerService {
416399
this.activeInstallations.delete(serviceName)
417400
logger.info(`[DockerService] Cleaned up failed installation for ${serviceName}`)
418401
} catch (error) {
419-
logger.error(`[DockerService] Failed to cleanup installation for ${serviceName}: ${error.message}`)
402+
logger.error(
403+
`[DockerService] Failed to cleanup installation for ${serviceName}: ${error.message}`
404+
)
420405
}
421406
}
422407

admin/app/services/system_service.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,29 +223,35 @@ export class SystemService {
223223

224224
/**
225225
* Checks the current state of Docker containers against the database records and updates the database accordingly.
226-
* It will mark services as not installed if their corresponding containers are not running, and can also handle cleanup of any orphaned records.
227-
* Handles cases where a container might have been manually removed or is in an unexpected state, ensuring the database reflects the actual state of containers.
226+
* It will mark services as not installed if their corresponding containers do not exist, regardless of their running state.
227+
* Handles cases where a container might have been manually removed, ensuring the database reflects the actual existence of containers.
228+
* Containers that exist but are stopped, paused, or restarting will still be considered installed.
228229
*/
229230
private async _syncContainersWithDatabase() {
230231
try {
231232
const allServices = await Service.all()
232233
const serviceStatusList = await this.dockerService.getServicesStatus()
233234

234235
for (const service of allServices) {
235-
const status = serviceStatusList.find((s) => s.service_name === service.service_name)
236+
const containerExists = serviceStatusList.find(
237+
(s) => s.service_name === service.service_name
238+
)
239+
236240
if (service.installed) {
237-
if (!status || status.status !== 'running') {
241+
// If marked as installed but container doesn't exist, mark as not installed
242+
if (!containerExists) {
238243
logger.warn(
239-
`Service ${service.service_name} is marked as installed but container is not running. Marking as not installed.`
244+
`Service ${service.service_name} is marked as installed but container does not exist. Marking as not installed.`
240245
)
241246
service.installed = false
242247
service.installation_status = 'idle'
243248
await service.save()
244249
}
245250
} else {
246-
if (status && status.status === 'running') {
251+
// If marked as not installed but container exists (any state), mark as installed
252+
if (containerExists) {
247253
logger.warn(
248-
`Service ${service.service_name} is marked as not installed but container is running. Marking as installed.`
254+
`Service ${service.service_name} is marked as not installed but container exists. Marking as installed.`
249255
)
250256
service.installed = true
251257
service.installation_status = 'idle'

admin/types/services.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Service from '#models/service'
22

3-
export type ServiceStatus = 'unknown' | 'running' | 'stopped'
43
export type ServiceSlim = Pick<
54
Service,
65
| 'id'
@@ -11,4 +10,4 @@ export type ServiceSlim = Pick<
1110
| 'friendly_name'
1211
| 'description'
1312
| 'icon'
14-
> & { status?: ServiceStatus }
13+
> & { status?: string }

0 commit comments

Comments
 (0)