From 21a366077a0c80c64234c137f0c236d4d30614de Mon Sep 17 00:00:00 2001 From: Perry Manuk Date: Thu, 17 Sep 2020 15:26:39 +0200 Subject: [PATCH] Techdocs without docker-in-docker (#2438) We're deploying backstage to GKE and have had issues getting techdocs volume mount logic to work because of mixing paths between the host and the container running the backend. So we added a condition where if mkdocs is found in the backend container it will use that to generate the docs rather than spinning it up in a docker container. --- plugins/techdocs-backend/package.json | 1 + .../src/techdocs/stages/generate/helpers.ts | 46 +++++++++++++++++++ .../src/techdocs/stages/generate/techdocs.ts | 38 ++++++++++----- 3 files changed, 73 insertions(+), 12 deletions(-) diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 476b321e1e..b9c2953d28 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -25,6 +25,7 @@ "@backstage/config": "^0.1.1-alpha.21", "@types/dockerode": "^2.5.34", "@types/express": "^4.17.6", + "command-exists-promise": "^2.0.2", "default-branch": "^1.0.8", "dockerode": "^3.2.1", "express": "^4.17.1", diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.ts index 9f33823444..d68bb74aa6 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.ts @@ -18,6 +18,7 @@ import { Entity } from '@backstage/catalog-model'; import { Writable, PassThrough } from 'stream'; import Docker from 'dockerode'; import { SupportedGeneratorKey } from './types'; +import { spawn } from 'child_process'; // TODO: Implement proper support for more generators. export function getGeneratorKey(entity: Entity): SupportedGeneratorKey { @@ -38,6 +39,13 @@ type RunDockerContainerOptions = { createOptions?: Docker.ContainerCreateOptions; }; +export type RunCommandOptions = { + command: string; + args: string[]; + options: object; + logStream?: Writable; +}; + export async function runDockerContainer({ imageName, args, @@ -96,3 +104,41 @@ export async function runDockerContainer({ return { error, statusCode }; } + +/** + * + * @param options the options object + * @param options.command the command to run + * @param options.args the arguments to pass the command + * @param options.options options used in spawn + * @param options.logStream the log streamer to capture log messages + */ +export const runCommand = async ({ + command, + args, + options, + logStream = new PassThrough(), +}: RunCommandOptions) => { + await new Promise((resolve, reject) => { + const process = spawn(command, args, options); + + process.stdout.on('data', stream => { + logStream.write(stream); + }); + + process.stderr.on('data', stream => { + logStream.write(stream); + }); + + process.on('error', error => { + return reject(error); + }); + + process.on('close', code => { + if (code !== 0) { + return reject(`Command ${command} failed, exit code: ${code}`); + } + return resolve(); + }); + }); +}; diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts index 40cd7590b6..cb89b42e38 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts @@ -24,7 +24,9 @@ import { GeneratorRunOptions, GeneratorRunResult, } from './types'; -import { runDockerContainer } from './helpers'; +import { runDockerContainer, runCommand } from './helpers'; + +const commandExists = require('command-exists-promise'); export class TechdocsGenerator implements GeneratorBase { private readonly logger: Logger; @@ -46,17 +48,29 @@ export class TechdocsGenerator implements GeneratorBase { ); try { - await runDockerContainer({ - imageName: 'spotify/techdocs', - args: ['build', '-d', '/result'], - logStream, - docsDir: directory, - resultDir, - dockerClient, - }); - this.logger.info( - `[TechDocs]: Successfully generated docs from ${directory} into ${resultDir}`, - ); + const mkdocsInstalled = await commandExists('mkdocs'); + if (mkdocsInstalled) { + await runCommand({ + command: 'mkdocs', + args: ['build', '-d', resultDir, '-v'], + options: { + cwd: directory, + }, + logStream, + }); + } else { + await runDockerContainer({ + imageName: 'spotify/techdocs', + args: ['build', '-d', '/result'], + logStream, + docsDir: directory, + resultDir, + dockerClient, + }); + this.logger.info( + `[TechDocs]: Successfully generated docs from ${directory} into ${resultDir}`, + ); + } } catch (error) { this.logger.debug( `[TechDocs]: Failed to generate docs from ${directory} into ${resultDir}`,