diff --git a/__tests__/buildx/buildx.test.ts b/__tests__/buildx/buildx.test.ts index d7c6445..b56bd70 100644 --- a/__tests__/buildx/buildx.test.ts +++ b/__tests__/buildx/buildx.test.ts @@ -24,6 +24,8 @@ import * as exec from '@actions/exec'; import {Buildx} from '../../src/buildx/buildx'; import {Context} from '../../src/context'; +import {Cert} from '../../src/types/buildx'; + // prettier-ignore const tmpDir = path.join(process.env.TEMP || '/tmp', 'buildx-jest').split(path.sep).join(path.posix.sep); const tmpName = path.join(tmpDir, '.tmpname-jest').split(path.sep).join(path.posix.sep); @@ -192,3 +194,88 @@ describe('versionSatisfies', () => { expect(await buildx.versionSatisfies(range, version)).toBe(expected); }); }); + +describe('resolveCertsDriverOpts', () => { + const originalEnv = process.env; + beforeEach(() => { + jest.resetModules(); + process.env = { + ...originalEnv, + BUILDX_CONFIG: path.join(tmpDir, 'resolveCertsDriverOpts', 'buildx') + }; + }); + afterEach(() => { + process.env = originalEnv; + rimraf.sync(path.join(tmpDir, 'resolveCertsDriverOpts', 'buildx')); + }); + // prettier-ignore + test.each([ + [ + 1, + 'mycontext', + 'docker-container', + {}, + [], + [] + ], + [ + 2, + 'docker-container://mycontainer', + 'docker-container', + {}, + [], + [] + ], + [ + 3, + 'tcp://graviton2:1234', + 'remote', + {}, + [], + [] + ], + [ + 4, + 'tcp://graviton2:1234', + 'remote', + { + cacert: 'foo', + cert: 'foo', + key: 'foo', + } as Cert, + [ + path.join(tmpDir, 'resolveCertsDriverOpts', 'buildx', 'certs', 'cacert_graviton2-1234.pem'), + path.join(tmpDir, 'resolveCertsDriverOpts', 'buildx', 'certs', 'cert_graviton2-1234.pem'), + path.join(tmpDir, 'resolveCertsDriverOpts', 'buildx', 'certs', 'key_graviton2-1234.pem') + ], + [ + `cacert=${path.join(tmpDir, 'resolveCertsDriverOpts', 'buildx', 'certs', 'cacert_graviton2-1234.pem')}`, + `cert=${path.join(tmpDir, 'resolveCertsDriverOpts', 'buildx', 'certs', 'cert_graviton2-1234.pem')}`, + `key=${path.join(tmpDir, 'resolveCertsDriverOpts', 'buildx', 'certs', 'key_graviton2-1234.pem')}` + ] + ], + [ + 5, + 'tcp://mybuilder:1234', + 'docker-container', + { + cacert: 'foo', + cert: 'foo', + key: 'foo', + } as Cert, + [ + path.join(tmpDir, 'resolveCertsDriverOpts', 'buildx', 'certs', 'cacert_mybuilder-1234.pem'), + path.join(tmpDir, 'resolveCertsDriverOpts', 'buildx', 'certs', 'cert_mybuilder-1234.pem'), + path.join(tmpDir, 'resolveCertsDriverOpts', 'buildx', 'certs', 'key_mybuilder-1234.pem') + ], + [] + ], + ])('%p. given %p endpoint, %p driver', async (id: number, endpoint: string, driver: string, cert: Cert, expectedFiles: Array, expectedOpts: Array) => { + fs.mkdirSync(Buildx.certsDir, {recursive: true}); + expect(Buildx.resolveCertsDriverOpts(driver, endpoint, cert)).toEqual(expectedOpts); + for (const k in expectedFiles) { + const file = expectedFiles[k]; + expect(fs.existsSync(file)).toBe(true); + } + }); +}); diff --git a/src/buildx/buildx.ts b/src/buildx/buildx.ts index 7dac1d7..2658733 100644 --- a/src/buildx/buildx.ts +++ b/src/buildx/buildx.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import fs from 'fs'; import path from 'path'; import * as exec from '@actions/exec'; import * as semver from 'semver'; @@ -23,6 +24,8 @@ import {Context} from '../context'; import {Inputs} from './inputs'; import {Install} from './install'; +import {Cert} from '../types/buildx'; + export interface BuildxOpts { context: Context; standalone?: boolean; @@ -126,4 +129,43 @@ export class Buildx { } return semver.satisfies(ver, range) || /^[0-9a-f]{7}$/.exec(ver) !== null; } + + public static resolveCertsDriverOpts(driver: string, endpoint: string, cert: Cert): Array { + let url: URL; + try { + url = new URL(endpoint); + } catch (e) { + return []; + } + if (url.protocol != 'tcp:') { + return []; + } + const driverOpts: Array = []; + if (Object.keys(cert).length == 0) { + return driverOpts; + } + let host = url.hostname; + if (url.port.length > 0) { + host += `-${url.port}`; + } + if (cert.cacert !== undefined) { + const cacertpath = path.join(Buildx.certsDir, `cacert_${host}.pem`); + fs.writeFileSync(cacertpath, cert.cacert); + driverOpts.push(`cacert=${cacertpath}`); + } + if (cert.cert !== undefined) { + const certpath = path.join(Buildx.certsDir, `cert_${host}.pem`); + fs.writeFileSync(certpath, cert.cert); + driverOpts.push(`cert=${certpath}`); + } + if (cert.key !== undefined) { + const keypath = path.join(Buildx.certsDir, `key_${host}.pem`); + fs.writeFileSync(keypath, cert.key); + driverOpts.push(`key=${keypath}`); + } + if (driver != 'remote') { + return []; + } + return driverOpts; + } } diff --git a/src/types/buildx.ts b/src/types/buildx.ts new file mode 100644 index 0000000..64be2be --- /dev/null +++ b/src/types/buildx.ts @@ -0,0 +1,21 @@ +/** + * Copyright 2023 actions-toolkit authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface Cert { + cacert?: string; + cert?: string; + key?: string; +}