@cloud-cli/dns 2.0.3 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -1
- package/dist/index.js +34 -15
- package/dist/index.spec.js +69 -50
- package/package.json +2 -2
- package/src/index.spec.ts +72 -50
- package/src/index.ts +34 -15
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { init } from
|
|
1
|
+
import { init, help } from "@cloud-cli/cli";
|
|
2
2
|
interface DomainAndTarget {
|
|
3
3
|
domain: string;
|
|
4
4
|
target?: string;
|
|
@@ -19,5 +19,6 @@ declare const _default: {
|
|
|
19
19
|
reload: typeof reload;
|
|
20
20
|
get: typeof get;
|
|
21
21
|
[init]: typeof addDnsConfig;
|
|
22
|
+
[help]: () => string;
|
|
22
23
|
};
|
|
23
24
|
export default _default;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import { join } from
|
|
3
|
-
import { exec } from
|
|
4
|
-
import { getConfig, init } from
|
|
5
|
-
const filePath = join(process.cwd(),
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { exec } from "@cloud-cli/exec";
|
|
4
|
+
import { getConfig, init, help } from "@cloud-cli/cli";
|
|
5
|
+
const filePath = join(process.cwd(), "configuration", "hosts.conf");
|
|
6
6
|
const dnsConfig = {
|
|
7
|
-
defaultTarget:
|
|
7
|
+
defaultTarget: "127.0.0.1",
|
|
8
8
|
};
|
|
9
9
|
function add(input) {
|
|
10
10
|
let current = list();
|
|
@@ -26,27 +26,46 @@ function list() {
|
|
|
26
26
|
if (!fs.existsSync(filePath)) {
|
|
27
27
|
return [];
|
|
28
28
|
}
|
|
29
|
-
const input = fs.readFileSync(filePath,
|
|
30
|
-
const entries = input.trim().split(
|
|
29
|
+
const input = fs.readFileSync(filePath, "utf8");
|
|
30
|
+
const entries = input.trim().split("\n").filter(Boolean).flatMap(parseDNSLine);
|
|
31
31
|
return entries;
|
|
32
32
|
}
|
|
33
33
|
function get(options) {
|
|
34
34
|
return list().filter((d) => d.domain === options.domain)[0] || null;
|
|
35
35
|
}
|
|
36
36
|
async function reload() {
|
|
37
|
-
const getPid = await exec(
|
|
38
|
-
const cmd = await exec(
|
|
39
|
-
return cmd.ok || Promise.reject(new Error(
|
|
37
|
+
const getPid = await exec("pidof", ["dnsmasq"]);
|
|
38
|
+
const cmd = await exec("kill", ["-s", "HUP", getPid.stdout.trim()]);
|
|
39
|
+
return cmd.ok || Promise.reject(new Error("Failed to reload"));
|
|
40
40
|
}
|
|
41
41
|
function addDnsConfig() {
|
|
42
|
-
Object.assign(dnsConfig, getConfig(
|
|
42
|
+
Object.assign(dnsConfig, getConfig("dns"));
|
|
43
43
|
}
|
|
44
44
|
function save(list) {
|
|
45
|
-
|
|
46
|
-
fs.writeFileSync(filePath, lines.join(
|
|
45
|
+
const lines = list.map((next) => next.target + " " + next.domain);
|
|
46
|
+
fs.writeFileSync(filePath, lines.join("\n").trim());
|
|
47
47
|
}
|
|
48
48
|
export function parseDNSLine(line) {
|
|
49
49
|
const [ip, ...domains] = line.split(/\s+/);
|
|
50
50
|
return domains.map((d) => ({ domain: d, target: ip }));
|
|
51
51
|
}
|
|
52
|
-
export default {
|
|
52
|
+
export default {
|
|
53
|
+
add,
|
|
54
|
+
remove,
|
|
55
|
+
list,
|
|
56
|
+
reload,
|
|
57
|
+
get,
|
|
58
|
+
[init]: addDnsConfig,
|
|
59
|
+
[help]: () => `Manage DNS host entries
|
|
60
|
+
|
|
61
|
+
Available commands:
|
|
62
|
+
dns.add --domain <domain> [--target <ip>] - Add a DNS entry (target defaults to 127.0.0.1)
|
|
63
|
+
dns.remove --domain <domain> - Remove a DNS entry
|
|
64
|
+
dns.list - List all DNS entries
|
|
65
|
+
dns.reload - Reload the DNS service
|
|
66
|
+
dns.get --domain <domain> - Get a DNS entry
|
|
67
|
+
|
|
68
|
+
Options:
|
|
69
|
+
domain - Domain name
|
|
70
|
+
target - IP address (defaults to 127.0.0.1)`,
|
|
71
|
+
};
|
package/dist/index.spec.js
CHANGED
|
@@ -1,90 +1,109 @@
|
|
|
1
|
-
import dns, { parseDNSLine } from
|
|
2
|
-
import fs from
|
|
3
|
-
import { init } from
|
|
4
|
-
import { describe, it, expect, vi, beforeEach } from
|
|
1
|
+
import dns, { parseDNSLine } from "./index";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { init, help } from "@cloud-cli/cli";
|
|
4
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
5
5
|
const execMocks = vi.hoisted(() => ({
|
|
6
6
|
exec: vi.fn(),
|
|
7
7
|
getConfig: vi.fn(),
|
|
8
8
|
}));
|
|
9
|
-
vi.mock(
|
|
10
|
-
vi.mock(
|
|
11
|
-
vi.mock(
|
|
9
|
+
vi.mock("get-port", () => ({ default: vi.fn().mockReturnValue(1234) }));
|
|
10
|
+
vi.mock("@cloud-cli/exec", () => ({ exec: execMocks.exec }));
|
|
11
|
+
vi.mock("@cloud-cli/cli", async (original) => {
|
|
12
12
|
const mod = await original();
|
|
13
13
|
return {
|
|
14
14
|
...mod,
|
|
15
15
|
getConfig: execMocks.getConfig,
|
|
16
|
+
help: mod.help,
|
|
16
17
|
};
|
|
17
18
|
});
|
|
18
19
|
beforeEach(() => {
|
|
19
|
-
vi.spyOn(fs,
|
|
20
|
-
vi.spyOn(fs,
|
|
20
|
+
vi.spyOn(fs, "writeFileSync").mockImplementation(() => { });
|
|
21
|
+
vi.spyOn(fs, "readFileSync").mockImplementation(() => "");
|
|
21
22
|
});
|
|
22
|
-
describe(
|
|
23
|
-
it(
|
|
24
|
-
|
|
23
|
+
describe("dns", () => {
|
|
24
|
+
it("should have a [help] Symbol export that is a function", () => {
|
|
25
|
+
expect(dns[help]).toBeDefined();
|
|
26
|
+
expect(typeof dns[help]).toBe("function");
|
|
27
|
+
});
|
|
28
|
+
it("should return a string help text", () => {
|
|
29
|
+
const helpText = dns[help]();
|
|
30
|
+
expect(typeof help).toBe("symbol");
|
|
31
|
+
expect(typeof helpText).toBe("string");
|
|
32
|
+
expect(helpText).toContain("DNS");
|
|
33
|
+
expect(helpText).toContain("dns.add --domain <domain>");
|
|
34
|
+
expect(helpText).toContain("dns.remove --domain <domain>");
|
|
35
|
+
expect(helpText).toContain("dns.list");
|
|
36
|
+
expect(helpText).toContain("dns.reload");
|
|
37
|
+
expect(helpText).toContain("dns.get --domain <domain>");
|
|
38
|
+
});
|
|
39
|
+
it('should not expose "help" as a normal command key', () => {
|
|
40
|
+
expect(Object.hasOwn(dns, "help")).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
it("should parse a DNS configuration line", () => {
|
|
43
|
+
const line = "1.2.3.4 foo bar";
|
|
25
44
|
const output = parseDNSLine(line);
|
|
26
45
|
expect(output).toEqual([
|
|
27
|
-
{ target:
|
|
28
|
-
{ target:
|
|
46
|
+
{ target: "1.2.3.4", domain: "foo" },
|
|
47
|
+
{ target: "1.2.3.4", domain: "bar" },
|
|
29
48
|
]);
|
|
30
49
|
});
|
|
31
|
-
it(
|
|
50
|
+
it("should load entries from file", () => {
|
|
32
51
|
let fileExists = false;
|
|
33
52
|
const buffer = `1.2.3.4 test\n5.6.7.8 foo`;
|
|
34
|
-
vi.spyOn(fs,
|
|
35
|
-
vi.spyOn(fs,
|
|
53
|
+
vi.spyOn(fs, "existsSync").mockImplementation(() => fileExists);
|
|
54
|
+
vi.spyOn(fs, "readFileSync").mockImplementation(() => buffer);
|
|
36
55
|
expect(dns.list()).toEqual([]);
|
|
37
56
|
fileExists = true;
|
|
38
57
|
const list = dns.list();
|
|
39
58
|
expect(list).toEqual([
|
|
40
|
-
{ domain:
|
|
41
|
-
{ domain:
|
|
59
|
+
{ domain: "test", target: "1.2.3.4" },
|
|
60
|
+
{ domain: "foo", target: "5.6.7.8" },
|
|
42
61
|
]);
|
|
43
62
|
});
|
|
44
|
-
it(
|
|
63
|
+
it("should get a DNS entry by domain", () => {
|
|
45
64
|
let fileExists = false;
|
|
46
65
|
const buffer = `1.2.3.4 test.com\n5.6.7.8 foo.com`;
|
|
47
|
-
vi.spyOn(fs,
|
|
48
|
-
vi.spyOn(fs,
|
|
49
|
-
expect(dns.get({ domain:
|
|
66
|
+
vi.spyOn(fs, "existsSync").mockImplementation(() => fileExists);
|
|
67
|
+
vi.spyOn(fs, "readFileSync").mockImplementation(() => buffer);
|
|
68
|
+
expect(dns.get({ domain: "foo.com" })).toBe(null);
|
|
50
69
|
fileExists = true;
|
|
51
|
-
expect(dns.get({ domain:
|
|
70
|
+
expect(dns.get({ domain: "foo.com" })).toEqual({ domain: "foo.com", target: "5.6.7.8" });
|
|
52
71
|
});
|
|
53
|
-
it(
|
|
54
|
-
let text =
|
|
55
|
-
vi.spyOn(fs,
|
|
72
|
+
it("should add and remove local DNS entries", () => {
|
|
73
|
+
let text = "";
|
|
74
|
+
vi.spyOn(fs, "writeFileSync").mockImplementation((_file, value) => {
|
|
56
75
|
text = value;
|
|
57
76
|
});
|
|
58
|
-
vi.spyOn(fs,
|
|
59
|
-
dns.add({ domain:
|
|
60
|
-
dns.add({ domain:
|
|
61
|
-
dns.add({ domain:
|
|
62
|
-
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String),
|
|
63
|
-
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String),
|
|
64
|
-
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String),
|
|
65
|
-
dns.remove({ domain:
|
|
66
|
-
expect(text).toBe(
|
|
77
|
+
vi.spyOn(fs, "readFileSync").mockImplementation(() => text);
|
|
78
|
+
dns.add({ domain: "foo", target: "2.3.4.5" });
|
|
79
|
+
dns.add({ domain: "bar", target: "2.3.4.5" });
|
|
80
|
+
dns.add({ domain: "baz" });
|
|
81
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String), "2.3.4.5 foo");
|
|
82
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String), "2.3.4.5 foo\n2.3.4.5 bar");
|
|
83
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String), "2.3.4.5 foo\n2.3.4.5 bar\n127.0.0.1 baz");
|
|
84
|
+
dns.remove({ domain: "baz" });
|
|
85
|
+
expect(text).toBe("2.3.4.5 foo\n2.3.4.5 bar");
|
|
67
86
|
});
|
|
68
|
-
describe(
|
|
69
|
-
it(
|
|
70
|
-
execMocks.exec.mockResolvedValueOnce({ ok: true, stdout:
|
|
87
|
+
describe("reload", () => {
|
|
88
|
+
it("should reload the DNS service", async () => {
|
|
89
|
+
execMocks.exec.mockResolvedValueOnce({ ok: true, stdout: "123" });
|
|
71
90
|
execMocks.exec.mockResolvedValueOnce({ ok: true });
|
|
72
91
|
await expect(dns.reload()).resolves.toBe(true);
|
|
73
|
-
expect(execMocks.exec).toHaveBeenCalledWith(
|
|
74
|
-
expect(execMocks.exec).toHaveBeenCalledWith(
|
|
92
|
+
expect(execMocks.exec).toHaveBeenCalledWith("pidof", ["dnsmasq"]);
|
|
93
|
+
expect(execMocks.exec).toHaveBeenCalledWith("kill", ["-s", "HUP", "123"]);
|
|
75
94
|
});
|
|
76
|
-
it(
|
|
77
|
-
execMocks.exec.mockResolvedValueOnce({ ok: true, stdout:
|
|
78
|
-
execMocks.exec.mockResolvedValueOnce({ ok: false, stderr:
|
|
79
|
-
await expect(dns.reload()).rejects.toEqual(new Error(
|
|
95
|
+
it("should show error on reload", async () => {
|
|
96
|
+
execMocks.exec.mockResolvedValueOnce({ ok: true, stdout: "123" });
|
|
97
|
+
execMocks.exec.mockResolvedValueOnce({ ok: false, stderr: "error" });
|
|
98
|
+
await expect(dns.reload()).rejects.toEqual(new Error("Failed to reload"));
|
|
80
99
|
});
|
|
81
100
|
});
|
|
82
|
-
describe(
|
|
83
|
-
it(
|
|
84
|
-
execMocks.getConfig.mockReturnValue({ defaultTarget:
|
|
101
|
+
describe("dns configuration", () => {
|
|
102
|
+
it("should configure the default target", () => {
|
|
103
|
+
execMocks.getConfig.mockReturnValue({ defaultTarget: "1.1.2.2" });
|
|
85
104
|
dns[init]();
|
|
86
|
-
dns.add({ domain:
|
|
87
|
-
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String),
|
|
105
|
+
dns.add({ domain: "bar" });
|
|
106
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String), "1.1.2.2 bar");
|
|
88
107
|
});
|
|
89
108
|
});
|
|
90
109
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloud-cli/dns",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -27,6 +27,6 @@
|
|
|
27
27
|
"@cloud-cli/exec": "^1.0.4"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@cloud-cli/cli": "^2.
|
|
30
|
+
"@cloud-cli/cli": "^2.2.0"
|
|
31
31
|
}
|
|
32
32
|
}
|
package/src/index.spec.ts
CHANGED
|
@@ -1,111 +1,133 @@
|
|
|
1
|
-
import dns, { parseDNSLine } from
|
|
2
|
-
import fs from
|
|
3
|
-
import { init } from
|
|
4
|
-
import { describe, it, expect, vi, beforeEach } from
|
|
1
|
+
import dns, { parseDNSLine } from "./index";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { init, help } from "@cloud-cli/cli";
|
|
4
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
5
5
|
|
|
6
6
|
const execMocks = vi.hoisted(() => ({
|
|
7
7
|
exec: vi.fn(),
|
|
8
8
|
getConfig: vi.fn(),
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
|
-
vi.mock(
|
|
12
|
-
vi.mock(
|
|
13
|
-
vi.mock(
|
|
11
|
+
vi.mock("get-port", () => ({ default: vi.fn().mockReturnValue(1234) }));
|
|
12
|
+
vi.mock("@cloud-cli/exec", () => ({ exec: execMocks.exec }));
|
|
13
|
+
vi.mock("@cloud-cli/cli", async (original) => {
|
|
14
14
|
const mod: any = await original();
|
|
15
15
|
return {
|
|
16
16
|
...mod,
|
|
17
17
|
getConfig: execMocks.getConfig,
|
|
18
|
+
help: mod.help,
|
|
18
19
|
};
|
|
19
20
|
});
|
|
20
21
|
|
|
21
22
|
beforeEach(() => {
|
|
22
|
-
vi.spyOn(fs,
|
|
23
|
-
vi.spyOn(fs,
|
|
23
|
+
vi.spyOn(fs, "writeFileSync").mockImplementation(() => {});
|
|
24
|
+
vi.spyOn(fs, "readFileSync").mockImplementation(() => "");
|
|
24
25
|
});
|
|
25
26
|
|
|
26
|
-
describe(
|
|
27
|
-
it(
|
|
28
|
-
|
|
27
|
+
describe("dns", () => {
|
|
28
|
+
it("should have a [help] Symbol export that is a function", () => {
|
|
29
|
+
expect(dns[help]).toBeDefined();
|
|
30
|
+
expect(typeof dns[help]).toBe("function");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should return a string help text", () => {
|
|
34
|
+
const helpText = dns[help]();
|
|
35
|
+
expect(typeof help).toBe("symbol");
|
|
36
|
+
expect(typeof helpText).toBe("string");
|
|
37
|
+
expect(helpText).toContain("DNS");
|
|
38
|
+
expect(helpText).toContain("dns.add --domain <domain>");
|
|
39
|
+
expect(helpText).toContain("dns.remove --domain <domain>");
|
|
40
|
+
expect(helpText).toContain("dns.list");
|
|
41
|
+
expect(helpText).toContain("dns.reload");
|
|
42
|
+
expect(helpText).toContain("dns.get --domain <domain>");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should not expose "help" as a normal command key', () => {
|
|
46
|
+
expect(Object.hasOwn(dns, "help")).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should parse a DNS configuration line", () => {
|
|
50
|
+
const line = "1.2.3.4 foo bar";
|
|
29
51
|
const output = parseDNSLine(line);
|
|
30
52
|
|
|
31
53
|
expect(output).toEqual([
|
|
32
|
-
{ target:
|
|
33
|
-
{ target:
|
|
54
|
+
{ target: "1.2.3.4", domain: "foo" },
|
|
55
|
+
{ target: "1.2.3.4", domain: "bar" },
|
|
34
56
|
]);
|
|
35
57
|
});
|
|
36
58
|
|
|
37
|
-
it(
|
|
59
|
+
it("should load entries from file", () => {
|
|
38
60
|
let fileExists = false;
|
|
39
61
|
const buffer = `1.2.3.4 test\n5.6.7.8 foo`;
|
|
40
|
-
vi.spyOn(fs,
|
|
41
|
-
vi.spyOn(fs,
|
|
62
|
+
vi.spyOn(fs, "existsSync").mockImplementation(() => fileExists);
|
|
63
|
+
vi.spyOn(fs, "readFileSync").mockImplementation(() => buffer);
|
|
42
64
|
|
|
43
65
|
expect(dns.list()).toEqual([]);
|
|
44
66
|
|
|
45
67
|
fileExists = true;
|
|
46
68
|
const list = dns.list();
|
|
47
69
|
expect(list).toEqual([
|
|
48
|
-
{ domain:
|
|
49
|
-
{ domain:
|
|
70
|
+
{ domain: "test", target: "1.2.3.4" },
|
|
71
|
+
{ domain: "foo", target: "5.6.7.8" },
|
|
50
72
|
]);
|
|
51
73
|
});
|
|
52
74
|
|
|
53
|
-
it(
|
|
75
|
+
it("should get a DNS entry by domain", () => {
|
|
54
76
|
let fileExists = false;
|
|
55
77
|
const buffer = `1.2.3.4 test.com\n5.6.7.8 foo.com`;
|
|
56
|
-
vi.spyOn(fs,
|
|
57
|
-
vi.spyOn(fs,
|
|
78
|
+
vi.spyOn(fs, "existsSync").mockImplementation(() => fileExists);
|
|
79
|
+
vi.spyOn(fs, "readFileSync").mockImplementation(() => buffer);
|
|
58
80
|
|
|
59
|
-
expect(dns.get({ domain:
|
|
81
|
+
expect(dns.get({ domain: "foo.com" })).toBe(null);
|
|
60
82
|
|
|
61
83
|
fileExists = true;
|
|
62
|
-
expect(dns.get({ domain:
|
|
84
|
+
expect(dns.get({ domain: "foo.com" })).toEqual({ domain: "foo.com", target: "5.6.7.8" });
|
|
63
85
|
});
|
|
64
86
|
|
|
65
|
-
it(
|
|
66
|
-
let text =
|
|
67
|
-
vi.spyOn(fs,
|
|
87
|
+
it("should add and remove local DNS entries", () => {
|
|
88
|
+
let text = "";
|
|
89
|
+
vi.spyOn(fs, "writeFileSync").mockImplementation((_file, value: string) => {
|
|
68
90
|
text = value;
|
|
69
91
|
});
|
|
70
|
-
vi.spyOn(fs,
|
|
92
|
+
vi.spyOn(fs, "readFileSync").mockImplementation(() => text);
|
|
71
93
|
|
|
72
|
-
dns.add({ domain:
|
|
73
|
-
dns.add({ domain:
|
|
74
|
-
dns.add({ domain:
|
|
94
|
+
dns.add({ domain: "foo", target: "2.3.4.5" });
|
|
95
|
+
dns.add({ domain: "bar", target: "2.3.4.5" });
|
|
96
|
+
dns.add({ domain: "baz" });
|
|
75
97
|
|
|
76
|
-
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String),
|
|
77
|
-
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String),
|
|
78
|
-
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String),
|
|
98
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String), "2.3.4.5 foo");
|
|
99
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String), "2.3.4.5 foo\n2.3.4.5 bar");
|
|
100
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String), "2.3.4.5 foo\n2.3.4.5 bar\n127.0.0.1 baz");
|
|
79
101
|
|
|
80
|
-
dns.remove({ domain:
|
|
81
|
-
expect(text).toBe(
|
|
102
|
+
dns.remove({ domain: "baz" });
|
|
103
|
+
expect(text).toBe("2.3.4.5 foo\n2.3.4.5 bar");
|
|
82
104
|
});
|
|
83
105
|
|
|
84
|
-
describe(
|
|
85
|
-
it(
|
|
86
|
-
execMocks.exec.mockResolvedValueOnce({ ok: true, stdout:
|
|
106
|
+
describe("reload", () => {
|
|
107
|
+
it("should reload the DNS service", async () => {
|
|
108
|
+
execMocks.exec.mockResolvedValueOnce({ ok: true, stdout: "123" } as any);
|
|
87
109
|
execMocks.exec.mockResolvedValueOnce({ ok: true } as any);
|
|
88
110
|
|
|
89
111
|
await expect(dns.reload()).resolves.toBe(true);
|
|
90
|
-
expect(execMocks.exec).toHaveBeenCalledWith(
|
|
91
|
-
expect(execMocks.exec).toHaveBeenCalledWith(
|
|
112
|
+
expect(execMocks.exec).toHaveBeenCalledWith("pidof", ["dnsmasq"]);
|
|
113
|
+
expect(execMocks.exec).toHaveBeenCalledWith("kill", ["-s", "HUP", "123"]);
|
|
92
114
|
});
|
|
93
115
|
|
|
94
|
-
it(
|
|
95
|
-
execMocks.exec.mockResolvedValueOnce({ ok: true, stdout:
|
|
96
|
-
execMocks.exec.mockResolvedValueOnce({ ok: false, stderr:
|
|
116
|
+
it("should show error on reload", async () => {
|
|
117
|
+
execMocks.exec.mockResolvedValueOnce({ ok: true, stdout: "123" } as any);
|
|
118
|
+
execMocks.exec.mockResolvedValueOnce({ ok: false, stderr: "error" } as any);
|
|
97
119
|
|
|
98
|
-
await expect(dns.reload()).rejects.toEqual(new Error(
|
|
120
|
+
await expect(dns.reload()).rejects.toEqual(new Error("Failed to reload"));
|
|
99
121
|
});
|
|
100
122
|
});
|
|
101
123
|
|
|
102
|
-
describe(
|
|
103
|
-
it(
|
|
104
|
-
execMocks.getConfig.mockReturnValue({ defaultTarget:
|
|
124
|
+
describe("dns configuration", () => {
|
|
125
|
+
it("should configure the default target", () => {
|
|
126
|
+
execMocks.getConfig.mockReturnValue({ defaultTarget: "1.1.2.2" });
|
|
105
127
|
dns[init]();
|
|
106
|
-
dns.add({ domain:
|
|
128
|
+
dns.add({ domain: "bar" });
|
|
107
129
|
|
|
108
|
-
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String),
|
|
130
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String), "1.1.2.2 bar");
|
|
109
131
|
});
|
|
110
132
|
});
|
|
111
133
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import { join } from
|
|
3
|
-
import { exec } from
|
|
4
|
-
import { getConfig, init } from
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { exec } from "@cloud-cli/exec";
|
|
4
|
+
import { getConfig, init, help } from "@cloud-cli/cli";
|
|
5
5
|
|
|
6
6
|
interface DNSConfig {
|
|
7
7
|
defaultTarget?: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
const filePath = join(process.cwd(),
|
|
10
|
+
const filePath = join(process.cwd(), "configuration", "hosts.conf");
|
|
11
11
|
const dnsConfig: DNSConfig = {
|
|
12
|
-
defaultTarget:
|
|
12
|
+
defaultTarget: "127.0.0.1",
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
interface DomainAndTarget {
|
|
@@ -43,8 +43,8 @@ function list(): DomainAndTarget[] {
|
|
|
43
43
|
return [];
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const input = fs.readFileSync(filePath,
|
|
47
|
-
const entries = input.trim().split(
|
|
46
|
+
const input = fs.readFileSync(filePath, "utf8");
|
|
47
|
+
const entries = input.trim().split("\n").filter(Boolean).flatMap(parseDNSLine);
|
|
48
48
|
|
|
49
49
|
return entries;
|
|
50
50
|
}
|
|
@@ -54,18 +54,18 @@ function get(options: { domain: string }): DomainAndTarget | null {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
async function reload() {
|
|
57
|
-
const getPid = await exec(
|
|
58
|
-
const cmd = await exec(
|
|
59
|
-
return cmd.ok || Promise.reject(new Error(
|
|
57
|
+
const getPid = await exec("pidof", ["dnsmasq"]);
|
|
58
|
+
const cmd = await exec("kill", ["-s", "HUP", getPid.stdout.trim()]);
|
|
59
|
+
return cmd.ok || Promise.reject(new Error("Failed to reload"));
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function addDnsConfig() {
|
|
63
|
-
Object.assign(dnsConfig, getConfig(
|
|
63
|
+
Object.assign(dnsConfig, getConfig("dns"));
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
function save(list: DomainAndTarget[]) {
|
|
67
|
-
|
|
68
|
-
fs.writeFileSync(filePath, lines.join(
|
|
67
|
+
const lines = list.map((next) => next.target + " " + next.domain);
|
|
68
|
+
fs.writeFileSync(filePath, lines.join("\n").trim());
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export function parseDNSLine(line: string): DomainAndTarget[] {
|
|
@@ -73,4 +73,23 @@ export function parseDNSLine(line: string): DomainAndTarget[] {
|
|
|
73
73
|
return domains.map((d) => ({ domain: d, target: ip }));
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
export default {
|
|
76
|
+
export default {
|
|
77
|
+
add,
|
|
78
|
+
remove,
|
|
79
|
+
list,
|
|
80
|
+
reload,
|
|
81
|
+
get,
|
|
82
|
+
[init]: addDnsConfig,
|
|
83
|
+
[help]: () => `Manage DNS host entries
|
|
84
|
+
|
|
85
|
+
Available commands:
|
|
86
|
+
dns.add --domain <domain> [--target <ip>] - Add a DNS entry (target defaults to 127.0.0.1)
|
|
87
|
+
dns.remove --domain <domain> - Remove a DNS entry
|
|
88
|
+
dns.list - List all DNS entries
|
|
89
|
+
dns.reload - Reload the DNS service
|
|
90
|
+
dns.get --domain <domain> - Get a DNS entry
|
|
91
|
+
|
|
92
|
+
Options:
|
|
93
|
+
domain - Domain name
|
|
94
|
+
target - IP address (defaults to 127.0.0.1)`,
|
|
95
|
+
};
|