From c9ae7b02808eb0c0c8dbb6a2ed1538243abe617d Mon Sep 17 00:00:00 2001 From: Barry Gordon Date: Tue, 19 Oct 2021 16:22:02 +0100 Subject: [PATCH] Mount the absolute path generated from inputs into docker --- __fixtures__/output/empty/.keep | 0 __tests__/updater-integration.test.ts | 18 +++++- __tests__/updater.test.ts | 83 +++++++++++++++++++++++---- src/main.ts | 3 +- src/updater.ts | 27 +++++---- 5 files changed, 105 insertions(+), 26 deletions(-) delete mode 100644 __fixtures__/output/empty/.keep diff --git a/__fixtures__/output/empty/.keep b/__fixtures__/output/empty/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/__tests__/updater-integration.test.ts b/__tests__/updater-integration.test.ts index 64b5793..239ae76 100644 --- a/__tests__/updater-integration.test.ts +++ b/__tests__/updater-integration.test.ts @@ -1,5 +1,6 @@ import axios from 'axios' - +import fs from 'fs' +import path from 'path' import {ApiClient} from '../src/api-client' import {ImageService} from '../src/image-service' import {JobParameters} from '../src/inputs' @@ -19,13 +20,20 @@ describe('Updater', () => { const internalDockerHost = process.platform === 'darwin' ? 'host.docker.internal' : '172.17.0.1' const dependabotApiDockerUrl = `http://${internalDockerHost}:${FAKE_SERVER_PORT}` + const workingDirectory = path.join( + __dirname, + '..', + 'tmp', + './integration_working_directory' + ) + const params = new JobParameters( 1, 'job-token', 'cred-token', dependabotApiUrl, dependabotApiDockerUrl, - 'nyi-workingDirectory' + workingDirectory ) const client = axios.create({baseURL: dependabotApiUrl}) @@ -41,11 +49,14 @@ describe('Updater', () => { await ImageService.pull(PROXY_IMAGE_NAME) server = await runFakeDependabotApi(FAKE_SERVER_PORT) + + fs.mkdirSync(workingDirectory) }) afterEach(async () => { server && server() // teardown server process await removeDanglingUpdaterContainers() + fs.rmdirSync(workingDirectory, {recursive: true}) }) jest.setTimeout(120000) @@ -63,7 +74,8 @@ describe('Updater', () => { PROXY_IMAGE_NAME, apiClient, details, - credentials + credentials, + workingDirectory ) await updater.runUpdater() diff --git a/__tests__/updater.test.ts b/__tests__/updater.test.ts index 485a044..afd27f9 100644 --- a/__tests__/updater.test.ts +++ b/__tests__/updater.test.ts @@ -1,3 +1,5 @@ +import fs from 'fs' +import path from 'path' import {Updater} from '../src/updater' import Docker from 'dockerode' import {ContainerService} from '../src/container-service' @@ -8,6 +10,17 @@ jest.mock('dockerode') jest.mock('../src/container-service') jest.mock('../src/proxy') +const outputFixturePath = (fixtureName: string): string => { + return path.join( + __dirname, + '..', + '__fixtures__', + 'output', + fixtureName, + 'output.json' + ) +} + describe('Updater', () => { const mockApiClient: any = { getJobDetails: jest.fn(), @@ -45,8 +58,22 @@ describe('Updater', () => { id: 1 } + const workingDirectory = path.join( + __dirname, + '..', + 'tmp', + './test_working_directory' + ) + + const outputFilePath = path.join(workingDirectory, 'output', 'output.json') + + beforeEach(async () => { + fs.mkdirSync(workingDirectory) + }) + afterEach(async () => { jest.clearAllMocks() // Reset any mocked classes + fs.rmdirSync(workingDirectory, {recursive: true}) }) describe('when there is a happy path update', () => { @@ -56,16 +83,32 @@ describe('Updater', () => { mockApiClient, mockJobDetails, [], - '../__fixtures__/output/happy_path/output.json' + workingDirectory ) + const outputFixture = outputFixturePath('happy_path') + beforeEach(async () => { jest .spyOn(Docker.prototype, 'createContainer') .mockResolvedValue(mockContainer) jest.spyOn(ProxyBuilder.prototype, 'run').mockResolvedValue(mockProxy) - jest.spyOn(ContainerService, 'run').mockImplementation(jest.fn()) + jest + .spyOn(ContainerService, 'run') + .mockImplementationOnce( + jest.fn(async () => { + fs.copyFileSync(outputFixture, outputFilePath) + return true + }) + ) + .mockImplementationOnce( + jest.fn( + jest.fn(async () => { + return true + }) + ) + ) }) it('should be successful', async () => { @@ -80,7 +123,7 @@ describe('Updater', () => { mockApiClient, mockJobDetails, [], - '../__fixtures__/output/happy_path/output.json' + workingDirectory ) beforeEach(async () => { @@ -100,7 +143,9 @@ describe('Updater', () => { }) it('should raise an error', async () => { - await expect(updater.runUpdater()).rejects.toThrow() + await expect(updater.runUpdater()).rejects.toThrow( + 'First call to container service errored' + ) }) }) @@ -111,9 +156,11 @@ describe('Updater', () => { mockApiClient, mockJobDetails, [], - '../__fixtures__/output/happy_path/output.json' + workingDirectory ) + const outputFixture = outputFixturePath('happy_path') + beforeEach(async () => { jest .spyOn(Docker.prototype, 'createContainer') @@ -123,7 +170,12 @@ describe('Updater', () => { jest .spyOn(ContainerService, 'run') - .mockImplementationOnce(jest.fn()) + .mockImplementationOnce( + jest.fn(async () => { + fs.copyFileSync(outputFixture, outputFilePath) + return true + }) + ) .mockImplementationOnce( jest.fn(async () => Promise.reject( @@ -134,7 +186,9 @@ describe('Updater', () => { }) it('should raise an error', async () => { - await expect(updater.runUpdater()).rejects.toThrow() + await expect(updater.runUpdater()).rejects.toThrow( + 'Second call to container service errored' + ) }) }) @@ -145,7 +199,7 @@ describe('Updater', () => { mockApiClient, mockJobDetails, [], - '../__fixtures__/output/empty/output.json' + workingDirectory ) beforeEach(async () => { @@ -172,9 +226,11 @@ describe('Updater', () => { mockApiClient, mockJobDetails, [], - '../__fixtures__/output/malformed/output.json' + workingDirectory ) + const outputFixture = outputFixturePath('malformed') + beforeEach(async () => { jest .spyOn(Docker.prototype, 'createContainer') @@ -182,11 +238,16 @@ describe('Updater', () => { jest.spyOn(ProxyBuilder.prototype, 'run').mockResolvedValue(mockProxy) - jest.spyOn(ContainerService, 'run').mockImplementation(jest.fn()) + jest.spyOn(ContainerService, 'run').mockImplementation( + jest.fn(async () => { + fs.copyFileSync(outputFixture, outputFilePath) + return true + }) + ) }) it('should raise an error', async () => { - await expect(updater.runUpdater()).rejects.toThrow() + await expect(updater.runUpdater()).rejects.toThrow(/Unexpected token/) }) }) }) diff --git a/src/main.ts b/src/main.ts index 0b1d5f3..fe697f6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -52,7 +52,8 @@ export async function run(context: Context): Promise { PROXY_IMAGE_NAME, apiClient, details, - credentials + credentials, + params.workingDirectory ) try { diff --git a/src/updater.ts b/src/updater.ts index 33474bc..c6acd71 100644 --- a/src/updater.ts +++ b/src/updater.ts @@ -18,6 +18,8 @@ const CA_CERT_FILENAME = 'dbot-ca.crt' export class Updater { docker: Docker + outputHostPath: string + repoHostPath: string constructor( private readonly updaterImage: string, @@ -25,15 +27,21 @@ export class Updater { private readonly apiClient: ApiClient, private readonly details: JobDetails, private readonly credentials: Credential[], - private readonly outputPath = '../../output/output.json' + private readonly workingDirectory: string ) { this.docker = new Docker() + this.outputHostPath = path.join(workingDirectory, 'output') + this.repoHostPath = path.join(workingDirectory, 'repo') } /** * Execute an update job and report the result to Dependabot API. */ async runUpdater(): Promise { + // Create required folders in the workingDirectory + fs.mkdirSync(this.outputHostPath) + fs.mkdirSync(this.repoHostPath) + const proxy = await new ProxyBuilder(this.docker, this.proxyImage).run( this.apiClient.params.jobId, this.credentials @@ -67,7 +75,7 @@ export class Updater { await ContainerService.run(container) - const outputPath = path.join(__dirname, this.outputPath) + const outputPath = path.join(this.outputHostPath, 'output.json') if (!fs.existsSync(outputPath)) { throw new Error('No output.json created by the fetcher container') } @@ -148,8 +156,8 @@ export class Updater { Memory: 8 * 1024 * 1024 * 1024, // 8GB in bytes NetworkMode: proxy.networkName, Binds: [ - `${path.join(__dirname, '../../output')}:${JOB_OUTPUT_PATH}:rw`, - `${path.join(__dirname, '../../repo')}:${REPO_CONTENTS_PATH}:rw` + `${this.outputHostPath}:${JOB_OUTPUT_PATH}:rw`, + `${this.repoHostPath}:${REPO_CONTENTS_PATH}:rw` ] } }) @@ -161,15 +169,12 @@ export class Updater { private async cleanup(proxy: Proxy): Promise { await proxy.shutdown() - const outputDir = path.join(__dirname, '../../output') - const repoDir = path.join(__dirname, '../../repo') - - if (fs.existsSync(outputDir)) { - fs.rmdirSync(outputDir, {recursive: true}) + if (fs.existsSync(this.outputHostPath)) { + fs.rmdirSync(this.outputHostPath, {recursive: true}) } - if (fs.existsSync(repoDir)) { - fs.rmdirSync(repoDir, {recursive: true}) + if (fs.existsSync(this.repoHostPath)) { + fs.rmdirSync(this.repoHostPath, {recursive: true}) } } }