refactor: removed custom error type; greatly increased internal api
							parent
							
								
									9aebb3bedc
								
							
						
					
					
						commit
						c8191a19e1
					
				
							
								
								
									
										6
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										6
									
								
								Makefile
								
								
								
								
							|  | @ -1,6 +1,6 @@ | |||
| # =====CONFIG=====
 | ||||
| V_PATH ?= v | ||||
| V := $(V_PATH) -showcc | ||||
| V := $(V_PATH) -showcc -d use_openssl | ||||
| 
 | ||||
| all: vdocker | ||||
| 
 | ||||
|  | @ -10,6 +10,10 @@ all: vdocker | |||
| vdocker: | ||||
| 	$(V) -g -shared . | ||||
| 
 | ||||
| .PHONY: c | ||||
| c: | ||||
| 	$(V) -o docker.c . | ||||
| 
 | ||||
| 
 | ||||
| # =====DOCS=====
 | ||||
| .PHONY: api-docs | ||||
|  |  | |||
							
								
								
									
										48
									
								
								containers.v
								
								
								
								
							
							
						
						
									
										48
									
								
								containers.v
								
								
								
								
							|  | @ -1,6 +1,5 @@ | |||
| module docker | ||||
| 
 | ||||
| import json | ||||
| import time | ||||
| import net.http { Method } | ||||
| import types { ContainerListItem } | ||||
|  | @ -38,29 +37,14 @@ pub: | |||
| 
 | ||||
| pub fn (mut d DockerConn) container_create(c NewContainer) !CreatedContainer { | ||||
| 	d.send_request_with_json(Method.post, '/containers/create', c)! | ||||
| 	head, res := d.read_response()! | ||||
| 
 | ||||
| 	if head.status_code != 201 { | ||||
| 		data := json.decode(DockerError, res)! | ||||
| 
 | ||||
| 		return error(data.message) | ||||
| 	} | ||||
| 
 | ||||
| 	data := json.decode(CreatedContainer, res)! | ||||
| 
 | ||||
| 	return data | ||||
| 	return d.read_json_response<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')! | ||||
| 	head, body := d.read_response()! | ||||
| 
 | ||||
| 	if head.status_code != 204 { | ||||
| 		data := json.decode(DockerError, body)! | ||||
| 
 | ||||
| 		return error(data.message) | ||||
| 	} | ||||
| 	d.read_response()! | ||||
| } | ||||
| 
 | ||||
| struct ContainerInspect { | ||||
|  | @ -83,15 +67,8 @@ pub mut: | |||
| 
 | ||||
| pub fn (mut d DockerConn) container_inspect(id string) !ContainerInspect { | ||||
| 	d.send_request(Method.get, '/containers/$id/json')! | ||||
| 	head, body := d.read_response()! | ||||
| 
 | ||||
| 	if head.status_code != 200 { | ||||
| 		data := json.decode(DockerError, body)! | ||||
| 
 | ||||
| 		return error(data.message) | ||||
| 	} | ||||
| 
 | ||||
| 	mut data := json.decode(ContainerInspect, body)! | ||||
| 	mut data := d.read_json_response<ContainerInspect>()! | ||||
| 
 | ||||
| 	// The Docker engine API *should* always return UTC time. | ||||
| 	data.state.start_time = time.parse_rfc3339(data.state.start_time_str)! | ||||
|  | @ -105,26 +82,13 @@ pub fn (mut d DockerConn) container_inspect(id string) !ContainerInspect { | |||
| 
 | ||||
| pub fn (mut d DockerConn) container_remove(id string) ! { | ||||
| 	d.send_request(Method.delete, '/containers/$id')! | ||||
| 	head, body := d.read_response()! | ||||
| 
 | ||||
| 	if head.status_code != 204 { | ||||
| 		data := json.decode(DockerError, body)! | ||||
| 
 | ||||
| 		return error(data.message) | ||||
| 	} | ||||
| 	d.read_response()! | ||||
| } | ||||
| 
 | ||||
| pub fn (mut d DockerConn) container_get_logs(id string) !&StreamFormatReader { | ||||
| 	d.send_request(Method.get, '/containers/$id/logs?stdout=true&stderr=true')! | ||||
| 	head := d.read_response_head()! | ||||
| 
 | ||||
| 	if head.status_code != 200 { | ||||
| 		content_length := head.header.get(http.CommonHeader.content_length)!.int() | ||||
| 		body := d.read_response_body(content_length)! | ||||
| 		data := json.decode(DockerError, body)! | ||||
| 
 | ||||
| 		return error(data.message) | ||||
| 	} | ||||
| 	d.read_response_head()! | ||||
| 	d.check_error()! | ||||
| 
 | ||||
| 	return d.get_stream_format_reader() | ||||
| } | ||||
|  |  | |||
							
								
								
									
										77
									
								
								docker.v
								
								
								
								
							
							
						
						
									
										77
									
								
								docker.v
								
								
								
								
							|  | @ -27,6 +27,7 @@ mut: | |||
| 	url          string | ||||
| 	params       map[string]string | ||||
| 	content_type string | ||||
| 	head         http.Response | ||||
| 	body         string | ||||
| } | ||||
| 
 | ||||
|  | @ -81,79 +82,61 @@ fn (mut d DockerConn) send_request_with_json<T>(method http.Method, url_str stri | |||
| // '\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 { | ||||
| fn (mut d DockerConn) read_response_head() ! { | ||||
| 	mut res := []u8{} | ||||
| 
 | ||||
| 	util.read_until_separator(mut d.reader, mut res, docker.http_separator)! | ||||
| 
 | ||||
| 	return http.parse_response(res.bytestr()) | ||||
| 	d.head = http.parse_response(res.bytestr())! | ||||
| } | ||||
| 
 | ||||
| // 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 { | ||||
| 	if length == 0 { | ||||
| 		return '' | ||||
| fn (mut d DockerConn) read_response_body() ! { | ||||
| 	content_length := d.head.header.get(.content_length)!.int() | ||||
| 
 | ||||
| 	if content_length == 0 { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	mut buf := []u8{len: docker.buf_len} | ||||
| 	mut c := 0 | ||||
| 	mut builder := strings.new_builder(docker.buf_len) | ||||
| 
 | ||||
| 	for builder.len < length { | ||||
| 	for builder.len < content_length { | ||||
| 		c = d.reader.read(mut buf) or { break } | ||||
| 
 | ||||
| 		builder.write(buf[..c])! | ||||
| 	} | ||||
| 
 | ||||
| 	return builder.str() | ||||
| 	d.body = builder.str() | ||||
| } | ||||
| 
 | ||||
| // 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) { | ||||
| 	head := d.read_response_head()! | ||||
| 
 | ||||
| 	if head.status().is_error() { | ||||
| 		content_length := head.header.get(.content_length)!.int() | ||||
| 		body := d.read_response_body(content_length)! | ||||
| 		mut err := json.decode(DockerError, body)! | ||||
| 		err.status = head.status_code | ||||
| 
 | ||||
| 		return err | ||||
| 	} | ||||
| fn (mut d DockerConn) read_response() ! { | ||||
| 	d.read_response_head()! | ||||
| 	d.check_error()! | ||||
| 
 | ||||
| 	// 204 means "No Content", so we can assume nothing follows after this | ||||
| 	if head.status() == .no_content { | ||||
| 		return head, '' | ||||
| 	if d.head.status() == .no_content { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if head.header.get(http.CommonHeader.transfer_encoding) or { '' } == 'chunked' { | ||||
| 	if d.head.header.get(http.CommonHeader.transfer_encoding) or { '' } == 'chunked' { | ||||
| 		mut builder := strings.new_builder(1024) | ||||
| 		mut body := d.get_chunked_response_reader() | ||||
| 
 | ||||
| 		util.reader_to_writer(mut body, mut builder)! | ||||
| 
 | ||||
| 		return head, builder.str() | ||||
| 		d.body = builder.str() | ||||
| 	} else { | ||||
| 		d.read_response_body()! | ||||
| 	} | ||||
| 
 | ||||
| 	content_length := head.header.get(http.CommonHeader.content_length)!.int() | ||||
| 	body := d.read_response_body(content_length)! | ||||
| 
 | ||||
| 	return head, body | ||||
| } | ||||
| 
 | ||||
| fn (mut d DockerConn) read_json_response<T>() !T { | ||||
| 	head, body := d.read_response()! | ||||
| 	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)! | ||||
| 	mut data := json.decode(T, d.body)! | ||||
| 
 | ||||
| 	//$for field in T.fields { | ||||
| 	//$if field.typ is time.Time { | ||||
|  | @ -180,3 +163,21 @@ fn (mut d DockerConn) get_stream_format_reader() &StreamFormatReader { | |||
| 
 | ||||
| 	return r2 | ||||
| } | ||||
| 
 | ||||
| struct DockerError { | ||||
| pub: | ||||
| 	message string | ||||
| } | ||||
| 
 | ||||
| // check_error should be called after read_response_head. If the status code of | ||||
| // the response is an error, the body is consumed and the Docker HTTP error is | ||||
| // returned as a V error. If the status isn't the error, this function is a | ||||
| // no-op. | ||||
| fn (mut d DockerConn) check_error() ! { | ||||
| 	if d.head.status().is_error() { | ||||
| 		d.read_response_body()! | ||||
| 		d_err := json.decode(DockerError, d.body)! | ||||
| 
 | ||||
| 		return error_with_code('$d.head.status(): $d_err.message', d.head.status_code) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										22
									
								
								errors.v
								
								
								
								
							
							
						
						
									
										22
									
								
								errors.v
								
								
								
								
							|  | @ -1,22 +0,0 @@ | |||
| module docker | ||||
| 
 | ||||
| struct DockerError { | ||||
| pub mut: | ||||
| 	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) DockerError { | ||||
| 	return DockerError{ | ||||
| 		status: status | ||||
| 		message: message | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										20
									
								
								images.v
								
								
								
								
							
							
						
						
									
										20
									
								
								images.v
								
								
								
								
							|  | @ -2,13 +2,11 @@ module docker | |||
| 
 | ||||
| import net.http { Method } | ||||
| import types { Image } | ||||
| import json | ||||
| 
 | ||||
| pub fn (mut d DockerConn) image_inspect(image string) !Image { | ||||
| 	d.send_request(.get, '/images/$image/json')! | ||||
| 	_, body := d.read_response()! | ||||
| 
 | ||||
| 	data := json.decode(Image, body)! | ||||
| 	data := d.read_json_response<Image>()! | ||||
| 
 | ||||
| 	return data | ||||
| } | ||||
|  | @ -16,16 +14,8 @@ pub fn (mut d DockerConn) image_inspect(image string) !Image { | |||
| // 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')! | ||||
| 	head := d.read_response_head()! | ||||
| 
 | ||||
| 	if head.status().is_error() { | ||||
| 		content_length := head.header.get(.content_length)!.int() | ||||
| 		body := d.read_response_body(content_length)! | ||||
| 		mut err := json.decode(DockerError, body)! | ||||
| 		err.status = head.status_code | ||||
| 
 | ||||
| 		return err | ||||
| 	} | ||||
| 	d.read_response_head()! | ||||
| 	d.check_error()! | ||||
| 
 | ||||
| 	// Keep reading the body until the pull has completed | ||||
| 	mut body := d.get_chunked_response_reader() | ||||
|  | @ -40,9 +30,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(.post, '/commit?container=$id&repo=$repo&tag=$tag')! | ||||
| 	_, body := d.read_response()! | ||||
| 
 | ||||
| 	data := json.decode(Image, body)! | ||||
| 	data := d.read_json_response<Image>()! | ||||
| 
 | ||||
| 	return data | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue