Skip to content

Commit b9a0707

Browse files
Merge pull request #736 from nextcloud-libraries/enh/docker-functions
feat: export some more docker functions
2 parents 4af7382 + 39073b3 commit b9a0707

File tree

1 file changed

+103
-40
lines changed

1 file changed

+103
-40
lines changed

lib/docker.ts

Lines changed: 103 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ interface StartOptions {
7777
*
7878
* @param {string|undefined} branch server branch to use (default 'master')
7979
* @param {boolean|string|undefined} mountApp bind mount app within server (`true` for autodetect, `false` to disable, or a string to force a path) (default true)
80-
* @param {StartOptions|undefined} options Optional parameters to configre the container creation
80+
* @param {StartOptions|undefined} options Optional parameters to configure the container creation
8181
* @return Promise resolving to the IP address of the server
8282
* @throws {Error} If Nextcloud container could not be started
8383
*/
@@ -109,19 +109,7 @@ export async function startNextcloud(branch = 'master', mountApp: boolean|string
109109
}
110110

111111
try {
112-
// Pulling images
113-
console.log('\nPulling images… ⏳')
114-
await new Promise((resolve, reject) => docker.pull(SERVER_IMAGE, (_err, stream: Stream) => {
115-
const onFinished = function(err: Error | null) {
116-
if (!err) {
117-
return resolve(true)
118-
}
119-
reject(err)
120-
}
121-
// https://github.com/apocas/dockerode/issues/357
122-
docker.modem.followProgress(stream, onFinished)
123-
}))
124-
console.log('└─ Done')
112+
await pullImage()
125113

126114
// Getting latest image
127115
console.log('\nChecking running containers… 🔍')
@@ -189,8 +177,8 @@ export async function startNextcloud(branch = 'master', mountApp: boolean|string
189177
await container.start()
190178

191179
// Set proper permissions for the data folder
192-
await runExec(container, ['chown', '-R', 'www-data:www-data', '/var/www/html/data'], false, 'root')
193-
await runExec(container, ['chmod', '0770', '/var/www/html/data'], false, 'root')
180+
await runExec(['chown', '-R', 'www-data:www-data', '/var/www/html/data'], { container, user: 'root' })
181+
await runExec(['chmod', '0770', '/var/www/html/data'], { container, user: 'root' })
194182

195183
// Get container's IP
196184
const ip = await getContainerIP(container)
@@ -207,6 +195,27 @@ export async function startNextcloud(branch = 'master', mountApp: boolean|string
207195
}
208196
}
209197

198+
const pullImage = function() {
199+
// Pulling images
200+
console.log('\nPulling images… ⏳')
201+
return new Promise((resolve, reject) => docker.pull(SERVER_IMAGE, (_err, stream: Stream) => {
202+
const onFinished = function(err: Error | null) {
203+
if (!err) {
204+
return resolve(true)
205+
}
206+
reject(err)
207+
}
208+
// https://github.com/apocas/dockerode/issues/357
209+
if(stream) {
210+
docker.modem.followProgress(stream, onFinished)
211+
} else {
212+
reject('Failed to open stream')
213+
}
214+
}))
215+
.then(() => console.log('└─ Done'))
216+
.catch(err => console.log(`└─ 🛑 FAILED! Trying to continue with existing image. (${err})`))
217+
}
218+
210219
/**
211220
* Configure Nextcloud
212221
*
@@ -219,20 +228,20 @@ export const configureNextcloud = async function(apps = ['viewer'], vendoredBran
219228

220229
console.log('\nConfiguring Nextcloud…')
221230
container = container ?? getContainer()
222-
await runExec(container, ['php', 'occ', '--version'], true)
231+
await runOcc('--version', { container, verbose: true })
223232

224233
// Be consistent for screenshots
225-
await runExec(container, ['php', 'occ', 'config:system:set', 'default_language', '--value', 'en'], true)
226-
await runExec(container, ['php', 'occ', 'config:system:set', 'force_language', '--value', 'en'], true)
227-
await runExec(container, ['php', 'occ', 'config:system:set', 'default_locale', '--value', 'en_US'], true)
228-
await runExec(container, ['php', 'occ', 'config:system:set', 'force_locale', '--value', 'en_US'], true)
229-
await runExec(container, ['php', 'occ', 'config:system:set', 'enforce_theme', '--value', 'light'], true)
234+
await setSystemConfig('default_language', 'en', { container })
235+
await setSystemConfig('force_language', 'en', { container })
236+
await setSystemConfig('default_locale', 'en_US', { container })
237+
await setSystemConfig('force_locale', 'en_US', { container })
238+
await setSystemConfig('enforce_theme', 'light', { container })
230239

231240
// Checking apcu
232241
console.log('├─ Checking APCu configuration... 👀')
233-
const distributed = await runExec(container, ['php', 'occ', 'config:system:get', 'memcache.distributed'])
234-
const local = await runExec(container, ['php', 'occ', 'config:system:get', 'memcache.local'])
235-
const hashing = await runExec(container, ['php', 'occ', 'config:system:get', 'hashing_default_password'])
242+
const distributed = await getSystemConfig('memcache.distributed', { container })
243+
const local = await getSystemConfig('memcache.local', { container })
244+
const hashing = await getSystemConfig('hashing_default_password', { container })
236245
if (!distributed.includes('Memcache\\APCu')
237246
|| !local.includes('Memcache\\APCu')
238247
|| !hashing.includes('true')) {
@@ -242,7 +251,7 @@ export const configureNextcloud = async function(apps = ['viewer'], vendoredBran
242251
console.log('│ └─ OK !')
243252

244253
// Build app list
245-
const json = await runExec(container, ['php', 'occ', 'app:list', '--output', 'json'], false)
254+
const json = await runOcc(['app:list', '--output', 'json'], { container })
246255
// fix dockerode bug returning invalid leading characters
247256
const applist = JSON.parse(json.substring(json.indexOf('{')))
248257

@@ -252,14 +261,14 @@ export const configureNextcloud = async function(apps = ['viewer'], vendoredBran
252261
console.log(`├─ ${app} version ${applist.enabled[app]} already installed and enabled`)
253262
} else if (app in applist.disabled) {
254263
// built in or mounted already as the app under development
255-
await runExec(container, ['php', 'occ', 'app:enable', '--force', app], true)
264+
await runOcc(['app:enable', '--force', app], { container, verbose: true })
256265
} else if (app in VENDOR_APPS) {
257266
// apps that are vendored but still missing (i.e. not build in or mounted already)
258-
await runExec(container, ['git', 'clone', '--depth=1', `--branch=${vendoredBranch}`, VENDOR_APPS[app], `apps/${app}`], true)
259-
await runExec(container, ['php', 'occ', 'app:enable', '--force', app], true)
267+
await runExec(['git', 'clone', '--depth=1', `--branch=${vendoredBranch}`, VENDOR_APPS[app], `apps/${app}`], { container, verbose: true })
268+
await runOcc(['app:enable', '--force', app], { container, verbose: true })
260269
} else {
261270
// try appstore
262-
await runExec(container, ['php', 'occ', 'app:install', '--force', app], true)
271+
await runOcc(['app:install', '--force', app], { container, verbose: true })
263272
}
264273
}
265274
console.log('└─ Nextcloud is now ready to use 🎉')
@@ -274,7 +283,7 @@ export const setupUsers = async function(container?: Container) {
274283
console.log('\nCreating test users… 👤')
275284
const users = ['test1', 'test2', 'test3', 'test4', 'test5']
276285
for (const user of users) {
277-
await runExec(container ?? getContainer(), ['php', 'occ', 'user:add', user, '--password-from-env'], true, 'www-data', ['OC_PASS=' + user])
286+
await addUser(user, { container, verbose: true })
278287
}
279288
console.log('└─ Done')
280289
}
@@ -288,7 +297,7 @@ export const setupUsers = async function(container?: Container) {
288297
export const createSnapshot = async function(snapshot?: string, container?: Container): Promise<string> {
289298
const hash = new Date().toISOString().replace(/[^0-9]/g, '')
290299
console.log('\nCreating init DB snapshot…')
291-
await runExec(container ?? getContainer(), ['cp', '/var/www/html/data/owncloud.db', `/var/www/html/data/owncloud.db-${snapshot ?? hash}`], true)
300+
await runExec(['cp', '/var/www/html/data/owncloud.db', `/var/www/html/data/owncloud.db-${snapshot ?? hash}`], { container, verbose: true })
292301
console.log('└─ Done')
293302
return snapshot ?? hash
294303
}
@@ -300,7 +309,7 @@ export const createSnapshot = async function(snapshot?: string, container?: Cont
300309
*/
301310
export const restoreSnapshot = async function(snapshot = 'init', container?: Container) {
302311
console.log('\nRestoring DB snapshot…')
303-
await runExec(container ?? getContainer(), ['cp', `/var/www/html/data/owncloud.db-${snapshot}`, '/var/www/html/data/owncloud.db'], true)
312+
await runExec(['cp', `/var/www/html/data/owncloud.db-${snapshot}`, '/var/www/html/data/owncloud.db'], { container, verbose: true })
304313
console.log('└─ Done')
305314
}
306315

@@ -356,15 +365,23 @@ export const waitOnNextcloud = async function(ip: string) {
356365
console.log('└─ Done')
357366
}
358367

359-
const runExec = async function(
360-
container: Docker.Container,
361-
command: string[],
362-
verbose = false,
363-
user = 'www-data',
364-
env: string[] = [],
368+
interface RunExecOptions {
369+
container: Docker.Container;
370+
user: string;
371+
env: string[];
372+
verbose: boolean;
373+
}
374+
375+
/**
376+
* Execute a command in the container
377+
*/
378+
export const runExec = async function(
379+
command: string | string[],
380+
{ container, user='www-data', verbose=false, env=[] }: Partial<RunExecOptions> = {},
365381
) {
382+
container = container || getContainer()
366383
const exec = await container.exec({
367-
Cmd: command,
384+
Cmd: typeof command === 'string' ? [command] : command,
368385
AttachStdout: true,
369386
AttachStderr: true,
370387
User: user,
@@ -397,6 +414,52 @@ const runExec = async function(
397414
})
398415
}
399416

417+
/**
418+
* Execute an occ command in the container
419+
*/
420+
export const runOcc = function(
421+
command: string | string[],
422+
{ container, env=[], verbose=false }: Partial<Omit<RunExecOptions, 'user'>> = {},
423+
) {
424+
const cmdArray = typeof command === 'string' ? [command] : command
425+
return runExec(['php', 'occ', ...cmdArray], { container, verbose, env })
426+
}
427+
428+
/**
429+
* Set a Nextcloud system config in the container.
430+
*/
431+
export const setSystemConfig = function(
432+
key: string,
433+
value: string,
434+
{ container }: { container?: Docker.Container } = {},
435+
) {
436+
return runOcc(['config:system:set', key, '--value', value], { container, verbose: true })
437+
}
438+
439+
/**
440+
* Get a Nextcloud system config value from the container.
441+
*/
442+
export const getSystemConfig = function(
443+
key: string,
444+
{ container }: { container?: Docker.Container } = {},
445+
) {
446+
return runOcc(['config:system:get', key], { container })
447+
}
448+
449+
450+
/**
451+
* Add a user to the Nextcloud in the container.
452+
*/
453+
export const addUser = function(
454+
user: string,
455+
{ container, env=[], verbose=false }: Partial<Omit<RunExecOptions, 'user'>> = {},
456+
) {
457+
return runOcc(
458+
['user:add', user, '--password-from-env'],
459+
{ container, verbose, env: ['OC_PASS=' + user, ...env] }
460+
)
461+
}
462+
400463
const sleep = function(milliseconds: number) {
401464
return new Promise((resolve) => setTimeout(resolve, milliseconds))
402465
}

0 commit comments

Comments
 (0)