feat: extend main with runRemoteUser and runRemoteTtl
This commit is contained in:
1 parent
422f7c1639
commit
ef1d0f4c06
1 file changed
+173
+173
@@ -3079,6 +3079,179 @@ fn runRemoteTxn(allocator: std.mem.Allocator, stdout: anytype, stderr: anytype,
|
|||||||
std.process.exit(1);
|
std.process.exit(1);
|
||||||
};
|
};
|
||||||
defer client.close();
|
defer client.close();
|
||||||
|
|
||||||
|
try authenticateIfRequested(&client, stderr, authFlags.auth);
|
||||||
|
|
||||||
|
const applied = client.txnOp(entries.items) catch |err| {
|
||||||
|
try stderr.print("transaction failed, nothing applied: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
try stdout.print("OK: {d} operation(s) committed atomically\n", .{applied});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The network counterpart of `runUser`: dispatches `remote-user <op> ...`
|
||||||
|
/// onto `client.zig`'s `admin*` methods (which wire-encode the
|
||||||
|
/// `user_admin` opcode) instead of a local `auth.Store`. Bootstrapping the
|
||||||
|
/// very first admin account still has to happen locally via `user add`
|
||||||
|
/// (see the module doc on `runUser`), but every subsequent user management
|
||||||
|
/// operation can go over the wire once that first admin can authenticate.
|
||||||
|
fn runRemoteUser(allocator: std.mem.Allocator, stdout: anytype, stderr: anytype, addressSpec: []const u8, rest: []const []const u8) !void {
|
||||||
|
if (rest.len < 1) return fail(stderr, "remote-user needs a subcommand (add/passwd/role/del/list)");
|
||||||
|
const address = try parseHostPort(stdout, addressSpec);
|
||||||
|
|
||||||
|
var client = cobalt.Client.connect(allocator, address) catch |err| {
|
||||||
|
try stderr.print("error: could not connect to {s}: {s}\n", .{ addressSpec, @errorName(err) });
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
defer client.close();
|
||||||
|
|
||||||
|
const authFlags = extractAuthFlags(allocator, rest[1..]) catch return fail(stderr, "invalid --user/--pass/--token value");
|
||||||
|
defer allocator.free(authFlags.rest);
|
||||||
|
|
||||||
|
try authenticateIfRequested(&client, stderr, authFlags.auth);
|
||||||
|
|
||||||
|
const op = rest[0];
|
||||||
|
const args = authFlags.rest;
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, op, "add")) {
|
||||||
|
if (args.len < 3) return fail(stderr, "remote-user add needs <username> <password> readonly|readwrite|admin");
|
||||||
|
const role = cobalt.AuthRole.parse(args[2]) catch return fail(stderr, "role must be readonly, readwrite, or admin");
|
||||||
|
client.adminCreateUser(args[0], args[1], role, cobalt.auth.recommendedRounds) catch |err| {
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
try stdout.print("user '{s}' created with role {s}\n", .{ args[0], role.name() });
|
||||||
|
} else if (std.mem.eql(u8, op, "passwd")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "remote-user passwd needs <username> <newpassword>");
|
||||||
|
client.adminSetPassword(args[0], args[1], cobalt.auth.recommendedRounds) catch |err| {
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
try stdout.print("password updated for '{s}'\n", .{args[0]});
|
||||||
|
} else if (std.mem.eql(u8, op, "role")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "remote-user role needs <username> readonly|readwrite|admin");
|
||||||
|
const role = cobalt.AuthRole.parse(args[1]) catch return fail(stderr, "role must be readonly, readwrite, or admin");
|
||||||
|
client.adminSetRole(args[0], role) catch |err| {
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
try stdout.print("role for '{s}' set to {s}\n", .{ args[0], role.name() });
|
||||||
|
} else if (std.mem.eql(u8, op, "del")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "remote-user del needs <username>");
|
||||||
|
client.adminDeleteUser(args[0]) catch |err| {
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
try stdout.print("user '{s}' deleted\n", .{args[0]});
|
||||||
|
} else if (std.mem.eql(u8, op, "list")) {
|
||||||
|
const users = client.adminListUsers(allocator) catch |err| {
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
defer cobalt.auth.freeUserList(allocator, users);
|
||||||
|
for (users) |u| try stdout.print("{s}\t{s}\n", .{ u.username, u.role.name() });
|
||||||
|
} else {
|
||||||
|
return fail(stderr, "unknown remote-user operation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The network counterpart of `runTtl`-shaped local commands (`expire`,
|
||||||
|
/// `persist`, `ttl`, `expire-sweep`): dispatches `remote-ttl <op> ...` onto
|
||||||
|
/// `client.zig`'s `ttl*` methods (which wire-encode the `ttl_op` opcode)
|
||||||
|
/// instead of calling `ttl.zig` directly against a local Db, the same
|
||||||
|
/// "reuse client.zig, keep the arg parsing local" shape as `runRemoteUser`.
|
||||||
|
fn runRemoteTtl(allocator: std.mem.Allocator, stdout: anytype, stderr: anytype, addressSpec: []const u8, rest: []const []const u8) !void {
|
||||||
|
if (rest.len < 1) return fail(stderr, "remote-ttl needs a subcommand (expire/persist/ttl/sweep)");
|
||||||
|
const address = try parseHostPort(stdout, addressSpec);
|
||||||
|
|
||||||
|
var client = cobalt.Client.connect(allocator, address) catch |err| {
|
||||||
|
try stderr.print("error: could not connect to {s}: {s}\n", .{ addressSpec, @errorName(err) });
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
defer client.close();
|
||||||
|
|
||||||
|
const authFlags = extractAuthFlags(allocator, rest[1..]) catch return fail(stderr, "invalid --user/--pass/--token value");
|
||||||
|
defer allocator.free(authFlags.rest);
|
||||||
|
|
||||||
|
try authenticateIfRequested(&client, stderr, authFlags.auth);
|
||||||
|
|
||||||
|
const op = rest[0];
|
||||||
|
const args = authFlags.rest;
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, op, "expire")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "remote-ttl expire needs <key> <seconds>");
|
||||||
|
const seconds = std.fmt.parseInt(u64, args[1], 10) catch return fail(stderr, "expire needs a whole number of seconds");
|
||||||
|
client.ttlExpire(args[0], seconds) catch |err| {
|
||||||
|
if (err == error.ServerKeyNotFound) return fail(stderr, "no such key");
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
} else if (std.mem.eql(u8, op, "persist")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "remote-ttl persist needs <key>");
|
||||||
|
client.ttlPersist(args[0]) catch |err| {
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
} else if (std.mem.eql(u8, op, "ttl")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "remote-ttl ttl needs <key>");
|
||||||
|
const result = client.ttlQuery(args[0]) catch |err| {
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
switch (result) {
|
||||||
|
.noKey => try stdout.writeAll("-2\n"),
|
||||||
|
.noExpiry => try stdout.writeAll("-1\n"),
|
||||||
|
.seconds => |s| try stdout.print("{d}\n", .{s}),
|
||||||
|
}
|
||||||
|
} else if (std.mem.eql(u8, op, "sweep")) {
|
||||||
|
const reaped = client.ttlSweep() catch |err| {
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
try stdout.print("reaped: {d}\n", .{reaped});
|
||||||
|
} else {
|
||||||
|
return fail(stderr, "unknown remote-ttl operation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The network counterpart of `runCollTtl`-shaped local commands (`coll
|
||||||
|
/// <kind> expire|ttl|persist`, `coll expire-sweep`): dispatches
|
||||||
|
/// `remote-coll-ttl sweep` or `remote-coll-ttl <kind> <op> ...` onto
|
||||||
|
/// `client.zig`'s `collTtl*` methods (which wire-encode the `coll_ttl_op`
|
||||||
|
/// opcode), same "reuse client.zig, keep the arg parsing local" shape as
|
||||||
|
/// `runRemoteTtl`. `sweep` has no `<kind>` (it reaps every kind at once,
|
||||||
|
/// same as local `coll expire-sweep`), so it is checked before trying to
|
||||||
|
/// parse `rest[0]` as a kind.
|
||||||
|
fn runRemoteCollTtl(allocator: std.mem.Allocator, stdout: anytype, stderr: anytype, addressSpec: []const u8, rest: []const []const u8) !void {
|
||||||
|
if (rest.len < 1) return fail(stderr, "remote-coll-ttl needs sweep or <kind> expire|ttl|persist <name>");
|
||||||
|
const address = try parseHostPort(stdout, addressSpec);
|
||||||
|
|
||||||
|
var client = cobalt.Client.connect(allocator, address) catch |err| {
|
||||||
|
try stderr.print("error: could not connect to {s}: {s}\n", .{ addressSpec, @errorName(err) });
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
defer client.close();
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, rest[0], "sweep")) {
|
||||||
|
const authFlags = extractAuthFlags(allocator, rest[1..]) catch return fail(stderr, "invalid --user/--pass/--token value");
|
||||||
|
defer allocator.free(authFlags.rest);
|
||||||
|
try authenticateIfRequested(&client, stderr, authFlags.auth);
|
||||||
|
const reaped = client.collTtlSweep() catch |err| {
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
try stdout.print("reaped: {d}\n", .{reaped});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const kind = cobalt.CollectionKind.parse(rest[0]) orelse
|
||||||
|
return fail(stderr, "unknown collection kind (expected counter/hash/set/list/zset)");
|
||||||
|
if (rest.len < 2) return fail(stderr, "remote-coll-ttl <kind> needs expire|ttl|persist <name>");
|
||||||
|
|
||||||
|
const authFlags = extractAuthFlags(allocator, rest[2..]) catch return fail(stderr, "invalid --user/--pass/--token value");
|
||||||
|
defer allocator.free(authFlags.rest);
|
||||||
|
|
||||||
|
try authenticateIfRequested(&client, stderr, authFlags.auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runVerify(allocator: std.mem.Allocator, stdout: anytype, dir: []const u8, count: usize) !void {
|
fn runVerify(allocator: std.mem.Allocator, stdout: anytype, dir: []const u8, count: usize) !void {
|
||||||
|
|||||||
Reference in new issue
Block a user