Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test-blacksmith.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ jobs:
- name: Checkout action repo
uses: actions/checkout@v4

- name: Assert BLACKSMITH_AGENT_ADDR is advertised
run: test -n "$BLACKSMITH_AGENT_ADDR"

- name: Test checkout with git mirror (self)
uses: ./
with:
Expand Down
14 changes: 14 additions & 0 deletions __test__/blacksmith-cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ describe('blacksmith-cache tests', () => {
beforeEach(() => {
jest.resetModules()
process.env = {...originalEnv}
process.env['BLACKSMITH_AGENT_ADDR'] = '192.168.127.1'
Comment thread
cursor[bot] marked this conversation as resolved.
process.env['BLACKSMITH_STICKY_DISK_GRPC_PORT'] = '5557'
mockIsRunningInContainer.mockReturnValue(false)
})

Expand All @@ -166,6 +168,18 @@ describe('blacksmith-cache tests', () => {
expect(blacksmithCache.shouldUseBlacksmithCache()).toBe(false)
})

it('returns false when BLACKSMITH_AGENT_ADDR is not set', () => {
process.env['BLACKSMITH_VM_ID'] = 'test-vm-id'
delete process.env['BLACKSMITH_AGENT_ADDR']
expect(blacksmithCache.shouldUseBlacksmithCache()).toBe(false)
})

it('returns false when BLACKSMITH_STICKY_DISK_GRPC_PORT is not set', () => {
process.env['BLACKSMITH_VM_ID'] = 'test-vm-id'
delete process.env['BLACKSMITH_STICKY_DISK_GRPC_PORT']
expect(blacksmithCache.shouldUseBlacksmithCache()).toBe(false)
})

it('returns false when BLACKSMITH_BYPASS_CHECKOUT=true (control-plane kill switch)', () => {
process.env['BLACKSMITH_VM_ID'] = 'test-vm-id'
process.env['BLACKSMITH_BYPASS_CHECKOUT'] = 'true'
Expand Down
46 changes: 38 additions & 8 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getMountPoint = getMountPoint;
exports.isBlacksmithEnvironment = isBlacksmithEnvironment;
exports.getAgentAddr = getAgentAddr;
exports.getGrpcPort = getGrpcPort;
exports.isAllowedInsideContainer = isAllowedInsideContainer;
exports.shouldUseBlacksmithCache = shouldUseBlacksmithCache;
exports.getMirrorPath = getMirrorPath;
Expand All @@ -59,7 +61,9 @@ const connect_node_1 = __nccwpck_require__(1125);
const stickydisk_connect_1 = __nccwpck_require__(2880);
const retryHelper = __importStar(__nccwpck_require__(2155));
const container_detector_1 = __nccwpck_require__(6424);
const GRPC_PORT = process.env.BLACKSMITH_STICKY_DISK_GRPC_PORT || '5557';
// Without a deadline, a black-holed dial stalls the checkout until the OS
// gives up on the TCP handshake.
const AGENT_RPC_TIMEOUT_MS = 45000;
const MOUNT_BASE = '/blacksmith-git-mirror';
const MIRROR_VERSION = 'v1';
const REFRESH_TIMEOUT_SECS = 120; // 2 minutes
Expand All @@ -86,6 +90,12 @@ function getMountPoint(owner, repo) {
function isBlacksmithEnvironment() {
return !!process.env.BLACKSMITH_VM_ID;
}
function getAgentAddr() {
return process.env.BLACKSMITH_AGENT_ADDR || undefined;
}
function getGrpcPort() {
return process.env.BLACKSMITH_STICKY_DISK_GRPC_PORT || undefined;
}
/**
* Escape hatch for container jobs that deliberately pass the runner's
* block devices through to the container (e.g. `options:
Expand Down Expand Up @@ -113,6 +123,10 @@ function shouldUseBlacksmithCache() {
if (!isBlacksmithEnvironment()) {
return false;
}
if (!getAgentAddr() || !getGrpcPort()) {
core.info('[blacksmith] BLACKSMITH_AGENT_ADDR or BLACKSMITH_STICKY_DISK_GRPC_PORT is not set; the Blacksmith agent is not reachable from this runner, falling back to actions/checkout behavior');
return false;
}
if (process.env.BLACKSMITH_BYPASS_CHECKOUT === 'true') {
core.info('[blacksmith] BLACKSMITH_BYPASS_CHECKOUT=true — skipping Blacksmith git mirror cache and falling back to actions/checkout behavior');
return false;
Expand Down Expand Up @@ -145,9 +159,14 @@ function getMirrorPath(owner, repo) {
* Create a gRPC client for communicating with the Blacksmith VM agent
*/
function createBlacksmithClient() {
core.debug(`Creating Blacksmith agent client with port: ${GRPC_PORT}`);
const addr = getAgentAddr();
const grpcPort = getGrpcPort();
if (!addr || !grpcPort) {
throw new Error('BLACKSMITH_AGENT_ADDR or BLACKSMITH_STICKY_DISK_GRPC_PORT is not set; cannot dial the Blacksmith agent');
}
core.debug(`Creating Blacksmith agent client for ${addr}:${grpcPort}`);
const transport = (0, connect_node_1.createGrpcTransport)({
baseUrl: `http://192.168.127.1:${GRPC_PORT}`,
baseUrl: `http://${addr}:${grpcPort}`,
httpVersion: '2'
});
return (0, connect_1.createClient)(stickydisk_connect_1.StickyDiskService, transport);
Expand Down Expand Up @@ -219,14 +238,18 @@ function setupCache(owner, repo) {
return __awaiter(this, void 0, void 0, function* () {
const client = createBlacksmithClient();
const stickyDiskKey = `${owner}-${repo}`;
// Test connection
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), AGENT_RPC_TIMEOUT_MS);
// Rethrow the original error so callers can classify it from the gRPC code.
core.info(`[git-mirror] Connecting to Blacksmith agent for ${stickyDiskKey}`);
try {
yield client.up({});
yield client.up({}, { signal: controller.signal });
core.debug('[git-mirror] Successfully connected to Blacksmith agent');
}
catch (error) {
throw new Error(`gRPC connection test failed: ${error.message}`);
clearTimeout(timeoutId);
core.warning(`[git-mirror] gRPC connection test failed: ${error.message}`);
throw error;
}
core.info(`[git-mirror] Requesting sticky disk for ${stickyDiskKey}`);
// Request sticky disk from VM agent
Expand All @@ -243,7 +266,7 @@ function setupCache(owner, repo) {
vmId: process.env.BLACKSMITH_VM_ID || '',
repoName: repoName,
stickyDiskToken: process.env.BLACKSMITH_STICKYDISK_TOKEN || ''
});
}, { signal: controller.signal });
}
catch (error) {
// Check if this is a gRPC Aborted error indicating hydration in progress
Expand All @@ -266,6 +289,9 @@ function setupCache(owner, repo) {
// Re-throw other errors
throw error;
}
finally {
clearTimeout(timeoutId);
}
const exposeId = response.exposeId || '';
const device = response.diskIdentifier || '';
if (!device) {
Expand Down Expand Up @@ -3034,7 +3060,7 @@ const core = __importStar(__nccwpck_require__(2186));
const http = __importStar(__nccwpck_require__(3685));
const METRICS_PORT = process.env.BLACKSMITH_METRICS_HTTP_PORT || '';
const VM_ID = process.env.BLACKSMITH_VM_ID || '';
const AGENT_IP = '192.168.127.1';
const AGENT_IP = process.env.BLACKSMITH_AGENT_ADDR || '';
/**
* Report an internal metric to the Blacksmith agent.
* Fire-and-forget: errors are logged but never thrown.
Expand All @@ -3045,6 +3071,10 @@ function reportInternalMetric(metricType, value, attributes) {
core.debug('[metrics] BLACKSMITH_METRICS_HTTP_PORT not set, skipping metric');
return;
}
if (!AGENT_IP) {
core.debug('[metrics] BLACKSMITH_AGENT_ADDR not set, skipping metric');
return;
}
const payload = JSON.stringify({
metric_type: metricType,
value,
Expand Down
65 changes: 50 additions & 15 deletions src/blacksmith-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {StickyDiskService} from '@buf/blacksmith_vm-agent.connectrpc_es/stickydi
import * as retryHelper from './retry-helper'
import {isRunningInContainer} from './container-detector'

const GRPC_PORT = process.env.BLACKSMITH_STICKY_DISK_GRPC_PORT || '5557'
// Without a deadline, a black-holed dial stalls the checkout until the OS
// gives up on the TCP handshake.
const AGENT_RPC_TIMEOUT_MS = 45000
const MOUNT_BASE = '/blacksmith-git-mirror'
const MIRROR_VERSION = 'v1'

Expand Down Expand Up @@ -72,6 +74,14 @@ export function isBlacksmithEnvironment(): boolean {
return !!process.env.BLACKSMITH_VM_ID
}

export function getAgentAddr(): string | undefined {
return process.env.BLACKSMITH_AGENT_ADDR || undefined
}

export function getGrpcPort(): string | undefined {
return process.env.BLACKSMITH_STICKY_DISK_GRPC_PORT || undefined
}

/**
* Escape hatch for container jobs that deliberately pass the runner's
* block devices through to the container (e.g. `options:
Expand Down Expand Up @@ -102,6 +112,12 @@ export function shouldUseBlacksmithCache(): boolean {
if (!isBlacksmithEnvironment()) {
return false
}
if (!getAgentAddr() || !getGrpcPort()) {
core.info(
'[blacksmith] BLACKSMITH_AGENT_ADDR or BLACKSMITH_STICKY_DISK_GRPC_PORT is not set; the Blacksmith agent is not reachable from this runner, falling back to actions/checkout behavior'
)
return false
}
if (process.env.BLACKSMITH_BYPASS_CHECKOUT === 'true') {
core.info(
'[blacksmith] BLACKSMITH_BYPASS_CHECKOUT=true — skipping Blacksmith git mirror cache and falling back to actions/checkout behavior'
Expand Down Expand Up @@ -141,9 +157,16 @@ export function getMirrorPath(owner: string, repo: string): string {
* Create a gRPC client for communicating with the Blacksmith VM agent
*/
function createBlacksmithClient() {
core.debug(`Creating Blacksmith agent client with port: ${GRPC_PORT}`)
const addr = getAgentAddr()
const grpcPort = getGrpcPort()
if (!addr || !grpcPort) {
throw new Error(
'BLACKSMITH_AGENT_ADDR or BLACKSMITH_STICKY_DISK_GRPC_PORT is not set; cannot dial the Blacksmith agent'
)
}
core.debug(`Creating Blacksmith agent client for ${addr}:${grpcPort}`)
const transport = createGrpcTransport({
baseUrl: `http://192.168.127.1:${GRPC_PORT}`,
baseUrl: `http://${addr}:${grpcPort}`,
httpVersion: '2'
})

Expand Down Expand Up @@ -228,13 +251,20 @@ export async function setupCache(
const client = createBlacksmithClient()
const stickyDiskKey = `${owner}-${repo}`

// Test connection
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), AGENT_RPC_TIMEOUT_MS)

// Rethrow the original error so callers can classify it from the gRPC code.
core.info(`[git-mirror] Connecting to Blacksmith agent for ${stickyDiskKey}`)
try {
await client.up({})
await client.up({}, {signal: controller.signal})
core.debug('[git-mirror] Successfully connected to Blacksmith agent')
} catch (error) {
throw new Error(`gRPC connection test failed: ${(error as Error).message}`)
clearTimeout(timeoutId)
core.warning(
`[git-mirror] gRPC connection test failed: ${(error as Error).message}`
)
throw error
}

core.info(`[git-mirror] Requesting sticky disk for ${stickyDiskKey}`)
Expand All @@ -245,15 +275,18 @@ export async function setupCache(
const repoName = `${owner}/${repo}`
let response
try {
response = await client.getStickyDisk({
stickyDiskKey: stickyDiskKey,
stickyDiskType: 'git_mirror',
region: process.env.BLACKSMITH_REGION || '',
installationModelId: process.env.BLACKSMITH_INSTALLATION_MODEL_ID || '',
vmId: process.env.BLACKSMITH_VM_ID || '',
repoName: repoName,
stickyDiskToken: process.env.BLACKSMITH_STICKYDISK_TOKEN || ''
})
response = await client.getStickyDisk(
{
stickyDiskKey: stickyDiskKey,
stickyDiskType: 'git_mirror',
region: process.env.BLACKSMITH_REGION || '',
installationModelId: process.env.BLACKSMITH_INSTALLATION_MODEL_ID || '',
vmId: process.env.BLACKSMITH_VM_ID || '',
repoName: repoName,
stickyDiskToken: process.env.BLACKSMITH_STICKYDISK_TOKEN || ''
},
{signal: controller.signal}
)
} catch (error) {
// Check if this is a gRPC Aborted error indicating hydration in progress
if (error instanceof ConnectError && error.code === Code.Aborted) {
Expand All @@ -279,6 +312,8 @@ export async function setupCache(
}
// Re-throw other errors
throw error
} finally {
clearTimeout(timeoutId)
}

const exposeId = (response as {exposeId?: string}).exposeId || ''
Expand Down
6 changes: 5 additions & 1 deletion src/internal-metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as http from 'http'

const METRICS_PORT = process.env.BLACKSMITH_METRICS_HTTP_PORT || ''
const VM_ID = process.env.BLACKSMITH_VM_ID || ''
const AGENT_IP = '192.168.127.1'
const AGENT_IP = process.env.BLACKSMITH_AGENT_ADDR || ''

/**
* Report an internal metric to the Blacksmith agent.
Expand All @@ -20,6 +20,10 @@ export async function reportInternalMetric(
)
return
}
if (!AGENT_IP) {
core.debug('[metrics] BLACKSMITH_AGENT_ADDR not set, skipping metric')
return
}

const payload = JSON.stringify({
metric_type: metricType,
Expand Down
Loading