From fa62c57585410144bd11bce9a58f71b041f07471 Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Mon, 2 Oct 2023 15:05:19 +0200 Subject: [PATCH] Rewrite in Zig --- .drone.yml | 4 +- .gitignore | 7 ++ .golangci.yml | 102 ------------------- Dockerfile | 23 ++--- README.md | 35 +++---- build.zig | 55 +++++++++++ entrypoint.sh | 88 ----------------- go.mod | 7 -- go.sum | 9 -- main.go | 267 -------------------------------------------------- src/main.zig | 164 +++++++++++++++++++++++++++++++ 11 files changed, 250 insertions(+), 511 deletions(-) delete mode 100644 .golangci.yml create mode 100644 build.zig delete mode 100755 entrypoint.sh delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 main.go create mode 100644 src/main.zig diff --git a/.drone.yml b/.drone.yml index 8f43dee..bc737bd 100644 --- a/.drone.yml +++ b/.drone.yml @@ -13,8 +13,8 @@ steps: dockerfile: Dockerfile context: . registry: hub.codefirst.iut.uca.fr - repo: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone + repo: hub.codefirst.iut.uca.fr/clement.freville2/codefirst-dockerproxy-clientdrone username: from_secret: SECRET_REGISTRY_USERNAME password: - from_secret: SECRET_REGISTRY_PASSWORD \ No newline at end of file + from_secret: SECRET_REGISTRY_PASSWORD diff --git a/.gitignore b/.gitignore index adf8f72..148d743 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,10 @@ # Go workspace file go.work +# IDE +.vscode +.idea + +# Zig +zig-cache/ +zig-out/ diff --git a/.golangci.yml b/.golangci.yml deleted file mode 100644 index 7a982dc..0000000 --- a/.golangci.yml +++ /dev/null @@ -1,102 +0,0 @@ -# options for analysis running -run: - # timeout for analysis, e.g. 30s, 5m, default is 1m - timeout: 1m - - # exit code when at least one issue was found, default is 1 - issues-exit-code: 0 - - # include test files or not, default is true - tests: false - - # which dirs to skip: issues from them won't be reported; - # can use regexp here: generated.*, regexp is applied on full path; - # default value is empty list, but default dirs are skipped independently - # from this option's value (see skip-dirs-use-default). - # "/" will be replaced by current OS file path separator to properly work - # on Windows. - skip-dirs: - - wasm - - static - - node_modules - - documents - - docker - - bind-* - - # default is true. Enables skipping of directories: - # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ - skip-dirs-use-default: true - - # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": - # If invoked with -mod=readonly, the go command is disallowed from the implicit - # automatic updating of go.mod described above. Instead, it fails when any changes - # to go.mod are needed. This setting is most useful to check that go.mod does - # not need updates, such as in a continuous integration and testing system. - # If invoked with -mod=vendor, the go command assumes that the vendor - # directory holds the correct copies of dependencies and ignores - # the dependency descriptions in go.mod. - modules-download-mode: mod - - # Allow multiple parallel golangci-lint instances running. - # If false (default) - golangci-lint acquires file lock on start. - allow-parallel-runners: true - - -# output configuration options -output: - # colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions - # default is "colored-line-number" - format: colored-line-number - - # print lines of code with issue, default is true - print-issued-lines: true - - # print linter name in the end of issue text, default is true - print-linter-name: true - - # make issues output unique by line, default is true - uniq-by-line: true - - # add a prefix to the output file references; default is no prefix - path-prefix: "" - - # sorts results by: filepath, line and column - sort-results: false - - -# all available settings of specific linters -linters-settings: - gofmt: - # simplify code: gofmt with `-s` option, true by default - simplify: true - staticcheck: - # Select the Go version to target. The default is '1.13'. - go: "1.19" - # https://staticcheck.io/docs/options#checks - checks: ["all"] - stylecheck: - # Select the Go version to target. The default is '1.13'. - go: "1.19" - # https://staticcheck.io/docs/options#checks - checks: ["all"] - errcheck: - # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; - # default is false: such cases aren't reported by default. - check-blank: true - -linters: - enable: - # go install github.com/kisielk/errcheck@latest - # go install github.com/gordonklaus/ineffassign@latest - # go install honnef.co/go/tools/cmd/staticcheck@latest - # go install gitlab.com/opennota/check/cmd/varcheck@latest - # go install github.com/go-critic/go-critic/cmd/gocritic@latest - - errcheck - - staticcheck - - stylecheck - - ineffassign - - varcheck - - gofmt - - gocritic - - wsl - fast: false diff --git a/Dockerfile b/Dockerfile index 69c343a..c417db3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,13 @@ -FROM golang:1.19-bullseye -LABEL author="Thomas Bellembois" +FROM docker.io/alpine:3.18 AS builder -# Copying sources. -WORKDIR /go/src/codefirst.iut.uca.fr/git/thomas.bellembois/codefirst-dockerproxy-clientdrone/v2/ -COPY . . +RUN apk add --no-cache zig --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing -# Installing. -RUN go install -v ./... -RUN chmod +x /go/bin/codefirst-dockerproxy-clientdrone +WORKDIR /work +COPY src src +COPY build.zig build.zig -# Copying entrypoint. -COPY entrypoint.sh / -RUN chmod +x /entrypoint.sh +RUN zig build -Doptimize=ReleaseSafe -USER root -EXPOSE 8081 -ENTRYPOINT [ "/entrypoint.sh" ] \ No newline at end of file +FROM scratch AS runner +COPY --from=builder /work/zig-out/bin/codefirst-dockerproxy-clientdrone / +ENTRYPOINT [ "/codefirst-dockerproxy-clientdrone" ] diff --git a/README.md b/README.md index c7aea14..36b0ea2 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,18 @@ -sur le proxy: -``` -docker network create cicd_net -./zig-out/bin/codefirst-dockerproxy -d -``` +# codefirst-dockerproxy-clientdrone -sur le client web: -``` -# Ajuster la constante proxyUrl et proxyPath puis compiler. -docker run --rm -it --volume $(pwd):/app sandrokeil/typescript tsc /app/src/index.ts -mv src/index.js build/src/index.js +Usage: -# Build l'image. -docker build -t hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientweb . - -# Run le container. -docker run -p 8081:80 hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientweb +```yaml + - name: deploy-container + image: hub.codefirst.iut.uca.fr/clement.freville2/codefirst-dockerproxy-clientdrone:latest + settings: + image: hub.codefirst.iut.uca.fr/my.login/my_repository:latest + container: my_container_name + command: create + overwrite: true ``` --> http://localhost:8081/dockerrunner/ +## Changes from upstream -sur le client cd: -``` -go run . -proxyhost localhost:8080 -devel -command create -imagename nginx -containername nginx1 -admins john.doe,mickey.mouse -go run . -proxyhost localhost:8080 -devel -command create -imagename nginx -containername nginx2 -go run . -proxyhost localhost:8080 -devel -command create -imagename nginx -containername nginx3 -``` \ No newline at end of file +- Removed the shell script to handle spaces in arguments. +- Used Drone settings instead of environment variables. diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..808e210 --- /dev/null +++ b/build.zig @@ -0,0 +1,55 @@ +const std = @import("std"); + +// Although this function looks imperative, note that its job is to +// declaratively construct a build graph that will be executed by an external +// runner. +pub fn build(b: *std.Build) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .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" }, + .target = target, + .optimize = optimize, + }); + 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 + // step when running `zig build`). + b.installArtifact(exe); + + // This *creates* a Run step in the build graph, to be executed when another + // step is evaluated that depends on it. The next line below will establish + // such a dependency. + const run_cmd = b.addRunArtifact(exe); + + // By making the run step depend on the install step, it will be run from the + // installation directory rather than directly from within the cache directory. + // This is not necessary, however, if the application depends on other installed + // files, this ensures they will be present and in the expected location. + run_cmd.step.dependOn(b.getInstallStep()); + + // This allows the user to pass arguments to the application in the build + // command itself, like this: `zig build run -- arg1 arg2 etc` + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // This creates a build step. It will be visible in the `zig build --help` menu, + // and can be selected like this: `zig build run` + // This will evaluate the `run` step rather than the default, which is "install". + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); +} diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index e2b936d..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash -ProxyScheme="" -ProxyHost="" -ProxyPath="" - -ImageName="" -ContainerName="" -Overwrite="" -Private="" -Env="" -Command="" -Admins="" - -if [ ! -z "$PROXYSCHEME" ] -then - ProxyScheme="-proxyscheme $PROXYSCHEME" -fi - -if [ ! -z "$PROXYHOST" ] -then - ProxyHost="-proxyhost $PROXYHOST" -fi - -if [ ! -z "$PROXYPATH" ] -then - ProxyPath="-proxypath $PROXYPATH" -fi - -if [ ! -z "$IMAGENAME" ] -then - ImageName="-imagename $IMAGENAME" -fi - -if [ ! -z "$CONTAINERNAME" ] -then - ContainerName="-containername $CONTAINERNAME" -fi - -if [ ! -z "$COMMAND" ] -then - Command="-command $COMMAND" -fi - -if [ ! -z "$ADMINS" ] -then - Admins="-admins $ADMINS" -fi - - -if [ ! -z "$PRIVATE" ] -then - Private="-private" -fi - -if [ ! -z "$OVERWRITE" ] -then - Overwrite="-overwrite" -fi - -prefix="CODEFIRST_CLIENTDRONE_ENV_" -ENVS=$(env | awk -F "=" '{print $1}' | grep ".*$prefix.*") - -if [ ! -z "$ENVS" ] -then - Env="" - arrayEnv=($ENVS) - - for i in "${arrayEnv[@]}" - do - envVarName=${i#"$prefix"} - Env=$Env" -env $envVarName=${!i}" - done -fi - -echo $ProxyScheme -echo $ProxyHost -echo $ProxyPath - -echo $ImageName -echo $ContainerName -echo $Overwrite -echo $Private -echo $Admins -echo $Env -echo $Command - -#/go/bin -sh -c "/go/bin/codefirst-dockerproxy-clientdrone $ProxyScheme $ProxyHost $ProxyPath $ImageName $ContainerName $Private $Admins $Overwrite $Env $Command" diff --git a/go.mod b/go.mod deleted file mode 100644 index b8bdf20..0000000 --- a/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module codefirst.iut.uca.fr/git/thomas.bellembois/codefirst-dockerproxy-clientdrone - -go 1.19 - -require github.com/go-resty/resty/v2 v2.7.0 - -require golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect diff --git a/go.sum b/go.sum deleted file mode 100644 index a5c1a24..0000000 --- a/go.sum +++ /dev/null @@ -1,9 +0,0 @@ -github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= -github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= -golang.org/x/net v0.0.0-20211029224645-99673261e6eb h1:pirldcYWx7rx7kE5r+9WsOXPXK0+WH5+uZ7uPmJ44uM= -golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/main.go b/main.go deleted file mode 100644 index 464f478..0000000 --- a/main.go +++ /dev/null @@ -1,267 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os" - "strings" - - "github.com/go-resty/resty/v2" -) - -type CodeFirstContainer struct { - ID string `json:"Id"` - Image string `json:"Image"` - Env []string `json:"Env,omitempty"` - Admins string `json:"Admins,omitempty"` - Private bool `json:"Private,omitempty"` -} - -type StringSliceFlag struct { - value []string -} - -func (s *StringSliceFlag) String() string { - return fmt.Sprintf("%s", *s) -} - -func (s *StringSliceFlag) Set(v string) error { - s.value = append(s.value, v) - return nil -} - -var ( - authUser string - - command string - proxyScheme, proxyHost, proxyPath string - imageName, containerName, admins string - - private, overwrite, devel bool - - env StringSliceFlag -) - -func main() { - - env = StringSliceFlag{} - - flag.StringVar(&command, "command", "list", "list|logs|create|delete|start") - flag.BoolVar(&devel, "devel", false, "use fake x-forwarded-user") - - flag.StringVar(&proxyScheme, "proxyscheme", "http", "proxy scheme") - flag.StringVar(&proxyHost, "proxyhost", "dockerproxy:8080", "proxy host") - flag.StringVar(&proxyPath, "proxypath", "/", "proxy path") - - flag.StringVar(&imageName, "imagename", "", "image name") - flag.StringVar(&containerName, "containername", "", "container name") - flag.StringVar(&admins, "admins", "", "admins (comma separated list)") - flag.BoolVar(&private, "private", false, "private container") - flag.BoolVar(&overwrite, "overwrite", false, "overwrite existing container") - flag.Var(&env, "env", "environment variables (separated by spaces)") - - flag.Parse() - - fmt.Println("flags:") - fmt.Printf("-imagename: %s\n", imageName) - fmt.Printf("-containername: %s\n", containerName) - fmt.Printf("-private: %t\n", private) - fmt.Printf("-admins: %s\n", admins) - fmt.Printf("-overwrite: %t\n", overwrite) - fmt.Printf("-env: %s\n", env) - - if command != "list" && containerName == "" { - fmt.Println("Missing containername parameter.") - os.Exit(1) - } - - if command == "create" && imageName == "" { - fmt.Println("Missing imagename parameter.") - os.Exit(1) - } - - if devel { - authUser = "thomas.bellembois" - } else { - authUser = os.Getenv("DRONE_REPO_OWNER") - } - - containerName = authUser + "-" + containerName - containerName = strings.ReplaceAll(containerName, ".", "") - - fmt.Printf("authUser: %s\n", authUser) - fmt.Printf("new containerName: %s\n", containerName) - - if len(authUser) == 0 { - fmt.Println("Not authenticated.") - os.Exit(1) - } - - switch command { - case "list": - list() - case "logs": - logs() - case "create": - if overwrite { - delete(true) - } - - if !exist() { - createImage() - create() - start() - } - case "start": - start() - case "delete": - delete(false) - } - -} - -func exist() bool { - client := resty.New() - - resp, err := client.R(). - SetHeader("x-forwarded-user", authUser). - Get(fmt.Sprintf("%s://%s/containers/%s/json", proxyScheme, proxyHost, containerName)) - - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - fmt.Println(string(resp.Body())) - - return resp.IsSuccess() -} - -func list() { - client := resty.New() - - resp, err := client.R(). - SetHeader("x-forwarded-user", authUser). - Get(fmt.Sprintf("%s://%s/containers/json", proxyScheme, proxyHost)) - - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - fmt.Println(string(resp.Body())) - - if resp.IsError() { - os.Exit(1) - } -} - -func logs() { - client := resty.New() - - resp, err := client.R(). - SetHeader("x-forwarded-user", authUser). - Get(fmt.Sprintf("%s://%s/containers/%s/logs", proxyScheme, proxyHost, containerName)) - - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - fmt.Println(string(resp.Body())) - - if resp.IsError() { - os.Exit(1) - } -} - -func start() { - client := resty.New() - - container := CodeFirstContainer{ - Image: imageName, - Env: env.value, - Private: private, - } - - resp, err := client.R(). - SetHeader("x-forwarded-user", authUser). - SetBody(container). - Post(fmt.Sprintf("%s://%s/containers/%s/start", proxyScheme, proxyHost, containerName)) - - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - fmt.Println(string(resp.Body())) - - if resp.IsError() { - os.Exit(1) - } -} - -func create() { - client := resty.New() - - container := CodeFirstContainer{ - Image: imageName, - Env: env.value, - Private: private, - Admins: admins, - } - - resp, err := client.R(). - SetHeader("x-forwarded-user", authUser). - SetBody(container). - Post(fmt.Sprintf("%s://%s/containers/create/%s", proxyScheme, proxyHost, containerName)) - - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - fmt.Println(string(resp.Body())) - - if resp.IsError() { - os.Exit(1) - } -} - -func delete(bypassError bool) { - client := resty.New() - - resp, err := client.R(). - SetHeader("x-forwarded-user", authUser). - Delete(fmt.Sprintf("%s://%s/containers/%s", proxyScheme, proxyHost, containerName)) - - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - fmt.Println(string(resp.Body())) - - if !bypassError && resp.IsError() { - os.Exit(1) - } -} - -func createImage() { - client := resty.New() - - resp, err := client.R(). - SetHeader("x-forwarded-user", authUser). - Post(fmt.Sprintf("%s://%s/images/create?fromImage=%s", proxyScheme, proxyHost, imageName)) - - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - fmt.Println(string(resp.Body())) - - if resp.IsError() { - os.Exit(1) - } -} diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..a9bfec0 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,164 @@ +const std = @import("std"); +const allocator = std.heap.page_allocator; +var client: std.http.Client = .{ .allocator = allocator }; +const Command = enum { list, logs, create, delete, start }; + +const ForwardedUser = "X-Forwarded-User"; + +const PluginError = error{ + InvalidUser, + InvalidCommand, + NoContainerName, + InvalidImage, +}; + +const CodefirstAuth = struct { + proxyScheme: []const u8, + proxyHost: []const u8, + user: []const u8, +}; + +const CodefirstContainer = struct { + Id: []const u8, + Image: []const u8, + Admins: []const u8, + Env: [][]const u8, + Private: bool, +}; + +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"); + } + 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; +} + +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, ""); + return status == .ok; +} + +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, ""); +} + +fn create(auth: CodefirstAuth, container: CodefirstContainer, containerName: []const u8) !void { + const path = try std.fmt.allocPrint(allocator, "{s}://{s}/containers/create/{s}", .{ auth.proxyScheme, auth.proxyHost, containerName }); + defer allocator.free(path); + var json = std.ArrayList(u8).init(allocator); + defer json.deinit(); + try std.json.stringify(container, .{}, json.writer()); + _ = try sendRequest(.POST, auth, path, json.items); +} + +fn start(auth: CodefirstAuth, imageName: []const u8, containerName: []const u8, env: [][]const u8, private: bool) !void { + const ContainerStart = struct { + Id: []const u8, + Image: []const u8, + Env: [][]const u8, + Private: bool, + }; + const command = ContainerStart{ + .Id = "", + .Image = imageName, + .Env = env, + .Private = private, + }; + const path = try std.fmt.allocPrint(allocator, "{s}://{s}/containers/{s}/start", .{ auth.proxyScheme, auth.proxyHost, containerName }); + defer allocator.free(path); + var json = std.ArrayList(u8).init(allocator); + defer json.deinit(); + try std.json.stringify(command, .{}, json.writer()); + _ = try sendRequest(.POST, auth, path, json.items); +} + +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, ""); +} + +pub fn run() !void { + const command_name = std.os.getenv("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, + }; + const containerName = std.os.getenv("PLUGIN_CONTAINER") orelse return PluginError.NoContainerName; + + var envs = std.ArrayList([]const u8).init(allocator); + defer envs.deinit(); + for (std.os.environ) |env| { + if (std.mem.startsWith(u8, std.mem.span(env), "CODEFIRST_CLIENTDRONE_ENV_")) { + try envs.append(std.mem.span(env)["CODEFIRST_CLIENTDRONE_ENV_".len..]); + } + } + + 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 container = CodefirstContainer{ + .Id = "", + .Image = imageName, + .Admins = admins, + .Env = envs.items, + .Private = private, + }; + + if (overwrite) { + try delete(auth, containerName); + } + if (!try exists(auth, containerName)) { + try createImage(auth, imageName); + try create(auth, container, containerName); + try start(auth, imageName, containerName, envs.items, private); + } + }, + .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"); + try start(auth, imageName, containerName, envs.items, private); + }, + .delete => { + try delete(auth, containerName); + }, + else => unreachable, + } +} + +pub fn main() !void { + run() catch |err| { + switch (err) { + PluginError.InvalidCommand => std.log.err("Invalid command (possible values: list|logs|create|delete|start)", .{}), + PluginError.InvalidUser => std.log.err("Invalid user", .{}), + PluginError.NoContainerName => std.log.err("No container name", .{}), + PluginError.InvalidImage => std.log.err("Invalid image", .{}), + else => std.log.err("{}", .{err}), + } + std.os.exit(1); + }; +}