diff --git a/Dockerfile b/Dockerfile index 47ae8df..4752ea1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:3.19 AS builder -RUN apk add --no-cache zig --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing +RUN apk add --no-cache zig --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community WORKDIR /work COPY src src diff --git a/build.zig b/build.zig index badefc4..17a6944 100644 --- a/build.zig +++ b/build.zig @@ -19,11 +19,11 @@ pub fn build(b: *std.Build) void { .name = "codefirst-dockerproxy-clientdrone", // In this case the main source file is merely a path, however, in more // complicated build scripts, this could be a generated file. - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, + .strip = true, }); - exe.strip = true; // This declares intent for the executable to be installed into the // standard location when the user invokes the "install" step (the default diff --git a/src/main.zig b/src/main.zig index 1ee4643..efbf2e5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -24,32 +24,30 @@ const CodefirstContainer = struct { const jsonOptions = std.json.StringifyOptions{ .emit_null_optional_fields = false }; /// Sends a request to the CodeFirst proxy and print the response body. -fn sendRequest(method: std.http.Method, auth: CodefirstAuth, path: []const u8, body: []const u8) !std.http.Status { - const uri = std.Uri.parse(path) catch unreachable; - var headers = std.http.Headers{ .allocator = allocator }; - defer headers.deinit(); - try headers.append(ForwardedUser, auth.user); - if (body.len > 0) { - try headers.append("Content-Type", "application/json"); +fn sendRequest(method: std.http.Method, auth: CodefirstAuth, path: []const u8, body: ?[]const u8) !std.http.Status { + var response_body = std.ArrayList(u8).init(allocator); + const headers = [_]std.http.Header{.{ .name = ForwardedUser, .value = auth.user }}; + const res = try client.fetch(.{ + .method = method, + .location = .{ .url = path }, + .headers = .{ .content_type = if (body != null) .{ .override = "application/json" } else .default }, + .extra_headers = &headers, + .payload = body, + .response_storage = .{ .dynamic = &response_body }, + }); + if (res.status.class() == .success) { + std.log.info("{s}", .{response_body.items}); + } else { + std.log.err("[HTTP {d}] {s}", .{ res.status, response_body.items }); } - var req = try client.request(method, uri, headers, .{}); - defer req.deinit(); - req.transfer_encoding = std.http.Client.RequestTransfer{ .content_length = body.len }; - try req.start(); - try req.writeAll(body); - try req.finish(); - try req.wait(); - const responseBody = req.reader().readAllAlloc(allocator, 8192) catch unreachable; - defer allocator.free(responseBody); - std.log.info("{s}", .{responseBody}); - return req.response.status; + return res.status; } /// Tests if a container with the given name exists. fn exists(auth: CodefirstAuth, containerName: []const u8) !bool { const path = try std.fmt.allocPrint(allocator, "{s}://{s}/containers/{s}/json", .{ auth.proxyScheme, auth.proxyHost, containerName }); defer allocator.free(path); - var status = try sendRequest(.GET, auth, path, ""); + const status = try sendRequest(.GET, auth, path, null); return status == .ok; } @@ -57,7 +55,7 @@ fn exists(auth: CodefirstAuth, containerName: []const u8) !bool { fn createImage(auth: CodefirstAuth, imageName: []const u8) !void { const path = try std.fmt.allocPrint(allocator, "{s}://{s}/images/create?fromImage={s}", .{ auth.proxyScheme, auth.proxyHost, imageName }); defer allocator.free(path); - _ = try sendRequest(.POST, auth, path, ""); + _ = try sendRequest(.POST, auth, path, null); } /// Creates a container with the given name. @@ -99,21 +97,22 @@ fn start(auth: CodefirstAuth, imageName: []const u8, containerName: []const u8, fn delete(auth: CodefirstAuth, containerName: []const u8) !void { const path = try std.fmt.allocPrint(allocator, "{s}://{s}/containers/{s}", .{ auth.proxyScheme, auth.proxyHost, containerName }); defer allocator.free(path); - _ = try sendRequest(.DELETE, auth, path, ""); + _ = try sendRequest(.DELETE, auth, path, null); } pub fn run() !void { - const command_name = std.os.getenv("PLUGIN_COMMAND") orelse return PluginError.InvalidCommand; + const env_map = std.process.getEnvMap(allocator) catch unreachable; + const command_name = env_map.get("PLUGIN_COMMAND") orelse return PluginError.InvalidCommand; const command = std.meta.stringToEnum(Command, command_name) orelse return PluginError.InvalidCommand; const auth = CodefirstAuth{ - .proxyScheme = std.os.getenv("PROXYSCHEME") orelse "http", - .proxyHost = std.os.getenv("PROXYHOST") orelse "dockerproxy:8080", - .user = std.os.getenv("DRONE_REPO_OWNER") orelse return PluginError.InvalidUser, + .proxyScheme = env_map.get("PROXYSCHEME") orelse "http", + .proxyHost = env_map.get("PROXYHOST") orelse "dockerproxy:8080", + .user = env_map.get("DRONE_REPO_OWNER") orelse return PluginError.InvalidUser, }; - var containerNameEnv = std.os.getenv("PLUGIN_CONTAINER") orelse return PluginError.NoContainerName; - var shortUser = try std.mem.replaceOwned(u8, allocator, auth.user, ".", ""); + const containerNameEnv = env_map.get("PLUGIN_CONTAINER") orelse return PluginError.NoContainerName; + const shortUser = try std.mem.replaceOwned(u8, allocator, auth.user, ".", ""); defer allocator.free(shortUser); - var containerName = try std.fmt.allocPrint(allocator, "{s}-{s}", .{ shortUser, containerNameEnv }); + const containerName = try std.fmt.allocPrint(allocator, "{s}-{s}", .{ shortUser, containerNameEnv }); defer allocator.free(containerName); var idx: usize = 0; @@ -140,10 +139,10 @@ pub fn run() !void { switch (command) { .create => { - const imageName = std.os.getenv("PLUGIN_IMAGE") orelse return PluginError.InvalidImage; - const private = std.mem.eql(u8, std.os.getenv("PLUGIN_PRIVATE") orelse "false", "true"); - const overwrite = std.mem.eql(u8, std.os.getenv("PLUGIN_OVERWRITE") orelse "false", "true"); - const admins = std.os.getenv("PLUGIN_ADMINS") orelse auth.user; + const imageName = env_map.get("PLUGIN_IMAGE") orelse return PluginError.InvalidImage; + const private = std.mem.eql(u8, env_map.get("PLUGIN_PRIVATE") orelse "false", "true"); + const overwrite = std.mem.eql(u8, env_map.get("PLUGIN_OVERWRITE") orelse "false", "true"); + const admins = env_map.get("PLUGIN_ADMINS") orelse auth.user; const container = CodefirstContainer{ .Id = "", @@ -167,8 +166,8 @@ pub fn run() !void { } }, .start => { - const imageName = std.os.getenv("PLUGIN_IMAGE") orelse return PluginError.InvalidImage; - const private = std.mem.eql(u8, std.os.getenv("PLUGIN_PRIVATE") orelse "false", "true"); + const imageName = env_map.get("PLUGIN_IMAGE") orelse return PluginError.InvalidImage; + const private = std.mem.eql(u8, env_map.get("PLUGIN_PRIVATE") orelse "false", "true"); try start(auth, imageName, containerName, envs.items, private); }, .delete => { @@ -187,6 +186,6 @@ pub fn main() !void { PluginError.InvalidImage => std.log.err("Invalid image", .{}), else => std.log.err("{}", .{err}), } - std.os.exit(1); + std.process.exit(1); }; }