diff --git a/src/build/build.v b/src/build/build.v index 98c56f5..0de91a6 100644 --- a/src/build/build.v +++ b/src/build/build.v @@ -6,6 +6,8 @@ import time import os import db import client +import strings +import util const container_build_dir = '/build' @@ -19,6 +21,10 @@ const build_image_repo = 'vieter-build' pub fn create_build_image(base_image string) ?string { mut dd := docker.new_conn()? + defer { + dd.close() or {} + } + commands := [ // Update repos & install required packages 'pacman -Syu --needed --noconfirm base-devel git' @@ -48,7 +54,7 @@ pub fn create_build_image(base_image string) ?string { image_tag := if image_parts.len > 1 { image_parts[1] } else { 'latest' } // We pull the provided image - docker.pull_image(image_name, image_tag)? + dd.pull_image(image_name, image_tag)? id := dd.create_container(c)?.id // id := docker.create_container(c)? @@ -70,7 +76,7 @@ pub fn create_build_image(base_image string) ?string { // TODO also add the base image's name into the image name to prevent // conflicts. tag := time.sys_mono_now().str() - image := docker.create_image_from_container(id, 'vieter-build', tag)? + image := dd.create_image_from_container(id, 'vieter-build', tag)? dd.remove_container(id)? return image.id @@ -88,6 +94,12 @@ pub: // provided GitRepo. The base image ID should be of an image previously created // by create_build_image. It returns the logs of the container. pub fn build_repo(address string, api_key string, base_image_id string, repo &db.GitRepo) ?BuildResult { + mut dd := docker.new_conn()? + + defer { + dd.close() or {} + } + build_arch := os.uname().machine // TODO what to do with PKGBUILDs that build multiple packages? @@ -115,27 +127,31 @@ pub fn build_repo(address string, api_key string, base_image_id string, repo &db user: 'builder:builder' } - id := docker.create_container(c)? - docker.start_container(id)? + id := dd.create_container(c)?.id + dd.start_container(id)? - mut data := docker.inspect_container(id)? + mut data := dd.inspect_container(id)? // This loop waits until the container has stopped, so we can remove it after for data.state.running { time.sleep(1 * time.second) - data = docker.inspect_container(id)? + data = dd.inspect_container(id)? } - logs := docker.get_container_logs(id)? + mut logs_stream := dd.get_container_logs(id)? - docker.remove_container(id)? + // Read in the entire stream + mut logs_builder := strings.new_builder(10 * 1024) + util.reader_to_writer(mut logs_stream, mut logs_builder)? + + dd.remove_container(id)? return BuildResult{ start_time: data.state.start_time end_time: data.state.end_time exit_code: data.state.exit_code - logs: logs + logs: logs_builder.str() } } @@ -153,7 +169,14 @@ fn build(conf Config, repo_id int) ? { res := build_repo(conf.address, conf.api_key, image_id, repo)? println('Removing build image...') - docker.remove_image(image_id)? + + mut dd := docker.new_conn()? + + defer { + dd.close() or {} + } + + dd.remove_image(image_id)? println('Uploading logs to Vieter...') c.add_build_log(repo.id, res.start_time, res.end_time, build_arch, res.exit_code, diff --git a/src/docker/images.v b/src/docker/images.v index ab735f2..51620af 100644 --- a/src/docker/images.v +++ b/src/docker/images.v @@ -22,14 +22,13 @@ pub fn (mut d DockerDaemon) pull_image(image string, tag string) ? { return error(data.message) } + // Keep reading the body until the pull has completed mut body := d.get_chunked_response_reader() mut buf := []u8{len: 1024} for { - c := body.read(mut buf) or { break } - - print(buf[..c].bytestr()) + body.read(mut buf) or { break } } } @@ -38,6 +37,22 @@ pub fn pull_image(image string, tag string) ?http.Response { return request('POST', urllib.parse('/v1.41/images/create?fromImage=$image&tag=$tag')?) } +// create_image_from_container creates a new image from a container. +pub fn (mut d DockerDaemon) create_image_from_container(id string, repo string, tag string) ?Image { + d.send_request('POST', urllib.parse('/v1.41/commit?container=$id&repo=$repo&tag=$tag')?)? + head, body := d.read_response()? + + if head.status_code != 201 { + data := json.decode(DockerError, body)? + + return error(data.message) + } + + data := json.decode(Image, body)? + + return data +} + // create_image_from_container creates a new image from a container with the // given repo & tag, given the container's ID. pub fn create_image_from_container(id string, repo string, tag string) ?Image { @@ -56,3 +71,15 @@ pub fn remove_image(id string) ?bool { return res.status_code == 200 } + +// remove_image removes the image with the given id. +pub fn (mut d DockerDaemon) remove_image(id string) ? { + d.send_request('DELETE', urllib.parse('/v1.41/images/$id')?)? + head, body := d.read_response()? + + if head.status_code != 200 { + data := json.decode(DockerError, body)? + + return error(data.message) + } +} diff --git a/src/docker/socket.v b/src/docker/socket.v index dfa7ea7..2ee9bff 100644 --- a/src/docker/socket.v +++ b/src/docker/socket.v @@ -33,6 +33,11 @@ pub fn new_conn() ?&DockerDaemon { return d } +// close closes the underlying socket connection. +pub fn (mut d DockerDaemon) close() ? { + d.socket.close()? +} + // send_request sends an HTTP request without body. pub fn (mut d DockerDaemon) send_request(method string, url urllib.URL) ? { req := '$method $url.request_uri() HTTP/1.1\nHost: localhost\n\n' @@ -124,7 +129,7 @@ pub fn (mut d DockerDaemon) read_response() ?(http.Response, string) { mut builder := strings.new_builder(1024) mut body := d.get_chunked_response_reader() - util.reader_to_writer(mut body, mut builder) ? + util.reader_to_writer(mut body, mut builder)? return head, builder.str() } diff --git a/src/main.v b/src/main.v index 6b1e7bc..db6d5ef 100644 --- a/src/main.v +++ b/src/main.v @@ -7,7 +7,6 @@ import build import console.git import console.logs import cron -import docker fn main() { mut app := cli.Command{ diff --git a/src/util/util.v b/src/util/util.v index 7aabc1b..324fb3d 100644 --- a/src/util/util.v +++ b/src/util/util.v @@ -28,7 +28,7 @@ pub fn reader_to_writer(mut reader io.Reader, mut writer io.Writer) ? { mut buf := []u8{len: 10 * 1024} for { - c := reader.read(mut buf) or { break } + reader.read(mut buf) or { break } writer.write(buf) or { break } }