mirror of
https://github.com/actions/runner-container-hooks.git
synced 2025-12-19 03:16:44 +00:00
refactored the api to accept remote registry, not complete yet
This commit is contained in:
@@ -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()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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`)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user