dot upstreams: per-upstream tls_name for sni and cert verification by dns name
This commit is contained in:
@@ -18,10 +18,19 @@ const log = std.log.scoped(.migrations);
|
||||
|
||||
pub const Step = struct { version: u32, sql: [:0]const u8 };
|
||||
|
||||
/// Append only. Editing a released step — or `config_schema.ddl_v1` — would make
|
||||
/// a fresh database and an upgraded one disagree, and nothing would detect it.
|
||||
pub const steps = [_]Step{
|
||||
.{ .version = 1, .sql = config_schema.ddl_v1 },
|
||||
.{ .version = 2, .sql = ddl_v2 },
|
||||
};
|
||||
|
||||
/// The DoT verification name (`upstreams.tls_name`). Empty keeps the pre-step-2
|
||||
/// behavior: verify the certificate against the url host.
|
||||
const ddl_v2: [:0]const u8 =
|
||||
\\ALTER TABLE upstreams ADD COLUMN tls_name TEXT NOT NULL DEFAULT '';
|
||||
;
|
||||
|
||||
pub const target_version: u32 = steps[steps.len - 1].version;
|
||||
|
||||
comptime {
|
||||
@@ -236,6 +245,63 @@ test "a stepwise upgrade applies only the new steps" {
|
||||
try testing.expectEqual(@as(i64, 1), try database.queryInt("SELECT count(*) FROM groups"));
|
||||
}
|
||||
|
||||
fn columnExists(database: *db.Db, table: []const u8, column: []const u8) !bool {
|
||||
var stmt = try database.prepare("SELECT count(*) FROM pragma_table_info(?1) WHERE name = ?2");
|
||||
defer stmt.deinit();
|
||||
try stmt.bindText(1, table);
|
||||
try stmt.bindText(2, column);
|
||||
if (!try stmt.step()) return error.SqliteError;
|
||||
return stmt.columnInt(0) != 0;
|
||||
}
|
||||
|
||||
test "a fresh database reaches version 2 with the tls_name column" {
|
||||
var database = try openMigrated();
|
||||
defer database.close();
|
||||
|
||||
try testing.expectEqual(@as(u32, 2), try migrate(&database));
|
||||
try testing.expectEqual(@as(u32, 2), target_version);
|
||||
try testing.expect(try columnExists(&database, "upstreams", "tls_name"));
|
||||
}
|
||||
|
||||
test "a version 1 database upgrades to 2 and keeps its rows with an empty tls_name" {
|
||||
var database = try openMigrated();
|
||||
defer database.close();
|
||||
|
||||
const first = [_]Step{.{ .version = 1, .sql = config_schema.ddl_v1 }};
|
||||
try testing.expectEqual(@as(u32, 1), try migrateSteps(&database, &first));
|
||||
try testing.expect(!try columnExists(&database, "upstreams", "tls_name"));
|
||||
try database.exec("INSERT INTO upstreams (url, priority, enabled) VALUES ('tls://1.1.1.1:853', 10, 1);");
|
||||
|
||||
try testing.expectEqual(@as(u32, 2), try migrate(&database));
|
||||
try testing.expectEqual(@as(u32, 2), try readVersion(&database));
|
||||
try testing.expect(try columnExists(&database, "upstreams", "tls_name"));
|
||||
|
||||
var stmt = try database.prepare("SELECT url, tls_name FROM upstreams");
|
||||
defer stmt.deinit();
|
||||
try testing.expect(try stmt.step());
|
||||
try testing.expectEqualStrings("tls://1.1.1.1:853", stmt.columnText(0));
|
||||
try testing.expectEqualStrings("", stmt.columnText(1));
|
||||
}
|
||||
|
||||
test "a failing step after step 2 rolls back the whole upgrade from version 1" {
|
||||
var database = try openMigrated();
|
||||
defer database.close();
|
||||
|
||||
const first = [_]Step{.{ .version = 1, .sql = config_schema.ddl_v1 }};
|
||||
_ = try migrateSteps(&database, &first);
|
||||
|
||||
const broken = [_]Step{
|
||||
steps[0],
|
||||
steps[1],
|
||||
.{ .version = 3, .sql = "CREATE TABLE third (" },
|
||||
};
|
||||
try testing.expectError(error.Unexpected, migrateSteps(&database, &broken));
|
||||
|
||||
// One transaction: the ALTER TABLE of step 2 went back with step 3.
|
||||
try testing.expect(!try columnExists(&database, "upstreams", "tls_name"));
|
||||
try testing.expectEqual(@as(u32, 1), try readVersion(&database));
|
||||
}
|
||||
|
||||
test "delete_order and content_tables name exactly the tables the schema creates" {
|
||||
var database = try openMigrated();
|
||||
defer database.close();
|
||||
|
||||
Reference in New Issue
Block a user