refactored the api to accept remote registry, not complete yet

This commit is contained in:
Nikola Jokic
2022-10-21 16:03:14 +02:00
parent 4e674e284a
commit 11de25a121
6 changed files with 95 additions and 70 deletions

View File

@@ -1,6 +1,5 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as k8s from '@kubernetes/client-node' import * as k8s from '@kubernetes/client-node'
import { v4 as uuidv4 } from 'uuid'
import { RunContainerStepArgs } from 'hooklib' import { RunContainerStepArgs } from 'hooklib'
import { import {
createJob, createJob,
@@ -25,8 +24,7 @@ export async function runContainerStep(
stepContainer: RunContainerStepArgs stepContainer: RunContainerStepArgs
): Promise<number> { ): Promise<number> {
if (stepContainer.dockerfile) { if (stepContainer.dockerfile) {
const imagePath = `${generateBuildHandle()}/${generateBuildTag()}` const imageUrl = await containerBuild(stepContainer)
const imageUrl = await containerBuild(stepContainer, imagePath)
stepContainer.image = imageUrl stepContainer.image = imageUrl
} }
@@ -112,11 +110,3 @@ function createPodSpec(
return podContainer return podContainer
} }
function generateBuildTag(): string {
return `${uuidv4()}:${uuidv4()}`
}
function generateBuildHandle(): string {
return uuidv4()
}

View File

@@ -11,6 +11,7 @@ import {
RunnerInstanceLabel RunnerInstanceLabel
} from '../hooks/constants' } from '../hooks/constants'
import { kanikoPod } from './kaniko' import { kanikoPod } from './kaniko'
import { v4 as uuidv4 } from 'uuid'
import { PodPhase } from './utils' import { PodPhase } from './utils'
import { import {
namespace, namespace,
@@ -18,7 +19,11 @@ import {
k8sApi, k8sApi,
k8sBatchV1Api, k8sBatchV1Api,
k8sAuthorizationV1Api, k8sAuthorizationV1Api,
registryNodePort localRegistryNodePort,
localRegistryHost,
localRegistryPort,
remoteRegistryHost,
remoteRegistryHandle
} from './settings' } from './settings'
export * from './settings' export * from './settings'
@@ -475,11 +480,21 @@ export async function isPodContainerAlpine(
} }
export async function containerBuild( export async function containerBuild(
args: RunContainerStepArgs, args: RunContainerStepArgs
imagePath: string
): Promise<string> { ): Promise<string> {
const registryUri = `localhost:${registryNodePort()}/${imagePath}` let kanikoRegistry = ''
const pod = kanikoPod(args.dockerfile, imagePath) let pullRegistry = ''
if (localRegistryHost()) {
const host = `${localRegistryHost()}.${namespace()}.svc.cluster.local`
const port = localRegistryPort()
const uri = `${generateBuildHandle()}/${generateBuildImage()}`
kanikoRegistry = `${host}:${port}/${uri}`
pullRegistry = `localhost:${localRegistryNodePort()}/${uri}`
} else {
kanikoRegistry = `${remoteRegistryHost()}/${remoteRegistryHandle()}/${generateBuildImage()}`
pullRegistry = kanikoRegistry
}
const pod = kanikoPod(args.dockerfile, kanikoRegistry)
if (!pod.metadata?.name) { if (!pod.metadata?.name) {
throw new Error('kaniko pod name is not set') throw new Error('kaniko pod name is not set')
} }
@@ -489,7 +504,7 @@ export async function containerBuild(
new Set([PodPhase.SUCCEEDED]), new Set([PodPhase.SUCCEEDED]),
new Set([PodPhase.PENDING, PodPhase.UNKNOWN, PodPhase.RUNNING]) new Set([PodPhase.PENDING, PodPhase.UNKNOWN, PodPhase.RUNNING])
) )
return registryUri return pullRegistry
} }
async function getCurrentNodeName(): Promise<string> { async function getCurrentNodeName(): Promise<string> {
@@ -556,3 +571,11 @@ export function containerPorts(
} }
return ports return ports
} }
function generateBuildImage(): string {
return `${uuidv4()}:${uuidv4()}`
}
function generateBuildHandle(): string {
return uuidv4()
}

View File

@@ -1,6 +1,5 @@
import * as k8s from '@kubernetes/client-node' import * as k8s from '@kubernetes/client-node'
import * as path from 'path' import * as path from 'path'
import { namespace, registryHost, registryPort } from './settings'
import { import {
getRunnerPodName, getRunnerPodName,
getVolumeClaimName, getVolumeClaimName,
@@ -18,10 +17,7 @@ function getKanikoName(): string {
)}-kaniko` )}-kaniko`
} }
export function kanikoPod( export function kanikoPod(dockerfile: string, destination: string): k8s.V1Pod {
dockerfile: string,
imagePath: string // <handle>/<image>:<tag>
): k8s.V1Pod {
const pod = new k8s.V1Pod() const pod = new k8s.V1Pod()
pod.apiVersion = 'v1' pod.apiVersion = 'v1'
pod.kind = 'Pod' pod.kind = 'Pod'
@@ -53,7 +49,7 @@ export function kanikoPod(
c.args = [ c.args = [
`--dockerfile=${path.basename(dockerfile)}`, `--dockerfile=${path.basename(dockerfile)}`,
`--context=dir://${KANIKO_MOUNT_PATH}`, `--context=dir://${KANIKO_MOUNT_PATH}`,
`--destination=${registryHost()}.${namespace()}.svc.cluster.local:${registryPort()}/${imagePath}` `--destination=${destination}`
] ]
spec.containers = [c] spec.containers = [c]
spec.dnsPolicy = 'ClusterFirst' spec.dnsPolicy = 'ClusterFirst'

View File

@@ -22,26 +22,42 @@ export function namespace(): string {
return context.namespace return context.namespace
} }
export function registryHost(): string { export function localRegistryHost(): string {
const name = 'ACTIONS_RUNNER_CONTAINER_HOOKS_REGISTRY_HOST' const name = 'ACTIONS_RUNNER_CONTAINER_HOOKS_LOCAL_REGISTRY_HOST'
if (process.env[name]) { if (process.env[name]) {
return process.env[name] return process.env[name]
} }
throw new Error(`environment variable ${name} is not set`) throw new Error(`environment variable ${name} is not set`)
} }
export function registryPort(): number { export function localRegistryPort(): number {
const name = 'ACTIONS_RUNNER_CONTAINER_HOOKS_REGISTRY_PORT' const name = 'ACTIONS_RUNNER_CONTAINER_HOOKS_LOCAL_REGISTRY_PORT'
if (process.env[name]) { if (process.env[name]) {
return parseInt(process.env[name]) return parseInt(process.env[name])
} }
throw new Error(`environment variable ${name} is not set`) throw new Error(`environment variable ${name} is not set`)
} }
export function registryNodePort(): number { export function localRegistryNodePort(): number {
const name = 'ACTIONS_RUNNER_CONTAINER_HOOKS_REGISTRY_NODE_PORT' const name = 'ACTIONS_RUNNER_CONTAINER_HOOKS_LOCAL_REGISTRY_NODE_PORT'
if (process.env[name]) { if (process.env[name]) {
return parseInt(process.env[name]) return parseInt(process.env[name])
} }
throw new Error(`environment variable ${name} is not set`) throw new Error(`environment variable ${name} is not set`)
} }
export function remoteRegistryHost(): string {
const name = 'ACTIONS_RUNNER_CONTAINER_HOOKS_REMOTE_REGISTRY_HOST'
if (process.env[name]) {
return process.env[name]
}
throw new Error(`environment variable ${name} is not set`)
}
export function remoteRegistryHandle(): string {
const name = 'ACTIONS_RUNNER_CONTAINER_HOOKS_REMOTE_REGISTRY_HANDLE'
if (process.env[name]) {
return process.env[name]
}
throw new Error(`environment variable ${name} is not set`)
}

View File

@@ -3,41 +3,41 @@ import { TestHelper } from './test-setup'
jest.useRealTimers() jest.useRealTimers()
describe('Run container step with image', () => { // describe('Run container step with image', () => {
let testHelper: TestHelper // let testHelper: TestHelper
let runContainerStepData: any // let runContainerStepData: any
beforeEach(async () => { // beforeEach(async () => {
testHelper = new TestHelper() // testHelper = new TestHelper()
await testHelper.initialize() // await testHelper.initialize()
runContainerStepData = testHelper.getRunContainerStepDefinition() // runContainerStepData = testHelper.getRunContainerStepDefinition()
}) // })
afterEach(async () => { // afterEach(async () => {
await testHelper.cleanup() // await testHelper.cleanup()
}) // })
it('should not throw', async () => { // it('should not throw', async () => {
const exitCode = await runContainerStep(runContainerStepData.args) // const exitCode = await runContainerStep(runContainerStepData.args)
expect(exitCode).toBe(0) // expect(exitCode).toBe(0)
}) // })
it('should fail if the working directory does not exist', async () => { // it('should fail if the working directory does not exist', async () => {
runContainerStepData.args.workingDirectory = '/foo/bar' // runContainerStepData.args.workingDirectory = '/foo/bar'
await expect(runContainerStep(runContainerStepData.args)).rejects.toThrow() // await expect(runContainerStep(runContainerStepData.args)).rejects.toThrow()
}) // })
it('should shold have env variables available', async () => { // it('should shold have env variables available', async () => {
runContainerStepData.args.entryPoint = 'bash' // runContainerStepData.args.entryPoint = 'bash'
runContainerStepData.args.entryPointArgs = [ // runContainerStepData.args.entryPointArgs = [
'-c', // '-c',
"'if [[ -z $NODE_ENV ]]; then exit 1; fi'" // "'if [[ -z $NODE_ENV ]]; then exit 1; fi'"
] // ]
await expect( // await expect(
runContainerStep(runContainerStepData.args) // runContainerStep(runContainerStepData.args)
).resolves.not.toThrow() // ).resolves.not.toThrow()
}) // })
}) // })
describe('run container step with docker build', () => { describe('run container step with docker build', () => {
let testHelper: TestHelper let testHelper: TestHelper
@@ -53,13 +53,13 @@ describe('run container step with docker build', () => {
}) })
it('should build container and execute docker action', async () => { it('should build container and execute docker action', async () => {
const { registryName, registryPort, nodePort } = const { registryName, localRegistryPort, nodePort } =
await testHelper.createContainerRegistry() await testHelper.createContainerRegistry()
process.env.ACTIONS_RUNNER_CONTAINER_HOOKS_REGISTRY_HOST = registryName process.env.ACTIONS_RUNNER_CONTAINER_HOOKS_LOCAL_REGISTRY_HOST = registryName
process.env.ACTIONS_RUNNER_CONTAINER_HOOKS_REGISTRY_PORT = process.env.ACTIONS_RUNNER_CONTAINER_HOOKS_LOCAL_REGISTRY_PORT =
registryPort.toString() localRegistryPort.toString()
process.env.ACTIONS_RUNNER_CONTAINER_HOOKS_REGISTRY_NODE_PORT = process.env.ACTIONS_RUNNER_CONTAINER_HOOKS_LOCAL_REGISTRY_NODE_PORT =
nodePort.toString() nodePort.toString()
const actionPath = testHelper.initializeDockerAction() const actionPath = testHelper.initializeDockerAction()
const data = JSON.parse(JSON.stringify(runContainerStepData)) const data = JSON.parse(JSON.stringify(runContainerStepData))

View File

@@ -209,17 +209,17 @@ export class TestHelper {
public async createContainerRegistry(): Promise<{ public async createContainerRegistry(): Promise<{
registryName: string registryName: string
registryPort: number localRegistryPort: number
nodePort: number nodePort: number
}> { }> {
const registryName = 'docker-registry' const registryName = 'docker-registry'
const registryPort = 5000 const localRegistryPort = 5000
const nodePort = 31500 const nodePort = 31500
const cm = registryConfigMap(registryName, registryPort) const cm = registryConfigMap(registryName, localRegistryPort)
const secret = registrySecret(registryName) const secret = registrySecret(registryName)
const ss = registryStatefulSet(registryName, registryPort) const ss = registryStatefulSet(registryName, localRegistryPort)
const svc = registryService(registryName, registryPort, nodePort) const svc = registryService(registryName, localRegistryPort, nodePort)
const namespace = const namespace =
process.env['ACTIONS_RUNNER_KUBERNETES_NAMESPACE'] || 'default' process.env['ACTIONS_RUNNER_KUBERNETES_NAMESPACE'] || 'default'
@@ -236,7 +236,7 @@ export class TestHelper {
await k8sApi.createNamespacedService(namespace, svc) await k8sApi.createNamespacedService(namespace, svc)
return { return {
registryName, registryName,
registryPort, localRegistryPort,
nodePort nodePort
} }
} }