Skip to content

Commit

Permalink
feat: extend Podman Desktop API Build Image parameters (podman-deskto…
Browse files Browse the repository at this point in the history
…p#5882)

* feat: extend Podman Desktop API Build Image parameters

Fixes podman-desktop#5851

Signed-off-by: Jeff MAURY <[email protected]>
  • Loading branch information
jeffmaury authored Feb 8, 2024
1 parent 0f5d4ce commit d070c53
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 47 deletions.
76 changes: 76 additions & 0 deletions packages/extension-api/src/extension-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2286,6 +2286,82 @@ declare module '@podman-desktop/api' {
provider?: ProviderContainerConnectionInfo | containerDesktopAPI.ContainerProviderConnection;
// The abort controller for running the build image operation
abortController?: AbortController;
// Extra hosts to add to /etc/hosts
extrahosts?: string;
/*
* A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are
* placed into a file called Dockerfile and the image is built from that file. If the URI points to a tarball, the
* file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points
* to a tarball and the dockerfile parameter is also specified, there must be a file with the corresponding path
* inside the tarball.
*/
remote?: string;
/*
* Default: false
* Suppress verbose build output.
*/
q?: boolean;
// JSON array of images used for build cache resolution.
cachefrom?: string;
// Attempt to pull the image even if an older image exists locally.
pull?: string;
/*
* Default: true
* Remove intermediate containers after a successful build.
*/
rm?: boolean;
/*
* Default: false
* Always remove intermediate containers, even upon failure.
*/
forcerm?: boolean;
// Set memory limit for build.
memory?: number;
// Total memory (memory + swap). Set as -1 to disable swap.
memswap?: number;
// CPU shares (relative weight).
cpushares?: number;
// CPUs in which to allow execution (e.g., 0-3, 0,1).
cpusetcpus?: number;
// The length of a CPU period in microseconds.
cpuperiod?: number;
// Microseconds of CPU time that the container can get in a CPU period.
cpuquota?: number;
/*
* JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the
* buildargs as the environment context for commands run via the ```Dockerfile``` RUN instruction, or for variable
* expansion in other ```Dockerfilev instructions. This is not meant for passing secret values.
* For example, the build arg ```FOO=bar``` would become ```{"FOO":"bar"}``` in JSON. This would result in the query
* parameter ```buildargs={"FOO":"bar"}```. Note that ```{"FOO":"bar"}``` should be URI component encoded.
*/
buildargs?: { [key: string]: string };
//Size of ```/dev/shm``` in bytes. The size must be greater than 0. If omitted the system uses 64MB.
shmsize?: number;
// Squash the resulting images layers into a single layer.
squash?: boolean;
// Arbitrary key/value labels to set on the image, as a JSON map of string pairs.
labels?: { [key: string]: string };
/*
* Sets the networking mode for the run commands during build. Supported standard values are: ```bridge```,
* ```host```, ```none```, and ```container:<name|id>```. Any other value is taken as a custom network's name or ID
* to which this container should connect to.
*/
networkmode?: string;
/*
* Default: ""
* Target build stage
*/
target?: string;
/*
* Default: ""
* BuildKit output configuration
*/
outputs?: string;
/*
* Default: false
* Do not use the cache when building the image.
*/
nocache?: boolean;
}

export interface NetworkCreateOptions {
Expand Down
161 changes: 145 additions & 16 deletions packages/main/src/plugin/container-registry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1133,9 +1133,14 @@ describe('buildImage', () => {
lifecycleMethods: undefined,
status: 'started',
};
await expect(containerRegistry.buildImage('context', () => {}, 'file', 'name', '', connection)).rejects.toThrow(
'no running provider for the matching container',
);
await expect(
containerRegistry.buildImage('context', () => {}, {
containerFile: 'file',
tag: 'name',
platform: '',
provider: connection,
}),
).rejects.toThrow('no running provider for the matching container');
});

test('called getFirstRunningConnection when undefined provider', async () => {
Expand Down Expand Up @@ -1172,9 +1177,14 @@ describe('buildImage', () => {
},
status: () => 'started',
};
await expect(containerRegistry.buildImage('context', () => {}, 'file', 'name', '', connection)).rejects.toThrow(
'no running provider for the matching container',
);
await expect(
containerRegistry.buildImage('context', () => {}, {
containerFile: 'file',
tag: 'name',
platform: '',
provider: connection,
}),
).rejects.toThrow('no running provider for the matching container');
});

test('throw if build command fail', async () => {
Expand Down Expand Up @@ -1209,9 +1219,14 @@ describe('buildImage', () => {
vi.spyOn(tar, 'pack').mockReturnValue({} as NodeJS.ReadableStream);
vi.spyOn(dockerAPI, 'buildImage').mockRejectedValue('human error message');

await expect(containerRegistry.buildImage('context', () => {}, 'file', 'name', '', connection)).rejects.toThrow(
'human error message',
);
await expect(
containerRegistry.buildImage('context', () => {}, {
containerFile: 'file',
tag: 'name',
platform: '',
provider: connection,
}),
).rejects.toThrow('human error message');
});

test('throw if build command fail using a ContainerProviderConnection input', async () => {
Expand Down Expand Up @@ -1245,9 +1260,14 @@ describe('buildImage', () => {
vi.spyOn(tar, 'pack').mockReturnValue({} as NodeJS.ReadableStream);
vi.spyOn(dockerAPI, 'buildImage').mockRejectedValue('human error message');

await expect(containerRegistry.buildImage('context', () => {}, 'file', 'name', '', connection)).rejects.toThrow(
'human error message',
);
await expect(
containerRegistry.buildImage('context', () => {}, {
containerFile: 'file',
tag: 'name',
platform: '',
provider: connection,
}),
).rejects.toThrow('human error message');
});

test('verify relativeFilePath gets sanitized on Windows', async () => {
Expand Down Expand Up @@ -1285,7 +1305,12 @@ describe('buildImage', () => {
return f(null, []);
});

await containerRegistry.buildImage('context', () => {}, '\\path\\file', 'name', '', connection);
await containerRegistry.buildImage('context', () => {}, {
containerFile: '\\path\\file',
tag: 'name',
platform: '',
provider: connection,
});

expect(dockerAPI.buildImage).toBeCalledWith({} as NodeJS.ReadableStream, {
registryconfig: {},
Expand Down Expand Up @@ -1329,7 +1354,12 @@ describe('buildImage', () => {
return f(null, []);
});

await containerRegistry.buildImage('context', () => {}, '\\path\\file', 'name', '', connection);
await containerRegistry.buildImage('context', () => {}, {
containerFile: '\\path\\file',
tag: 'name',
platform: '',
provider: connection,
});

expect(dockerAPI.buildImage).toBeCalledWith({} as NodeJS.ReadableStream, {
registryconfig: {},
Expand All @@ -1339,7 +1369,7 @@ describe('buildImage', () => {
});
});

test('verify buildImage receives correct args on non-Windows OS', async () => {
async function verifyBuildImage(extraArgs: object): Promise<void> {
const dockerAPI = new Dockerode({ protocol: 'http', host: 'localhost' });

// set providers with docker being first
Expand Down Expand Up @@ -1374,14 +1404,113 @@ describe('buildImage', () => {
return f(null, []);
});

await containerRegistry.buildImage('context', () => {}, '/dir/dockerfile', 'name', '', connection);
await containerRegistry.buildImage('context', () => {}, {
containerFile: '/dir/dockerfile',
tag: 'name',
platform: '',
provider: connection,
...extraArgs,
});

expect(dockerAPI.buildImage).toBeCalledWith({} as NodeJS.ReadableStream, {
registryconfig: {},
platform: '',
dockerfile: '/dir/dockerfile',
t: 'name',
...extraArgs,
});
}

test('verify buildImage receives correct args on non-Windows OS', async () => {
await verifyBuildImage({});
});

test('verify buildImage receives correct args on non-Windows OS with extrahosts', async () => {
await verifyBuildImage({ extrahosts: 'a string' });
});

test('verify buildImage receives correct args on non-Windows OS with remote', async () => {
await verifyBuildImage({ remote: 'a string' });
});

test('verify buildImage receives correct args on non-Windows OS with q', async () => {
await verifyBuildImage({ q: true });
});

test('verify buildImage receives correct args on non-Windows OS with cachefrom', async () => {
await verifyBuildImage({ cachefrom: 'quay.io/ubi9/ubi' });
});

test('verify buildImage receives correct args on non-Windows OS with cachefrom', async () => {
await verifyBuildImage({ cachefrom: 'quay.io/ubi9/ubi' });
});

test('verify buildImage receives correct args on non-Windows OS with pull', async () => {
await verifyBuildImage({ pull: 'quay.io/ubi9/ubi' });
});

test('verify buildImage receives correct args on non-Windows OS with rm', async () => {
await verifyBuildImage({ rm: true });
});

test('verify buildImage receives correct args on non-Windows OS with forcerm', async () => {
await verifyBuildImage({ forcerm: true });
});

test('verify buildImage receives correct args on non-Windows OS with memory', async () => {
await verifyBuildImage({ memory: 12 });
});

test('verify buildImage receives correct args on non-Windows OS with memswap', async () => {
await verifyBuildImage({ memswap: 13 });
});

test('verify buildImage receives correct args on non-Windows OS with cpushares', async () => {
await verifyBuildImage({ cpushares: 14 });
});

test('verify buildImage receives correct args on non-Windows OS with cpusetcpus', async () => {
await verifyBuildImage({ cpusetcpus: 15 });
});

test('verify buildImage receives correct args on non-Windows OS with cpuperiod', async () => {
await verifyBuildImage({ cpuperiod: 16 });
});

test('verify buildImage receives correct args on non-Windows OS with cpuquota', async () => {
await verifyBuildImage({ cpuquota: 17 });
});

test('verify buildImage receives correct args on non-Windows OS with buildargs', async () => {
await verifyBuildImage({ buildargs: { KEY1: 'VALUE1' } });
});

test('verify buildImage receives correct args on non-Windows OS with shmsize', async () => {
await verifyBuildImage({ shmsize: 18 });
});

test('verify buildImage receives correct args on non-Windows OS with squash', async () => {
await verifyBuildImage({ squash: false });
});

test('verify buildImage receives correct args on non-Windows OS with labels', async () => {
await verifyBuildImage({ labels: { LABEL1: 'VALUE_LABEL1' } });
});

test('verify buildImage receives correct args on non-Windows OS with networkmode', async () => {
await verifyBuildImage({ networkmode: 'bridge' });
});

test('verify buildImage receives correct args on non-Windows OS with target', async () => {
await verifyBuildImage({ target: 'target' });
});

test('verify buildImage receives correct args on non-Windows OS with outputs', async () => {
await verifyBuildImage({ outputs: 'outputs' });
});

test('verify buildImage receives correct args on non-Windows OS with nocache', async () => {
await verifyBuildImage({ nocache: true });
});
});

Expand Down
Loading

0 comments on commit d070c53

Please sign in to comment.