Skip to content

Commit

Permalink
docker/install: Support rootless
Browse files Browse the repository at this point in the history
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
  • Loading branch information
vvoland committed Nov 5, 2024
1 parent 61c10b2 commit 324f9c9
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 20 deletions.
62 changes: 49 additions & 13 deletions __tests__/docker/install.test.itg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {Exec} from '../../src/exec';

const tmpDir = fs.mkdtempSync(path.join(process.env.TEMP || os.tmpdir(), 'docker-install-itg-'));

/*
describe('install', () => {
const originalEnv = process.env;
beforeEach(() => {
Expand Down Expand Up @@ -65,18 +66,53 @@ aarch64:https://cloud.debian.org/images/cloud/bookworm/20231013-1532/debian-12-g
contextName: 'foo',
daemonConfig: `{"debug":true,"features":{"containerd-snapshotter":true}}`
});
await expect((async () => {
try {
await install.download();
await install.install();
await Docker.printVersion();
await Docker.printInfo();
} catch (error) {
console.error(error);
throw error;
} finally {
await install.tearDown();
}
})()).resolves.not.toThrow();
await expect(tryInstall(install)).resolves.not.toThrow();
}, 30 * 60 * 1000);
});
*/

describe('rootless', () => {
test(
'install',
async () => {
// Skip on non linux
if (os.platform() !== 'linux') {
return;
}
if (process.env.ImageOS && process.env.ImageOS.startsWith('ubuntu')) {
// Remove containerd first on ubuntu runners to make sure it takes
// ones packaged with docker
await Exec.exec('sudo', ['apt-get', 'remove', '-y', 'containerd.io'], {
env: Object.assign({}, process.env, {
DEBIAN_FRONTEND: 'noninteractive'
}) as {
[key: string]: string;
}
});
}
const install = new Install({
source: {type: 'image', tag: 'latest'},
runDir: tmpDir,
contextName: 'foo',
daemonConfig: `{"debug":true}`,
rootless: true
});
await expect(tryInstall(install)).resolves.not.toThrow();
},
30 * 60 * 1000
);
});

async function tryInstall(install: Install) {
try {
await install.download();
await install.install();
await Docker.printVersion();
await Docker.printInfo();
} catch (error) {
console.error(error);
throw error;
} finally {
await install.tearDown();
}
}
44 changes: 37 additions & 7 deletions src/docker/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {limaYamlData, dockerServiceLogsPs1, setupDockerWinPs1} from './assets';
import {GitHubRelease} from '../types/github';
import {HubRepository} from '../hubRepository';
import {Image} from '../types/oci/config';
import {exec} from "child_process";

export interface InstallSourceImage {
type: 'image';
Expand All @@ -56,6 +57,7 @@ export interface InstallOpts {
runDir: string;
contextName?: string;
daemonConfig?: string;
rootless?: boolean;
}

interface LimaImage {
Expand All @@ -65,19 +67,21 @@ interface LimaImage {
}

export class Install {
private readonly runDir: string;
private runDir: string;
private readonly source: InstallSource;
private readonly contextName: string;
private readonly daemonConfig?: string;
private _version: string | undefined;
private _toolDir: string | undefined;
private rootless: boolean;

private gitCommit: string | undefined;

private readonly limaInstanceName = 'docker-actions-toolkit';

constructor(opts: InstallOpts) {
this.runDir = opts.runDir;
this.rootless = opts.rootless || false;
this.source = opts.source || {
type: 'archive',
version: 'latest',
Expand Down Expand Up @@ -195,7 +199,13 @@ export class Install {
if (!this.runDir) {
throw new Error('runDir must be set');
}
switch (os.platform()) {

const platform = os.platform();
if (this.rootless && platform != 'linux') {
// TODO: Support on macOS (via lima)
throw new Error(`rootless is only supported on linux`);
}
switch (platform) {
case 'darwin': {
return await this.installDarwin();
}
Expand Down Expand Up @@ -312,6 +322,9 @@ export class Install {
}

private async installLinux(): Promise<string> {
if (this.rootless) {
this.runDir = os.homedir() + '/' + this.runDir;
}
const dockerHost = `unix://${path.join(this.runDir, 'docker.sock')}`;
await io.mkdirP(this.runDir);

Expand Down Expand Up @@ -339,15 +352,27 @@ export class Install {
}

const envs = Object.assign({}, process.env, {
PATH: `${this.toolDir}:${process.env.PATH}`
PATH: `${this.toolDir}:${process.env.PATH}`,
XDG_RUNTIME_DIR: this.runDir
}) as {
[key: string]: string;
};

await core.group('Start Docker daemon', async () => {
const bashPath: string = await io.which('bash', true);
const cmd = `${this.toolDir}/dockerd --host="${dockerHost}" --config-file="${daemonConfigPath}" --exec-root="${this.runDir}/execroot" --data-root="${this.runDir}/data" --pidfile="${this.runDir}/docker.pid" --userland-proxy=false`;
let dockerPath = `${this.toolDir}/dockerd`;
if (this.rootless) {
dockerPath = `${this.toolDir}/dockerd-rootless.sh`;
if (fs.existsSync('/proc/sys/kernel/apparmor_restrict_unprivileged_userns')) {
await Exec.exec('sudo', ['sh', '-c', 'echo 0 > /proc/sys/kernel/apparmor_restrict_unprivileged_userns']);
}
}

let cmd = `${dockerPath} --host="${dockerHost}" --config-file="${daemonConfigPath}" --exec-root="${this.runDir}/execroot" --data-root="${this.runDir}/data" --pidfile="${this.runDir}/docker.pid"`;
core.info(`[command] ${cmd}`); // https://github.com/actions/toolkit/blob/3d652d3133965f63309e4b2e1c8852cdbdcb3833/packages/exec/src/toolrunner.ts#L47
if (this.rootless) {
cmd = `sudo -u \\#1001 ` + cmd;
}
const proc = await child_process.spawn(
// We can't use Exec.exec here because we need to detach the process to
// avoid killing it when the action finishes running. Even if detached,
Expand All @@ -359,19 +384,23 @@ EOF`,
[],
{
env: envs,
detached: true,
//detached: true,
shell: true,
stdio: ['ignore', process.stdout, process.stderr]
}
);
core.info(`unref`);
proc.unref();
await Util.sleep(3);

core.info(`sleep`);
await Util.sleep(5);
const retries = 10;
core.info(`Waiting for Docker daemon to start (up to ${retries} retries)...`);
await retry(
async bail => {
try {
await Exec.getExecOutput(`docker version`, undefined, {
silent: true,
silent: false,
env: Object.assign({}, envs, {
DOCKER_HOST: dockerHost,
DOCKER_CONTENT_TRUST: 'false'
Expand All @@ -380,6 +409,7 @@ EOF`,
}
});
} catch (e) {
core.error(e);
bail(e);
}
},
Expand Down

0 comments on commit 324f9c9

Please sign in to comment.