diff --git a/.gitignore b/.gitignore index 936332c..140f8cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1 @@ *.so - -_docs/ diff --git a/.woodpecker/.lint.yml b/.woodpecker/.lint.yml deleted file mode 100644 index 31df08c..0000000 --- a/.woodpecker/.lint.yml +++ /dev/null @@ -1,13 +0,0 @@ -branches: - exclude: [ main ] - -platform: 'linux/amd64' - -pipeline: - lint: - image: 'chewingbever/vlang:latest' - pull: true - commands: - - make lint - when: - event: [ push ] diff --git a/Makefile b/Makefile deleted file mode 100644 index b25f105..0000000 --- a/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# =====CONFIG===== -V_PATH ?= v -V := $(V_PATH) -showcc - -all: vdocker - - -# =====COMPILATION===== -.PHONY: vdocker -vdocker: - $(V) -g -shared . - - -# =====DOCS===== -.PHONY: api-docs -api-docs: - rm -rf '_docs' - v doc -f html -m -readme . - - -# =====OTHER===== -.PHONY: lint -lint: - $(V) fmt -verify . - -.PHONY: fmt -fmt: - $(V) fmt -w . diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index 52e22a2..0000000 --- a/ROADMAP.md +++ /dev/null @@ -1,143 +0,0 @@ -# Roadmap - -This file keeps track of which parts of the Docker Engine API v1.41 are -currently supported. Note that in-development support is not listed here, so as -long as the full functionality isn't supported, it won't be noted. - -This list was taking from the [API -reference](https://docs.docker.com/engine/api/v1.41/). - -* Containers - - [x] List containers - - [ ] Create a container - - [ ] Inspect a container - - [ ] List processes running inside a container - - [ ] Get container logs - - [ ] Get changes on a container’s filesystem - - [ ] Export a container - - [ ] Get container stats based on resource usage - - [ ] Resize a container TTY - - [ ] Start a container - - [ ] Stop a container - - [ ] Restart a container - - [ ] Kill a container - - [ ] Update a container - - [ ] Rename a container - - [ ] Pause a container - - [ ] Unpause a container - - [ ] Attach to a container - - [ ] Attach to a container via a websocket - - [ ] Wait for a container - - [ ] Remove a container - - [ ] Get information about files in a container - - [ ] Get an archive of a filesystem resource in a container - - [ ] Extract an archive of files or folders to a directory in a container - - [ ] Delete stopped containers - -* Images - - [ ] List images - - [ ] Build an image - - [ ] Delete builder cache - - [ ] Create an image - - [ ] Inspect an image - - [ ] Get the history of an image - - [ ] Push an image - - [ ] Tag an image - - [ ] Remove an image - - [ ] Search images - - [ ] Delete unused images - - [ ] Create a new image from a container - - [ ] Export an image - - [ ] Export several images - - [ ] Import images - -* Networks - - [ ] List networks - - [ ] Inspect a network - - [ ] Remove a network - - [ ] Create a network - - [ ] Connect a container to a network - - [ ] Disconnect a container from a network - - [ ] Delete unused networks - -* Volumes - - [x] List volumes - - [ ] Create a volume - - [ ] Inspect a volume - - [ ] Remove a volume - - [ ] Delete unused volumes - -* Exec - - [ ] Create an exec instance - - [ ] Start an exec instance - - [ ] Resize an exec instance - - [ ] Inspect an exec instance - -* Swarm - - [ ] Inspect swarm - - [ ] Initialize a new swarm - - [ ] Join an existing swarm - - [ ] Leave a swarm - - [ ] Update a swarm - - [ ] Get the unlock key - - [ ] Unlock a locked manager - -* Nodes - - [ ] List nodes - - [ ] Inspect a node - - [ ] Delete a node - - [ ] Update a node - -* Services - - [ ] List services - - [ ] Create a service - - [ ] Inspect a service - - [ ] Delete a service - - [ ] Update a service - - [ ] Get service logs - -* Tasks - - [ ] List tasks - - [ ] Inspect a task - - [ ] Get task logs - -* Secrets - - [ ] List secrets - - [ ] Create a secret - - [ ] Inspect a secret - - [ ] Delete a secret - - [ ] Update a secret - -* Configs - - [ ] List configs - - [ ] Create a config - - [ ] Inspect a config - - [ ] Delete a config - - [ ] Update a config - -* Plugins - - [ ] List plugins - - [ ] Get plugin privileges - - [ ] Install a plugin - - [ ] Inspect a plugin - - [ ] Remove a plugin - - [ ] Enable a plugin - - [ ] Disable a plugin - - [ ] Upgrade a plugin - - [ ] Create a plugin - - [ ] Push a plugin - - [ ] Configure a plugin - -* System - - [ ] Check auth configuration - - [ ] Get system information - - [ ] Get version - - [ ] Ping - - [ ] Monitor events - - [ ] Get data usage information - -* Distribution - - [ ] Get image information from the registry - -* Session - - [ ] Initialize interactive session diff --git a/containers.v b/containers.v index 0aee8f4..886c739 100644 --- a/containers.v +++ b/containers.v @@ -1,81 +1,12 @@ module vdocker import json +import net.urllib import time import net.http { Method } -pub struct Port { - ip string [json: IP] - private_port u16 [json: PrivatePort] - public_port u16 [json: PublicPort] - type_ string [json: Type] -} - -pub struct HostConfig { - network_mode string [json: NetworkMode] -} - -pub struct EndpointIpamConfig { - ipv4_address string [json: IPv4Address] - ipv6_address string [json: IPv6Address] - link_local_ips []string [json: LinkLocalIPs] -} - -pub struct EndpointSettings { - ipam_config EndpointIpamConfig [json: IPAMConfig] - links []string [json: Links] - aliases []string [json: Aliases] - network_id string [json: NetworkID] - endpoint_id string [json: EndpointID] - gateway string [json: Gateway] - ip_address string [json: IPAddress] - ip_prefix_len int [json: IPPrefixLen] - ipv6_gateway string [json: IPv6Gateway] - global_ipv6_address string [json: GlobalIPv6Address] - global_ipv6_prefix_len i64 [json: GlobalIPv6PrefixLen] - mac_address string [json: MacAddress] - driver_opts map[string]string [json: DriverOpts] -} - -pub struct NetworkSettings { - networks map[string]EndpointSettings [json: Networks] -} - -pub struct MountPoint { - type_ string [json: Type] - name string [json: Name] - source string [json: Source] - destination string [json: Destination] - driver string [json: Driver] - mode string [json: Mode] - rw bool [json: RW] - propagation string [json: Propagation] -} - -pub struct ContainerListItem { - id string [json: Id] - names []string [json: Names] - image string [json: Image] - image_id string [json: ImageID] - command string [json: Command] - created i64 [json: Created] - ports []Port [json: Ports] - size_rw i64 [json: SizeRw] - size_root_fs i64 [json: SizeRootFs] - labels map[string]string [json: Labels] - state string [json: State] - status string [json: Status] - host_config HostConfig [json: HostConfig] - network_settings NetworkSettings [json: NetworkSettings] - mounts []MountPoint [json: Mounts] -} - -pub fn (mut d DockerConn) container_list() ?[]ContainerListItem { - d.send_request(Method.get, '/containers/json')? - - data := d.read_json_response<[]ContainerListItem>()? - - return data +struct DockerError { + message string } pub struct NewContainer { @@ -93,8 +24,9 @@ pub: warnings []string [json: Warnings] } -pub fn (mut d DockerConn) container_create(c NewContainer) ?CreatedContainer { - d.send_request_with_json(Method.post, '/containers/create', c)? +// create_container creates a new container with the given config. +pub fn (mut d DockerConn) create_container(c NewContainer) ?CreatedContainer { + d.send_request_with_json(Method.post, urllib.parse('/v1.41/containers/create')?, c)? head, res := d.read_response()? if head.status_code != 201 { @@ -109,8 +41,8 @@ pub fn (mut d DockerConn) container_create(c NewContainer) ?CreatedContainer { } // start_container starts the container with the given id. -pub fn (mut d DockerConn) container_start(id string) ? { - d.send_request(Method.post, 'containers/$id/start')? +pub fn (mut d DockerConn) start_container(id string) ? { + d.send_request(Method.post, urllib.parse('/v1.41/containers/$id/start')?)? head, body := d.read_response()? if head.status_code != 204 { @@ -138,8 +70,9 @@ pub mut: end_time time.Time [skip] } -pub fn (mut d DockerConn) container_inspect(id string) ?ContainerInspect { - d.send_request(Method.get, 'containers/$id/json')? +// inspect_container returns detailed information for a given container. +pub fn (mut d DockerConn) inspect_container(id string) ?ContainerInspect { + d.send_request(Method.get, urllib.parse('/v1.41/containers/$id/json')?)? head, body := d.read_response()? if head.status_code != 200 { @@ -160,8 +93,9 @@ pub fn (mut d DockerConn) container_inspect(id string) ?ContainerInspect { return data } -pub fn (mut d DockerConn) container_remove(id string) ? { - d.send_request(Method.delete, 'containers/$id')? +// remove_container removes the container with the given id. +pub fn (mut d DockerConn) remove_container(id string) ? { + d.send_request(Method.delete, urllib.parse('/v1.41/containers/$id')?)? head, body := d.read_response()? if head.status_code != 204 { @@ -171,8 +105,10 @@ pub fn (mut d DockerConn) container_remove(id string) ? { } } -pub fn (mut d DockerConn) container_get_logs(id string) ?&StreamFormatReader { - d.send_request(Method.get, 'containers/$id/logs?stdout=true&stderr=true')? +// get_container_logs returns a reader object allowing access to the +// container's logs. +pub fn (mut d DockerConn) get_container_logs(id string) ?&StreamFormatReader { + d.send_request(Method.get, urllib.parse('/v1.41/containers/$id/logs?stdout=true&stderr=true')?)? head := d.read_response_head()? if head.status_code != 200 { diff --git a/docker.v b/docker.v index a171a2e..f4bcf23 100644 --- a/docker.v +++ b/docker.v @@ -13,8 +13,6 @@ const ( buf_len = 10 * 1024 http_separator = [u8(`\r`), `\n`, `\r`, `\n`] http_chunk_separator = [u8(`\r`), `\n`] - timestamp_attr = 'timestamp' - api_version = 'v1.41' ) pub struct DockerConn { @@ -41,8 +39,7 @@ pub fn (mut d DockerConn) close() ? { } // send_request sends an HTTP request without body. -fn (mut d DockerConn) send_request(method http.Method, url_str string) ? { - url := urllib.parse('/$vdocker.api_version$url_str')? +pub fn (mut d DockerConn) send_request(method http.Method, url urllib.URL) ? { req := '$method $url.request_uri() HTTP/1.1\nHost: localhost\n\n' d.socket.write_string(req)? @@ -52,8 +49,7 @@ fn (mut d DockerConn) send_request(method http.Method, url_str string) ? { } // send_request_with_body sends an HTTP request with the given body. -fn (mut d DockerConn) send_request_with_body(method http.Method, url_str string, content_type string, body string) ? { - url := urllib.parse('/$vdocker.api_version$url_str')? +pub fn (mut d DockerConn) send_request_with_body(method http.Method, url urllib.URL, content_type string, body string) ? { req := '$method $url.request_uri() HTTP/1.1\nHost: localhost\nContent-Type: $content_type\nContent-Length: $body.len\n\n$body\n\n' d.socket.write_string(req)? @@ -64,17 +60,17 @@ fn (mut d DockerConn) send_request_with_body(method http.Method, url_str string, // send_request_with_json is a convenience wrapper around // send_request_with_body that encodes the input as JSON. -fn (mut d DockerConn) send_request_with_json(method http.Method, url_str string, data &T) ? { +pub fn (mut d DockerConn) send_request_with_json(method http.Method, url urllib.URL, data &T) ? { body := json.encode(data) - return d.send_request_with_body(method, url_str, 'application/json', body) + return d.send_request_with_body(method, url, 'application/json', body) } // read_response_head consumes the socket's contents until it encounters // '\r\n\r\n', after which it parses the response as an HTTP response. // Importantly, this function never consumes the reader past the HTTP // separator, so the body can be read fully later on. -fn (mut d DockerConn) read_response_head() ?http.Response { +pub fn (mut d DockerConn) read_response_head() ?http.Response { mut res := []u8{} util.read_until_separator(mut d.reader, mut res, vdocker.http_separator)? @@ -84,7 +80,7 @@ fn (mut d DockerConn) read_response_head() ?http.Response { // read_response_body reads `length` bytes from the stream. It can be used when // the response encoding isn't chunked to fully read it. -fn (mut d DockerConn) read_response_body(length int) ?string { +pub fn (mut d DockerConn) read_response_body(length int) ?string { if length == 0 { return '' } @@ -105,7 +101,7 @@ fn (mut d DockerConn) read_response_body(length int) ?string { // read_response is a convenience function which always consumes the entire // response & returns it. It should only be used when we're certain that the // result isn't too large. -fn (mut d DockerConn) read_response() ?(http.Response, string) { +pub fn (mut d DockerConn) read_response() ?(http.Response, string) { head := d.read_response_head()? if head.header.get(http.CommonHeader.transfer_encoding) or { '' } == 'chunked' { @@ -123,29 +119,9 @@ fn (mut d DockerConn) read_response() ?(http.Response, string) { return head, res } -fn (mut d DockerConn) read_json_response() ?T { - head, body := d.read_response()? - - if head.status_code < 200 || head.status_code > 300 { - data := json.decode(DockerError, body)? - - return docker_error(head.status_code, data.message) - } - - mut data := json.decode(T, body)? - - //$for field in T.fields { - //$if field.typ is time.Time { - // data.$(field.name) = time.parse_rfc3339(data.$(field.name + '_str'))? - //} - //} - - return data -} - // get_chunked_response_reader returns a ChunkedResponseReader using the socket // as reader. -fn (mut d DockerConn) get_chunked_response_reader() &ChunkedResponseReader { +pub fn (mut d DockerConn) get_chunked_response_reader() &ChunkedResponseReader { r := new_chunked_response_reader(d.reader) return r @@ -153,7 +129,7 @@ fn (mut d DockerConn) get_chunked_response_reader() &ChunkedResponseReader { // get_stream_format_reader returns a StreamFormatReader using the socket as // reader. -fn (mut d DockerConn) get_stream_format_reader() &StreamFormatReader { +pub fn (mut d DockerConn) get_stream_format_reader() &StreamFormatReader { r := new_chunked_response_reader(d.reader) r2 := new_stream_format_reader(r) diff --git a/errors.v b/errors.v deleted file mode 100644 index e5da55a..0000000 --- a/errors.v +++ /dev/null @@ -1,21 +0,0 @@ -module vdocker - -struct DockerError { - status int [skip] - message string -} - -fn (err DockerError) code() int { - return err.status -} - -fn (err DockerError) msg() string { - return err.message -} - -fn docker_error(status int, message string) IError { - return IError(DockerError{ - status: status - message: message - }) -} diff --git a/images.v b/images.v index aeaa53f..945951d 100644 --- a/images.v +++ b/images.v @@ -1,6 +1,7 @@ module vdocker import net.http { Method } +import net.urllib import json struct Image { @@ -10,7 +11,7 @@ pub: // pull_image pulls the given image:tag. pub fn (mut d DockerConn) pull_image(image string, tag string) ? { - d.send_request(Method.post, 'images/create?fromImage=$image&tag=$tag')? + d.send_request(Method.post, urllib.parse('/v1.41/images/create?fromImage=$image&tag=$tag')?)? head := d.read_response_head()? if head.status_code != 200 { @@ -33,7 +34,7 @@ pub fn (mut d DockerConn) pull_image(image string, tag string) ? { // create_image_from_container creates a new image from a container. pub fn (mut d DockerConn) create_image_from_container(id string, repo string, tag string) ?Image { - d.send_request(Method.post, 'commit?container=$id&repo=$repo&tag=$tag')? + d.send_request(Method.post, urllib.parse('/v1.41/commit?container=$id&repo=$repo&tag=$tag')?)? head, body := d.read_response()? if head.status_code != 201 { @@ -49,7 +50,7 @@ pub fn (mut d DockerConn) create_image_from_container(id string, repo string, ta // remove_image removes the image with the given id. pub fn (mut d DockerConn) remove_image(id string) ? { - d.send_request(Method.delete, 'images/$id')? + d.send_request(Method.delete, urllib.parse('/v1.41/images/$id')?)? head, body := d.read_response()? if head.status_code != 200 { diff --git a/volumes.v b/volumes.v deleted file mode 100644 index 9642198..0000000 --- a/volumes.v +++ /dev/null @@ -1,48 +0,0 @@ -module vdocker - -import net.http { Method } -import time - -struct UsageData { - size int [json: Size] - ref_count int [json: RefCount] -} - -struct Volume { - created_at_str string [json: CreatedAt] -pub mut: - created_at time.Time [skip] - name string [json: Name] - driver string [json: Driver] - mountpoint string [json: Mountpoint] - status map[string]string [json: Status] - labels map[string]string [json: Labels] - scope string [json: Scope] - options map[string]string [json: Options] - usage_data UsageData [json: UsageData] -} - -[params] -pub struct VolumeListFilter { - dangling bool - driver string - labels []string - name string -} - -struct VolumeListResponse { - volumes []Volume [json: Volumes] - warnings []string [json: Warnings] -} - -pub fn (mut d DockerConn) volume_list() ?VolumeListResponse { - d.send_request(Method.get, '/volumes')? - - mut data := d.read_json_response()? - - for mut vol in data.volumes { - vol.created_at = time.parse_rfc3339(vol.created_at_str)? - } - - return data -}