src: start sending get request with query params

We are incorrectly using formData in a get request. To move
away from this we send both query params and formData until
the server is fully upgraded. After which we can stop sending
formData.
pull/1358/head
Aditya Maru 2024-12-09 10:28:36 -05:00
parent 0186286e06
commit 0f99a0b1c7
8 changed files with 131 additions and 58 deletions

View File

@ -30,8 +30,6 @@ jobs:
- name: Check for changes
run: |
# Ignore yarn.lock changes since we're using npm
git update-index --assume-unchanged yarn.lock || true
if [[ -n "$(git status --porcelain)" ]]; then
echo "::error::Build generated new changes. Please commit the generated files."
git status

2
dist/index.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

48
package-lock.json generated
View File

@ -11,6 +11,7 @@
"@docker/actions-toolkit": "0.37.1",
"@iarna/toml": "^2.2.5",
"axios-retry": "^4.5.0",
"form-data": "^4.0.1",
"handlebars": "^4.7.7",
"portfinder": "^1.0.32"
},
@ -511,6 +512,20 @@
"xml2js": "^0.5.0"
}
},
"node_modules/@azure/ms-rest-js/node_modules/form-data": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz",
"integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12",
"safe-buffer": "^5.2.1"
},
"engines": {
"node": ">= 0.12"
}
},
"node_modules/@azure/ms-rest-js/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@ -2579,9 +2594,9 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"version": "1.7.9",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
"peer": true,
"dependencies": {
"follow-redirects": "^1.15.6",
@ -2600,20 +2615,6 @@
"axios": "0.x || 1.x"
}
},
"node_modules/axios/node_modules/form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"peer": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/b4a": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
@ -3996,17 +3997,16 @@
}
},
"node_modules/form-data": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz",
"integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12",
"safe-buffer": "^5.2.1"
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 0.12"
"node": ">= 6"
}
},
"node_modules/fs.realpath": {

View File

@ -30,6 +30,7 @@
"@docker/actions-toolkit": "0.37.1",
"@iarna/toml": "^2.2.5",
"axios-retry": "^4.5.0",
"form-data": "^4.0.1",
"handlebars": "^4.7.7",
"portfinder": "^1.0.32"
},

View File

@ -0,0 +1,54 @@
import * as reporter from '../reporter';
import { getStickyDisk } from '../setup_builder';
import FormData from 'form-data';
jest.mock('../reporter');
describe('getStickyDisk', () => {
const mockGet = jest.fn();
beforeEach(() => {
jest.resetAllMocks();
process.env.GITHUB_REPO_NAME = 'test-repo';
process.env.BLACKSMITH_REGION = 'test-region';
process.env.BLACKSMITH_INSTALLATION_MODEL_ID = 'test-model';
process.env.VM_ID = 'test-vm';
(reporter.createBlacksmithAgentClient as jest.Mock).mockResolvedValue({});
(reporter.get as jest.Mock).mockImplementation(mockGet);
mockGet.mockResolvedValue({
data: {
expose_id: 'test-expose-id',
disk_identifier: 'test-device'
}
});
});
it('sets both FormData and query parameters correctly', async () => {
const appendSpy = jest.spyOn(FormData.prototype, 'append');
await getStickyDisk();
expect(mockGet).toHaveBeenCalledTimes(1);
const [, url, formData] = mockGet.mock.calls[0];
// Verify query parameters
expect(url).toContain('stickyDiskKey=test-repo');
expect(url).toContain('region=test-region');
expect(url).toContain('installationModelID=test-model');
expect(url).toContain('vmID=test-vm');
// Verify FormData is correct type
expect(formData instanceof FormData).toBeTruthy();
// Verify the headers are set correctly
const headers = formData.getHeaders();
expect(headers['content-type']).toContain('multipart/form-data');
// Verify the correct fields were appended
expect(appendSpy).toHaveBeenCalledWith('stickyDiskKey', 'test-repo');
expect(appendSpy).toHaveBeenCalledWith('region', 'test-region');
expect(appendSpy).toHaveBeenCalledWith('installationModelID', 'test-model');
expect(appendSpy).toHaveBeenCalledWith('vmID', 'test-vm');
});
});

View File

@ -2,6 +2,7 @@ import * as core from '@actions/core';
import axios, {AxiosError, AxiosInstance, AxiosResponse, AxiosStatic } from 'axios';
import axiosRetry from 'axios-retry';
import {ExportRecordResponse} from '@docker/actions-toolkit/lib/types/buildx/history';
import FormData from 'form-data';
// Configure base axios instance for Blacksmith API.
const createBlacksmithAPIClient = () => {
@ -33,7 +34,11 @@ const createBlacksmithAPIClient = () => {
export async function createBlacksmithAgentClient(): Promise<AxiosInstance> {
const stickyDiskMgrUrl = 'http://192.168.127.1:5556';
const client = axios.create({
baseURL: stickyDiskMgrUrl
baseURL: stickyDiskMgrUrl,
headers: {
Authorization: `Bearer ${process.env.BLACKSMITH_STICKYDISK_TOKEN}`,
'X-Github-Repo-Name': process.env.GITHUB_REPO_NAME || '',
}
});
axiosRetry(client, {
@ -78,11 +83,7 @@ export async function reportBuildCompleted(exportRes?: ExportRecordResponse, bla
formData.append('exposeID', exposeId || '');
formData.append('stickyDiskKey', process.env.GITHUB_REPO_NAME || '');
await agentClient.post('/stickydisks', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
await post(agentClient, '/stickydisks', formData);
// Report success to Blacksmith API
const requestOptions = {
@ -131,11 +132,7 @@ export async function reportBuildFailed(dockerBuildId: string | null, dockerBuil
formData.append('exposeID', exposeId || '');
formData.append('stickyDiskKey', process.env.GITHUB_REPO_NAME || '');
await blacksmithAgentClient.post('/stickydisks', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
await post(blacksmithAgentClient, '/stickydisks', formData);
// Report failure to Blacksmith API
const requestOptions = {
@ -180,9 +177,18 @@ export async function get(client: AxiosInstance, url: string, formData: FormData
return await client.get(url, {
...(formData && {data: formData}),
headers: {
Authorization: `Bearer ${process.env.BLACKSMITH_STICKYDISK_TOKEN}`,
'X-Github-Repo-Name': process.env.GITHUB_REPO_NAME || '',
'Content-Type': 'multipart/form-data'
...client.defaults.headers.common,
...(formData && {'Content-Type': 'multipart/form-data'})
},
signal: options?.signal
});
}
export async function post(client: AxiosInstance, url: string, formData: FormData | null, options?: {signal?: AbortSignal}): Promise<AxiosResponse> {
return await client.post(url, formData, {
headers: {
...client.defaults.headers.common,
...(formData && { 'Content-Type': 'multipart/form-data' }),
},
signal: options?.signal
});

View File

@ -4,6 +4,7 @@ import {exec} from 'child_process';
import {promisify} from 'util';
import * as TOML from '@iarna/toml';
import * as reporter from './reporter';
import FormData from 'form-data';
const mountPoint = '/var/lib/buildkit';
const execAsync = promisify(exec);
@ -143,25 +144,38 @@ async function getDiskSize(device: string): Promise<number> {
}
}
async function getStickyDisk(options?: {signal?: AbortSignal}): Promise<{expose_id: string; device: string}> {
export async function getStickyDisk(options?: {signal?: AbortSignal}): Promise<{expose_id: string; device: string}> {
const client = await reporter.createBlacksmithAgentClient();
const formData = new FormData();
// TODO(adityamaru): Support a stickydisk-per-build flag that will namespace the stickydisks by Dockerfile.
// For now, we'll use the repo name as the stickydisk key.
const repoName = process.env.GITHUB_REPO_NAME || '';
if (repoName === '') {
// Prepare data for both FormData and query params
const stickyDiskKey = process.env.GITHUB_REPO_NAME || '';
if (stickyDiskKey === '') {
throw new Error('GITHUB_REPO_NAME is not set');
}
formData.append('stickyDiskKey', repoName);
formData.append('region', process.env.BLACKSMITH_REGION || 'eu-central');
formData.append('installationModelID', process.env.BLACKSMITH_INSTALLATION_MODEL_ID || '');
formData.append('vmID', process.env.VM_ID || '');
core.debug(`Getting sticky disk for ${repoName}`);
core.debug('FormData contents:');
for (const pair of formData.entries()) {
core.debug(`${pair[0]}: ${pair[1]}`);
}
const response = await reporter.get(client, '/stickydisks', formData, options);
const region = process.env.BLACKSMITH_REGION || 'eu-central';
const installationModelID = process.env.BLACKSMITH_INSTALLATION_MODEL_ID || '';
const vmID = process.env.VM_ID || '';
// Create FormData (for backwards compatibility).
// TODO(adityamaru): Remove this once all of our VM agents are reading query params.
const formData = new FormData();
formData.append('stickyDiskKey', stickyDiskKey);
formData.append('region', region);
formData.append('installationModelID', installationModelID);
formData.append('vmID', vmID);
// Create query params string.
const queryParams = new URLSearchParams({
stickyDiskKey,
region,
installationModelID,
vmID
}).toString();
core.debug(`Getting sticky disk for ${stickyDiskKey}`);
// Send request with both FormData and query params
const response = await reporter.get(client, `/stickydisks?${queryParams}`, formData, options);
const exposeId = response.data?.expose_id || '';
const device = response.data?.disk_identifier || '';
return {expose_id: exposeId, device: device};