forked from lobo/lambda-zig
170 lines
6.9 KiB
Zig
170 lines
6.9 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const Package = @import("lambdabuild/Package.zig");
|
|
const Iam = @import("lambdabuild/Iam.zig");
|
|
const Deploy = @import("lambdabuild/Deploy.zig");
|
|
const Invoke = @import("lambdabuild/Invoke.zig");
|
|
|
|
fn fileExists(file_name: []const u8) bool {
|
|
const file = std.fs.openFileAbsolute(file_name, .{}) catch return false;
|
|
defer file.close();
|
|
return true;
|
|
}
|
|
fn addArgs(allocator: std.mem.Allocator, original: []const u8, args: [][]const u8) ![]const u8 {
|
|
var rc = original;
|
|
for (args) |arg| {
|
|
rc = try std.mem.concat(allocator, u8, &.{ rc, " ", arg });
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/// lambdaBuildSteps will add four build steps to the build (if compiling
|
|
/// the code on a Linux host):
|
|
///
|
|
/// * awslambda_package: Packages the function for deployment to Lambda
|
|
/// (dependencies are the zip executable and a shell)
|
|
/// * awslambda_iam: Gets an IAM role for the Lambda function, and creates it if it does not exist
|
|
/// (dependencies are the AWS CLI, grep and a shell)
|
|
/// * awslambda_deploy: Deploys the lambda function to a live AWS environment
|
|
/// (dependencies are the AWS CLI, and a shell)
|
|
/// * awslambda_run: Runs the lambda function in a live AWS environment
|
|
/// (dependencies are the AWS CLI, and a shell)
|
|
///
|
|
/// awslambda_run depends on deploy
|
|
/// awslambda_deploy depends on iam and package
|
|
///
|
|
/// iam and package do not have any dependencies
|
|
pub fn configureBuild(b: *std.Build, exe: *std.Build.Step.Compile, function_name: []const u8) !void {
|
|
// The rest of this function is currently reliant on the use of Linux
|
|
// system being used to build the lambda function
|
|
//
|
|
// It is likely that much of this will work on other Unix-like OSs, but
|
|
// we will work this out later
|
|
//
|
|
// TODO: support other host OSs
|
|
if (builtin.os.tag != .linux) return;
|
|
|
|
@import("aws").aws.globalLogControl(.info, .warn, .info, false);
|
|
const package_step = Package.create(b, .{ .exe = exe });
|
|
|
|
const step = b.step("awslambda_package", "Package the function");
|
|
step.dependOn(&package_step.step);
|
|
package_step.step.dependOn(b.getInstallStep());
|
|
|
|
// Doing this will require that the aws dependency be added to the downstream
|
|
// build.zig.zon
|
|
// const lambdabuild = b.addExecutable(.{
|
|
// .name = "lambdabuild",
|
|
// .root_source_file = .{
|
|
// // we use cwd_relative here because we need to compile this relative
|
|
// // to whatever directory this file happens to be. That is likely
|
|
// // in a cache directory, not the base of the build.
|
|
// .cwd_relative = try std.fs.path.join(b.allocator, &[_][]const u8{
|
|
// std.fs.path.dirname(@src().file).?,
|
|
// "lambdabuild/src/main.zig",
|
|
// }),
|
|
// },
|
|
// .target = b.host,
|
|
// });
|
|
// const aws_dep = b.dependency("aws", .{
|
|
// .target = b.host,
|
|
// .optimize = lambdabuild.root_module.optimize orelse .Debug,
|
|
// });
|
|
// const aws_module = aws_dep.module("aws");
|
|
// lambdabuild.root_module.addImport("aws", aws_module);
|
|
//
|
|
|
|
const iam_role_name = b.option(
|
|
[]const u8,
|
|
"function-role",
|
|
"IAM role name for function (will create if it does not exist) [lambda_basic_execution]",
|
|
) orelse "lambda_basic_execution_blah2";
|
|
|
|
const iam_role_arn = b.option(
|
|
[]const u8,
|
|
"function-arn",
|
|
"Preexisting IAM role arn for function",
|
|
);
|
|
|
|
const iam = Iam.create(b, .{
|
|
.role_name = iam_role_name,
|
|
.role_arn = iam_role_arn,
|
|
});
|
|
const iam_step = b.step("awslambda_iam", "Create/Get IAM role for function");
|
|
iam_step.dependOn(&iam.step);
|
|
|
|
const region = try b.allocator.create(@import("lambdabuild/Region.zig"));
|
|
region.* = .{
|
|
.allocator = b.allocator,
|
|
.specified_region = b.option([]const u8, "region", "Region to use [default is autodetect from environment/config]"),
|
|
};
|
|
|
|
// Deployment
|
|
const deploy = Deploy.create(b, .{
|
|
.name = function_name,
|
|
.package = package_step.packagedFileLazyPath(),
|
|
.arch = exe.root_module.resolved_target.?.result.cpu.arch,
|
|
.iam_step = iam,
|
|
.region = region,
|
|
});
|
|
deploy.step.dependOn(&package_step.step);
|
|
|
|
const deploy_step = b.step("awslambda_deploy", "Deploy the function");
|
|
deploy_step.dependOn(&deploy.step);
|
|
|
|
const payload = b.option([]const u8, "payload", "Lambda payload [{\"foo\":\"bar\", \"baz\": \"qux\"}]") orelse
|
|
\\ {"foo": "bar", "baz": "qux"}"
|
|
;
|
|
|
|
const invoke = Invoke.create(b, .{
|
|
.name = function_name,
|
|
.payload = payload,
|
|
.region = region,
|
|
});
|
|
invoke.step.dependOn(&deploy.step);
|
|
const run_step = b.step("awslambda_run", "Run the app in AWS lambda");
|
|
run_step.dependOn(&invoke.step);
|
|
}
|
|
|
|
// AWS_CONFIG_FILE (default is ~/.aws/config
|
|
// AWS_DEFAULT_REGION
|
|
fn findRegionFromSystem(allocator: std.mem.Allocator) ![]const u8 {
|
|
const env_map = try std.process.getEnvMap(allocator);
|
|
if (env_map.get("AWS_DEFAULT_REGION")) |r| return r;
|
|
const config_file_path = env_map.get("AWS_CONFIG_FILE") orelse
|
|
try std.fs.path.join(allocator, &[_][]const u8{
|
|
env_map.get("HOME") orelse env_map.get("USERPROFILE").?,
|
|
".aws",
|
|
"config",
|
|
});
|
|
const config_file = try std.fs.openFileAbsolute(config_file_path, .{});
|
|
defer config_file.close();
|
|
const config_bytes = try config_file.readToEndAlloc(allocator, 1024 * 1024);
|
|
const profile = env_map.get("AWS_PROFILE") orelse "default";
|
|
var line_iterator = std.mem.split(u8, config_bytes, "\n");
|
|
var in_profile = false;
|
|
while (line_iterator.next()) |line| {
|
|
const trimmed = std.mem.trim(u8, line, " \t\r");
|
|
if (trimmed.len == 0 or trimmed[0] == '#') continue;
|
|
if (!in_profile) {
|
|
if (trimmed[0] == '[' and trimmed[trimmed.len - 1] == ']') {
|
|
// this is a profile directive!
|
|
// std.debug.print("profile: {s}, in file: {s}\n", .{ profile, trimmed[1 .. trimmed.len - 1] });
|
|
if (std.mem.eql(u8, profile, trimmed[1 .. trimmed.len - 1])) {
|
|
in_profile = true;
|
|
}
|
|
}
|
|
continue; // we're only looking for a profile at this point
|
|
}
|
|
// look for our region directive
|
|
if (trimmed[0] == '[' and trimmed[trimmed.len - 1] == ']')
|
|
return error.RegionNotFound; // we've hit another profile without getting our region
|
|
if (!std.mem.startsWith(u8, trimmed, "region")) continue;
|
|
var equalityiterator = std.mem.split(u8, trimmed, "=");
|
|
_ = equalityiterator.next() orelse return error.RegionNotFound;
|
|
const raw_val = equalityiterator.next() orelse return error.RegionNotFound;
|
|
return try allocator.dupe(u8, std.mem.trimLeft(u8, raw_val, " \t"));
|
|
}
|
|
return error.RegionNotFound;
|
|
}
|