Skip to content

Commit

Permalink
Add gravity update
Browse files Browse the repository at this point in the history
  • Loading branch information
mattwebbio committed Oct 1, 2022
1 parent 6e8a7ae commit 2066860
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 8 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ It is recommended you run this service with Docker.
| `SECONDARY_HOST_(#)_BASE_URL` | Yes | N/A | `http://192.168.1.3` or `https://pihole2.example.com` | The base URL of your secondary Pi-hole, including the scheme (HTTP or HTTPS) and port but not including a following slash. Replace `(#)` with a number, starting at `1`, to add multiple secondary Pi-holes. |
| `SECONDARY_HOST_(#)_PASSWORD` | Yes | N/A | `mypassword2` | The password used to log in to the admin interface. |
| `INTERVAL_MINUTES` | No | 30 | Any non-zero positive integer, for example `5`, `30`, or `1440` | How long to wait between synchronizations. Defaults to five minutes. Remember that the DNS server on your secondary servers restarts everytime a sync is performed. |
| `UPDATE_GRAVITY` | No | `true` | `true`/`false` | Triggers a gravity update after a backup has been uploaded to a secondary Pi-hole. This updates adlists and restarts gravity. |
| `SYNC_WHITELIST` | No | `true` | `true`/`false` | Copies the whitelist |
| `SYNC_REGEX_WHITELIST` | No | `true` | `true`/`false` | Copies the regex whitelist |
| `SYNC_BLACKLIST` | No | `true` | `true`/`false` | Copies the blacklist |
Expand Down
99 changes: 98 additions & 1 deletion src/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,104 @@ describe('Client', () => {
});
});

test('should upload backup successfully', async () => {
test('should throw error if gravity update fails', async () => {
teleporter
.post('/admin/scripts/pi-hole/php/teleporter.php')
.reply(
200,
'Processed adlist (14 entries)<br>\n' +
'Processed adlist group assignments (13 entries)<br>\n' +
'Processed blacklist (exact) (0 entries)<br>\n' +
'Processed blacklist (regex) (3 entries)<br>\n' +
'Processed client (8 entries)<br>\n' +
'Processed client group assignments (16 entries)<br>\n' +
'Processed local DNS records (41 entries)<br>\n' +
'Processed domain_audit (0 entries)<br>\n' +
'Processed black-/whitelist group assignments (10 entries)<br>\n' +
'Processed group (3 entries)<br>\n' +
'Processed whitelist (exact) (4 entries)<br>\n' +
'Processed whitelist (regex) (0 entries)<br>\n' +
'OK'
);
teleporter
.get('/admin/scripts/pi-hole/php/gravity.sh.php', undefined)
.reply(200, '\ndata: \n\ndata: [✓] TCP (IPv6)\ndata: \ndata: \n\ndata:');

const expectError = expect(client.uploadBackup(backup)).rejects;

await expectError.toBeInstanceOf(ErrorNotification);
await expectError.toMatchObject({
message: 'Failed updating gravity on "http://10.0.0.2".',
verbose: {
host: 'http://10.0.0.2',
status: 200,
eventStream: '[✓] TCP (IPv6)'
}
});
});

test('should upload backup and update gravity successfully', async () => {
const syncOptions = jest.spyOn(Config, 'syncOptions', 'get');

let requestBody = '';
teleporter
.post('/admin/scripts/pi-hole/php/teleporter.php', (body) => (requestBody = body))
.reply(
200,
'Processed adlist (14 entries)<br>\n' +
'Processed adlist group assignments (13 entries)<br>\n' +
'Processed blacklist (exact) (0 entries)<br>\n' +
'Processed blacklist (regex) (3 entries)<br>\n' +
'Processed client (8 entries)<br>\n' +
'Processed client group assignments (16 entries)<br>\n' +
'Processed local DNS records (41 entries)<br>\n' +
'Processed domain_audit (0 entries)<br>\n' +
'Processed black-/whitelist group assignments (10 entries)<br>\n' +
'Processed group (3 entries)<br>\n' +
'Processed whitelist (exact) (4 entries)<br>\n' +
'Processed whitelist (regex) (0 entries)<br>\n' +
'OK'
);
teleporter
.get('/admin/scripts/pi-hole/php/gravity.sh.php', undefined)
.reply(
200,
'\ndata: \n\ndata: [✓] TCP (IPv6)\ndata: \ndata: \n\ndata: [✓] Pi-hole blocking is enabled\ndata: \n\ndata:'
);

const result = await client.uploadBackup(backup);

expect(result).toStrictEqual(true);
expect(syncOptions).toHaveBeenCalled();
expect(requestBody).toContain(
'name="token"\r\n\r\nabcdefgijklmnopqrstuvwxyzabcdefgijklmnopqrst'
);
expect(requestBody).toContain('name="whitelist"\r\n\r\ntrue');
expect(requestBody).toContain('name="regex_whitelist"\r\n\r\ntrue');
expect(requestBody).toContain('name="blacklist"\r\n\r\ntrue');
expect(requestBody).toContain('name="regexlist"\r\n\r\ntrue');
expect(requestBody).toContain('name="adlist"\r\n\r\ntrue');
expect(requestBody).toContain('name="client"\r\n\r\ntrue');
expect(requestBody).toContain('name="group"\r\n\r\ntrue');
expect(requestBody).toContain('name="auditlog"\r\n\r\nfalse');
expect(requestBody).toContain('name="staticdhcpleases"\r\n\r\nfalse');
expect(requestBody).toContain('name="localdnsrecords"\r\n\r\ntrue');
expect(requestBody).toContain('name="localcnamerecords"\r\n\r\ntrue');
expect(requestBody).toContain('name="flushtables"\r\n\r\ntrue');
expect(requestBody).toContain('name="action"\r\n\r\nin');
expect(requestBody).toContain(
'name="zip_file"; filename="backup.tar.gz"\r\nContent-Type: application/octet-stream'
);
expect(requestBody.match(/Content-Disposition: form-data; name=/g)).toHaveLength(
15
);
});

test('should not update gravity if `updateGravity` is disabled', async () => {
const syncOptions = jest.spyOn(Config, 'syncOptions', 'get');
const updateGravity = jest
.spyOn(Config, 'updateGravity', 'get')
.mockReturnValue(false);

let requestBody = '';
teleporter
Expand Down Expand Up @@ -280,6 +376,7 @@ describe('Client', () => {
expect(requestBody.match(/Content-Disposition: form-data; name=/g)).toHaveLength(
15
);
updateGravity.mockRestore();
});
});
});
41 changes: 34 additions & 7 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,26 +110,53 @@ export class Client {
form.append('action', 'in');
form.append('zip_file', backup, 'backup.tar.gz');

const response = await this.fetch(
const uploadResponse = await this.fetch(
`${this.host.baseUrl}/admin/scripts/pi-hole/php/teleporter.php`,
{
body: form,
method: 'POST'
}
);
const text = await response.text();
if (response.status !== 200 || !text.endsWith('OK'))
const uploadText = await uploadResponse.text();
if (uploadResponse.status !== 200 || !uploadText.endsWith('OK'))
throw new ErrorNotification({
message: `Error: failed to upload backup to "${this.host.baseUrl}".`,
message: `Failed to upload backup to "${this.host.baseUrl}".`,
verbose: {
host: this.host.baseUrl,
status: response.status,
responseBody: text
status: uploadResponse.status,
responseBody: uploadText
}
});

Log.info(chalk.green(`✔️ Backup uploaded to ${this.host.baseUrl}!`));
Log.verbose(`Result:\n${chalk.blue(text)}`);
Log.verbose(`Result:\n${chalk.blue(uploadText)}`);

if (Config.updateGravity) {
Log.info(chalk.yellow(`➡️ Updating gravity on ${this.host.baseUrl}...`));
const gravityUpdateResponse = await this.fetch(
`${this.host.baseUrl}/admin/scripts/pi-hole/php/gravity.sh.php`,
{ method: 'GET' }
);

const updateText = (await gravityUpdateResponse.text())
.replaceAll('\ndata:', '')
.trim();
if (
gravityUpdateResponse.status !== 200 ||
!updateText.endsWith('Pi-hole blocking is enabled')
)
throw new ErrorNotification({
message: `Failed updating gravity on "${this.host.baseUrl}".`,
verbose: {
host: this.host.baseUrl,
status: gravityUpdateResponse.status,
eventStream: updateText
}
});

Log.info(chalk.green(`✔️ Gravity updated on ${this.host.baseUrl}!`));
Log.verbose(`Result:\n${chalk.blue(updateText)}`);
}

return true;
}
Expand Down
4 changes: 4 additions & 0 deletions src/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ describe('Config', () => {
});
});

describe('updateGravity', () => {
testToHaveDefaultAndOverride('updateGravity', true, 'UPDATE_GRAVITY');
});

describe('verboseMode', () => {
testToHaveDefaultAndOverride('verboseMode', false, 'VERBOSE');
});
Expand Down
4 changes: 4 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export class Config {
return this._syncOptions;
}

static get updateGravity(): boolean {
return process.env['UPDATE_GRAVITY'] !== 'false';
}

static get verboseMode(): boolean {
return process.env['VERBOSE'] === 'true';
}
Expand Down

0 comments on commit 2066860

Please sign in to comment.