diff --git a/engine/build.gradle.kts b/engine/build.gradle.kts index b98ab2d4..cc219e7a 100644 --- a/engine/build.gradle.kts +++ b/engine/build.gradle.kts @@ -28,16 +28,6 @@ dependencies { prefer(libs.versions.moshi.get()) } } - listOf( - "com.squareup.okhttp3:okhttp" - ).forEach { - implementation(it) { - version { - strictly(libs.versions.okhttpVersionrange.get()) - prefer(libs.versions.okhttp.get()) - } - } - } listOf( libs.bundles.okio ).forEach { @@ -66,8 +56,6 @@ dependencies { testImplementation("ch.qos.logback:logback-classic:${libs.versions.logbackVersionrange.get()}!!${libs.versions.logback.get()}") implementation(libs.okio) - implementation(libs.okhttp) - testImplementation(libs.okhttpMockwebserver) implementation("org.apache.commons:commons-compress:1.28.0") testImplementation("org.apache.commons:commons-lang3:3.19.0") diff --git a/engine/src/main/java/de/gesellix/docker/engine/EngineClient.java b/engine/src/main/java/de/gesellix/docker/engine/EngineClient.java deleted file mode 100644 index 139cb1d7..00000000 --- a/engine/src/main/java/de/gesellix/docker/engine/EngineClient.java +++ /dev/null @@ -1,29 +0,0 @@ -package de.gesellix.docker.engine; - -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; - -import java.util.Map; - -/** - * Will be replaced with the implementation from github.com/docker-client/docker-remote-api-client. - * - * @deprecated - */ -@Deprecated -public interface EngineClient { - - EngineResponse request(EngineRequest requestConfig); - - EngineResponse head(Map requestConfig); - - EngineResponse get(Map requestConfig); - - EngineResponse put(Map requestConfig); - - EngineResponse post(Map requestConfig); - - EngineResponse delete(Map requestConfig); - - WebSocket webSocket(Map requestConfig, WebSocketListener listener); -} diff --git a/engine/src/main/java/de/gesellix/docker/engine/EngineRequest.java b/engine/src/main/java/de/gesellix/docker/engine/EngineRequest.java deleted file mode 100644 index a9af2b33..00000000 --- a/engine/src/main/java/de/gesellix/docker/engine/EngineRequest.java +++ /dev/null @@ -1,125 +0,0 @@ -package de.gesellix.docker.engine; - -import java.io.OutputStream; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -public class EngineRequest { - - private RequestMethod method; - private String path; - private Map headers = new HashMap<>(); - private Map> query = new HashMap<>(); - - private String contentType = null; - private Object body = null; - - private int timeout = 0; - - private boolean async = false; - private OutputStream stdout; - - private String apiVersion = null; - - public EngineRequest(RequestMethod method, String path) { - this.method = method; - this.path = path; - } - - public RequestMethod getMethod() { - return method; - } - - public void setMethod(RequestMethod method) { - this.method = method; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - public Map getHeaders() { - return headers; - } - - public void setHeaders(Map headers) { - this.headers = headers; - } - - public Map> getQuery() { - return query; - } - - public void setQuery(Map> query) { - this.query = query; - } - - public String getContentType() { - return contentType; - } - - public void setContentType(String contentType) { - this.contentType = contentType; - } - - public Object getBody() { - return body; - } - - public void setBody(Object body) { - this.body = body; - } - - public int getTimeout() { - return timeout; - } - - public void setTimeout(int timeout) { - this.timeout = timeout; - } - - public boolean isAsync() { - return async; - } - - public void setAsync(boolean async) { - this.async = async; - } - - public OutputStream getStdout() { - return stdout; - } - - public void setStdout(OutputStream stdout) { - this.stdout = stdout; - } - - public String getApiVersion() { - return apiVersion; - } - - public void setApiVersion(String apiVersion) { - this.apiVersion = apiVersion; - } - - @Override - public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } - EngineRequest that = (EngineRequest) o; - return timeout == that.timeout && async == that.async && method == that.method && Objects.equals(path, that.path) && Objects.equals(headers, that.headers) && - Objects.equals(query, that.query) && Objects.equals(contentType, that.contentType) && Objects.equals(body, that.body) && - Objects.equals(stdout, that.stdout) && Objects.equals(apiVersion, that.apiVersion); - } - - @Override - public int hashCode() { - return Objects.hash(method, path, headers, query, contentType, body, timeout, async, stdout, apiVersion); - } -} diff --git a/engine/src/main/java/de/gesellix/docker/engine/EngineResponse.java b/engine/src/main/java/de/gesellix/docker/engine/EngineResponse.java deleted file mode 100644 index a5e23dd2..00000000 --- a/engine/src/main/java/de/gesellix/docker/engine/EngineResponse.java +++ /dev/null @@ -1,94 +0,0 @@ -package de.gesellix.docker.engine; - -import java.io.InputStream; -import java.util.concurrent.Future; - -public class EngineResponse { - - private EngineResponseStatus status = new EngineResponseStatus(); - private Object headers; - private String contentType; - private String mimeType; - private String contentLength; - private InputStream stream; - private R content; - private Future taskFuture; - - public EngineResponseStatus getStatus() { - return status; - } - - public void setStatus(EngineResponseStatus status) { - this.status = status; - } - - public Object getHeaders() { - return headers; - } - - public void setHeaders(Object headers) { - this.headers = headers; - } - - public String getContentType() { - return contentType; - } - - public void setContentType(String contentType) { - this.contentType = contentType; - } - - public String getMimeType() { - return mimeType; - } - - public void setMimeType(String mimeType) { - this.mimeType = mimeType; - } - - public String getContentLength() { - return contentLength; - } - - public void setContentLength(String contentLength) { - this.contentLength = contentLength; - } - - public InputStream getStream() { - return stream; - } - - public void setStream(InputStream stream) { - this.stream = stream; - } - - public R getContent() { - return content; - } - - public void setContent(R content) { - this.content = content; - } - - public Future getTaskFuture() { - return taskFuture; - } - - public void setTaskFuture(Future taskFuture) { - this.taskFuture = taskFuture; - } - - @Override - public String toString() { - return "EngineResponse{" + - "status=" + status + - ", headers=" + headers + - ", contentType='" + contentType + '\'' + - ", mimeType='" + mimeType + '\'' + - ", contentLength='" + contentLength + '\'' + - ", stream=" + stream + - ", content=" + content + - ", taskFuture=" + taskFuture + - '}'; - } -} diff --git a/engine/src/main/java/de/gesellix/docker/engine/EngineResponseStatus.java b/engine/src/main/java/de/gesellix/docker/engine/EngineResponseStatus.java deleted file mode 100644 index 916f19d2..00000000 --- a/engine/src/main/java/de/gesellix/docker/engine/EngineResponseStatus.java +++ /dev/null @@ -1,60 +0,0 @@ -package de.gesellix.docker.engine; - -import java.util.Objects; - -public class EngineResponseStatus { - - private String text; - private int code; - private boolean success; - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } - - public int getCode() { - return code; - } - - public void setCode(int code) { - this.code = code; - } - - public boolean getSuccess() { - return success; - } - - public boolean isSuccess() { - return success; - } - - public void setSuccess(boolean success) { - this.success = success; - } - - @Override - public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } - EngineResponseStatus that = (EngineResponseStatus) o; - return code == that.code && success == that.success && Objects.equals(text, that.text); - } - - @Override - public int hashCode() { - return Objects.hash(text, code, success); - } - - @Override - public String toString() { - return "EngineResponseStatus{" + - "text='" + text + '\'' + - ", code=" + code + - ", success=" + success + - '}'; - } -} diff --git a/engine/src/main/java/de/gesellix/docker/engine/OkDockerClient.java b/engine/src/main/java/de/gesellix/docker/engine/OkDockerClient.java deleted file mode 100644 index e66995dc..00000000 --- a/engine/src/main/java/de/gesellix/docker/engine/OkDockerClient.java +++ /dev/null @@ -1,596 +0,0 @@ -package de.gesellix.docker.engine; - -import com.squareup.moshi.Moshi; -import de.gesellix.docker.client.filesocket.FileSocketFactory; -import de.gesellix.docker.client.filesocket.HostnameEncoder; -import de.gesellix.docker.client.filesocket.NamedPipeSocketFactory; -import de.gesellix.docker.client.filesocket.UnixSocketFactory; -import de.gesellix.docker.client.filesocket.UnixSocketFactorySupport; -import de.gesellix.docker.json.CustomObjectAdapterFactory; -import de.gesellix.docker.rawstream.RawInputStream; -import de.gesellix.docker.response.JsonContentHandler; -import de.gesellix.docker.ssl.DockerSslSocket; -import de.gesellix.docker.ssl.SslSocketConfigFactory; -import de.gesellix.util.IOUtils; -import okhttp3.CacheControl; -import okhttp3.Call; -import okhttp3.Headers; -import okhttp3.HttpUrl; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; -import okhttp3.internal.http.HttpMethod; -import okio.BufferedSource; -import okio.Okio; -import okio.Source; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.net.Proxy; -import java.net.URLEncoder; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import static de.gesellix.docker.client.filesocket.FileSocket.SOCKET_MARKER; -import static de.gesellix.docker.engine.RequestMethod.DELETE; -import static de.gesellix.docker.engine.RequestMethod.GET; -import static de.gesellix.docker.engine.RequestMethod.HEAD; -import static de.gesellix.docker.engine.RequestMethod.POST; -import static de.gesellix.docker.engine.RequestMethod.PUT; -import static java.util.concurrent.TimeUnit.MILLISECONDS; - -/** - * Will be replaced with the implementation from github.com/docker-client/docker-remote-api-client. - * - * @deprecated - */ -@Deprecated -public class OkDockerClient implements EngineClient { - - private static final Logger log = LoggerFactory.getLogger(OkDockerClient.class); - - private final Map socketFactories = new LinkedHashMap<>(); - - private final DockerClientConfig dockerClientConfig; - private Proxy proxy; - - private final Moshi moshi; - - public OkDockerClient() { - this(new DockerClientConfig()); - } - - public OkDockerClient(String dockerHost) { - this(new DockerClientConfig(dockerHost)); - } - - public OkDockerClient(DockerClientConfig dockerClientConfig) { - this(dockerClientConfig, Proxy.NO_PROXY); - } - - public OkDockerClient(DockerClientConfig dockerClientConfig, Proxy proxy) { - if (new UnixSocketFactorySupport().isSupported()) { - socketFactories.put("unix", new UnixSocketFactory()); - } - socketFactories.put("npipe", new NamedPipeSocketFactory()); - socketFactories.put("https", new SslSocketConfigFactory()); - - this.dockerClientConfig = dockerClientConfig; - this.proxy = proxy; - - this.moshi = new Moshi.Builder() - .add(new CustomObjectAdapterFactory()) - .build(); - } - - @Override - public EngineResponse head(Map requestConfig) { - EngineRequest engineRequest = ensureValidRequestConfig(requestConfig, HEAD); - return request(engineRequest); - } - - @Override - public EngineResponse get(Map requestConfig) { - EngineRequest engineRequest = ensureValidRequestConfig(requestConfig, GET); - return request(engineRequest); - } - - @Override - public EngineResponse put(Map requestConfig) { - EngineRequest engineRequest = ensureValidRequestConfig(requestConfig, PUT); - return request(engineRequest); - } - - @Override - public EngineResponse post(Map requestConfig) { - EngineRequest engineRequest = ensureValidRequestConfig(requestConfig, POST); - return request(engineRequest); - } - - @Override - public EngineResponse delete(Map requestConfig) { - EngineRequest engineRequest = ensureValidRequestConfig(requestConfig, DELETE); - return request(engineRequest); - } - - @Override - public WebSocket webSocket(Map requestConfig, WebSocketListener listener) { - EngineRequest engineRequest = ensureValidRequestConfig(requestConfig, GET); - Request.Builder requestBuilder = prepareRequest(new Request.Builder(), engineRequest); - Request request = requestBuilder.build(); - - OkHttpClient.Builder clientBuilder = prepareClient(new OkHttpClient.Builder(), engineRequest.getTimeout()); - OkHttpClient client = newClient(clientBuilder); - - return client.newWebSocket(request, listener); - } - - @Override - public EngineResponse request(EngineRequest requestConfig) { - EngineRequest config = ensureValidRequestConfig(requestConfig); - - Request.Builder requestBuilder = prepareRequest(new Request.Builder(), config); - final Request request = requestBuilder.build(); - - OkHttpClient.Builder clientBuilder = prepareClient(new OkHttpClient.Builder(), config.getTimeout()); - final OkHttpClient client = newClient(clientBuilder); - - log.debug(request.method() + " " + request.url() + " using proxy: " + client.proxy()); - - Call call = client.newCall(request); - EngineResponse dockerResponse; - try { - Response response = call.execute(); - log.debug("response: " + response); - dockerResponse = handleResponse(response, config); - if (dockerResponse.getStream() == null) { -// log.warn("closing response..."); - response.close(); - } - } - catch (Exception e) { - log.error("Request failed", e); - throw new RuntimeException("Request failed", e); - } - return dockerResponse; - } - - private Request.Builder prepareRequest(final Request.Builder builder, final EngineRequest config) { - String method = config.getMethod().name(); - String contentType = config.getContentType(); - Map additionalHeaders = config.getHeaders(); - Object body = config.getBody(); - - String protocol = dockerClientConfig.getScheme(); - String host = dockerClientConfig.getHost(); - int port = dockerClientConfig.getPort(); - - String path = config.getPath(); - if (config.getApiVersion() != null) { - path = config.getApiVersion() + "/" + path; - } - String queryAsString; - if (config.getQuery() != null) { - queryAsString = queryToString(config.getQuery()); - } - else { - queryAsString = ""; - } - - HttpUrl.Builder urlBuilder = new HttpUrl.Builder().addPathSegments(path); - if (queryAsString != null && !queryAsString.isEmpty()) { - urlBuilder = urlBuilder.encodedQuery(queryAsString); -// } else { -// urlBuilder = urlBuilder.encodedQuery(null); - } - HttpUrl httpUrl = createUrl(urlBuilder, protocol, host, port); - - RequestBody requestBody = createRequestBody(method, contentType, body); - builder.method(method, requestBody).url(httpUrl).cacheControl(CacheControl.FORCE_NETWORK); - if (additionalHeaders != null) { - additionalHeaders.forEach(builder::header); - } - return builder; - } - - private OkHttpClient.Builder prepareClient(OkHttpClient.Builder builder, int currentTimeout) { - String protocol = dockerClientConfig.getScheme(); - switch (protocol) { - case "unix": - if (!socketFactories.containsKey(protocol)) { - log.error("Unix domain socket not supported, but configured (using defaults?). Please consider changing the DOCKER_HOST environment setting to use tcp."); - throw new IllegalStateException("Unix domain socket not supported."); - } - FileSocketFactory unixSocketFactory = (FileSocketFactory) socketFactories.get(protocol); - builder - .socketFactory(unixSocketFactory) - .dns(unixSocketFactory) - .build(); - break; - case "npipe": - FileSocketFactory npipeSocketFactory = (FileSocketFactory) socketFactories.get(protocol); - builder - .socketFactory(npipeSocketFactory) - .dns(npipeSocketFactory) - .build(); - break; - case "https": - String certPath = dockerClientConfig.getCertPath(); - SslSocketConfigFactory sslSocketFactory = (SslSocketConfigFactory) socketFactories.get(protocol); - DockerSslSocket dockerSslSocket = sslSocketFactory.createDockerSslSocket(certPath); - if (dockerSslSocket != null) { - builder - .sslSocketFactory(dockerSslSocket.getSslSocketFactory(), dockerSslSocket.getTrustManager()) - .build(); - } - break; - } - builder.proxy(proxy); - - // do we need to disable the timeout for streaming? - builder - .connectTimeout(currentTimeout, MILLISECONDS) - .readTimeout(currentTimeout, MILLISECONDS); - return builder; - } - - public OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - return clientBuilder.build(); - } - - private HttpUrl createUrl(HttpUrl.Builder urlBuilder, String protocol, String host, int port) { - HttpUrl httpUrl; - switch (protocol) { - case "unix": - case "npipe": - httpUrl = urlBuilder - .scheme("http") - .host(new HostnameEncoder().encode(host) + SOCKET_MARKER) - .build(); - break; - default: - httpUrl = urlBuilder - .scheme(protocol) - .host(host) - .port(port) - .build(); - break; - } - return httpUrl; - } - - private RequestBody createRequestBody(String method, String contentType, Object body) { - if (body == null && HttpMethod.requiresRequestBody(method)) { - return RequestBody.create("", MediaType.parse("application/json")); - } - - RequestBody requestBody = null; - if (body != null) { - switch (contentType) { - case "application/json": - requestBody = RequestBody.create(moshi.adapter(Map.class).toJson((Map) body), MediaType.parse(contentType)); - break; - case "application/octet-stream": - default: - Source source = Okio.source((InputStream) body); - BufferedSource buffer = Okio.buffer(source); - requestBody = new StreamingRequestBody(MediaType.parse(contentType), buffer); - break; - } - } - return requestBody; - } - - public EngineResponse handleResponse(Response httpResponse, EngineRequest config) throws IOException { - final EngineResponse response = readHeaders(httpResponse); - - if (response.getStatus().getCode() == 204) { - if (response.getStream() != null) { - // redirect the response body to /dev/null, since it's expected to be empty - IOUtils.consumeToDevNull(response.getStream()); - } - return response; - } - ResponseBody body = httpResponse.body(); - - String mimeType = response.getMimeType(); - if (mimeType == null) { - mimeType = ""; - } - switch (mimeType) { - case "application/vnd.docker.multiplexed-stream": - case "application/vnd.docker.raw-stream": - InputStream rawStream = new RawInputStream(body.byteStream()); - if (config.getStdout() != null) { - log.debug("redirecting to stdout."); - IOUtils.copy(rawStream, config.getStdout()); - response.setStream(null); - } - else { - response.setStream(rawStream); - } - break; - case "application/json": - if (config.isAsync()) { - consumeResponseBody(response, body.source(), config); - } - else { - Object content = new JsonContentHandler().getContent(body.source()); - consumeResponseBody(response, content, config); - } - break; - case "text/html": - case "text/plain": - InputStream text = body.byteStream(); - consumeResponseBody(response, text, config); - break; - case "application/octet-stream": - InputStream octet = body.byteStream(); - if (config.getStdout() != null) { - log.debug("redirecting to stdout."); - IOUtils.copy(octet, config.getStdout()); - response.setStream(null); - } - else { - log.debug("passing through via `response.stream`."); - response.setStream(octet); - } - break; - case "application/x-tar": - if (response.getStream() != null) { - if (config.getStdout() != null) { - log.debug("redirecting to stdout."); - IOUtils.copy(response.getStream(), config.getStdout()); - response.setStream(null); - } - else { - log.info(response.getMimeType() + " stream won't be consumed, but is available in the response."); - } - } - break; - default: - if (body == null || body.contentLength() == 0) { - response.setContent(body == null ? null : body.string()); - response.setStream(null); - return response; - } - log.debug("unexpected mime type '" + response.getMimeType() + "'."); - if (body.contentLength() == -1) { - InputStream stream = body.byteStream(); - if (config.getStdout() != null) { - log.debug("redirecting to stdout."); - IOUtils.copy(stream, config.getStdout()); - response.setStream(null); - } - else { - log.debug("passing through via `response.stream`."); - response.setStream(stream); - } - } - else { - log.debug("passing through via `response.content`."); - response.setContent(body.string()); - response.setStream(null); - } - break; - } - - return response; - } - - private EngineResponse readHeaders(Response httpResponse) { - final EngineResponse dockerResponse = new EngineResponse(); - - EngineResponseStatus status = new EngineResponseStatus(); - status.setText(httpResponse.message()); - status.setCode(httpResponse.code()); - status.setSuccess(httpResponse.isSuccessful()); - dockerResponse.setStatus(status); - log.trace("status: " + dockerResponse.getStatus()); - - final Headers headers = httpResponse.headers(); - log.trace("headers: \n" + headers); - dockerResponse.setHeaders(headers); - - String contentType = headers.get("content-type"); - dockerResponse.setContentType(contentType); - - String contentLength = headers.get("content-length"); - if (contentLength == null) { - contentLength = "-1"; - } - dockerResponse.setContentLength(contentLength); - - String mimeType = getMimeType(contentType); - dockerResponse.setMimeType(mimeType); - - if (dockerResponse.getStatus().getSuccess()) { - dockerResponse.setStream(httpResponse.body().byteStream()); - } - else { - dockerResponse.setStream(null); - } - return dockerResponse; - } - - private void consumeResponseBody(EngineResponse response, Object content, EngineRequest config) throws IOException { - if (content instanceof Source) { - if (config.isAsync()) { - response.setStream(Okio.buffer((Source) content).inputStream()); - } - else if (config.getStdout() != null) { - response.setStream(null); - Okio.buffer(Okio.sink(config.getStdout())).writeAll((Source) content); - } - else if (response.getContentLength() != null && Integer.parseInt(response.getContentLength()) >= 0) { - response.setStream(null); - response.setContent(Okio.buffer((Source) content).readUtf8()); - } - else { - response.setStream(Okio.buffer((Source) content).inputStream()); - } - } - else if (content instanceof InputStream) { - if (config.isAsync()) { - response.setStream((InputStream) content); - } - else if (config.getStdout() != null) { - IOUtils.copy((InputStream) content, config.getStdout()); - response.setStream(null); - } - else if (response.getContentLength() != null && Integer.parseInt(response.getContentLength()) >= 0) { - response.setContent(IOUtils.toString((InputStream) content)); - response.setStream(null); - } - else { - response.setStream((InputStream) content); - } - } - else { - response.setContent(content); - response.setStream(null); - } - } - - /** - * @see #ensureValidRequestConfig(EngineRequest) - * @deprecated use ensureValidRequestConfig(EngineRequest) - */ - @Deprecated - private EngineRequest ensureValidRequestConfig(final Map config, RequestMethod method) { - if (config == null || config.get("path") == null) { - log.error("bad request config: " + config); - throw new IllegalArgumentException("bad request config"); - } - if (((String) config.get("path")).startsWith("/")) { - config.put("path", ((String) config.get("path")).substring("/".length())); - } - config.put("method", method.name()); - - EngineRequest engineRequest = new EngineRequest(method, (String) config.get("path")); - engineRequest.setTimeout(config.get("timeout") == null ? 0 : (Integer) config.get("timeout")); - engineRequest.setHeaders((Map) config.get("headers")); - Map query = (Map) config.get("query"); - engineRequest.setQuery(coerceValuesToListOfString(query)); - - engineRequest.setContentType((String) config.get("requestContentType")); - engineRequest.setBody(config.get("body")); - - engineRequest.setAsync(config.get("async") != null && (Boolean) config.get("async")); - engineRequest.setStdout((OutputStream) config.get("stdout")); - - engineRequest.setApiVersion((String) config.get("apiVersion")); - return engineRequest; - } - - private EngineRequest ensureValidRequestConfig(final EngineRequest config) { - if (config == null || config.getPath() == null) { - log.error("bad request config: " + config); - throw new IllegalArgumentException("bad request config"); - } - if ((config.getPath()).startsWith("/")) { - config.setPath(config.getPath().substring("/".length())); - } - return config; - } - - private Map> coerceValuesToListOfString(Map queryParameters) { - if (queryParameters == null || queryParameters.isEmpty()) { - return new HashMap<>(); - } - return queryParameters.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, ((Map.Entry e) -> convert(e.getValue())))); - } - - private List convert(Object value) { - if (value instanceof String[]) { - return Arrays.stream((String[]) value).collect(Collectors.toList()); - } - else if (value instanceof Collection) { - return ((Collection) value).stream() - .map(Object::toString) - .collect(Collectors.toList()); - } - else if (value != null) { - return Collections.singletonList(value.toString()); - } - else { - return Collections.singletonList(""); - } - } - - public String queryToString(Map> queryParameters) { - if (queryParameters == null || queryParameters.isEmpty()) { - return ""; - } - return queryParameters.entrySet().stream() - .map((Map.Entry> e) -> { - String key = e.getKey(); - List value = e.getValue(); - if (value != null) { - return value.stream() - .map((s) -> asUrlEncodedQuery(key, s)) - .collect(Collectors.joining("&")); - } - else { - return asUrlEncodedQuery(key, ""); - } - }) - .collect(Collectors.joining("&")); - } - - private String asUrlEncodedQuery(String key, String value) { - try { - return URLEncoder.encode(key, "UTF-8") + "=" + URLEncoder.encode(value, "UTF-8"); - } - catch (UnsupportedEncodingException e) { - log.error("Url encoding failed for key=" + key + ",value=" + value, e); - throw new RuntimeException("Url encoding failed", e); - } - } - - public String getMimeType(String contentTypeHeader) { - if (contentTypeHeader == null) { - return null; - } - return contentTypeHeader.replace(" ", "").split(";")[0]; - } - - public String getCharset(String contentTypeHeader) { - String charset = "utf-8"; - Matcher matcher = Pattern.compile("[^;]+;\\s*charset=([^;]+)(;[^;]*)*").matcher(contentTypeHeader); - if (matcher.find()) { - charset = matcher.group(1); - } - return charset; - } - - Map getSocketFactories() { - return socketFactories; - } - - DockerClientConfig getDockerClientConfig() { - return dockerClientConfig; - } - - void setProxy(Proxy proxy) { - this.proxy = proxy; - } -} diff --git a/engine/src/main/java/de/gesellix/docker/engine/RequestMethod.java b/engine/src/main/java/de/gesellix/docker/engine/RequestMethod.java deleted file mode 100644 index 36e020f7..00000000 --- a/engine/src/main/java/de/gesellix/docker/engine/RequestMethod.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.gesellix.docker.engine; - -public enum RequestMethod { - - GET, - DELETE, - HEAD, - OPTIONS, - PATCH, - POST, - PUT -} diff --git a/engine/src/main/java/de/gesellix/docker/engine/StreamingRequestBody.java b/engine/src/main/java/de/gesellix/docker/engine/StreamingRequestBody.java deleted file mode 100644 index 8f558f61..00000000 --- a/engine/src/main/java/de/gesellix/docker/engine/StreamingRequestBody.java +++ /dev/null @@ -1,62 +0,0 @@ -package de.gesellix.docker.engine; - -import okhttp3.MediaType; -import okhttp3.RequestBody; -import okio.BufferedSink; -import okio.Source; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; - -public class StreamingRequestBody extends RequestBody { - - private final MediaType contentType; - private final Source body; - - public StreamingRequestBody(MediaType contentType, Source body) { - this.contentType = contentType; - this.body = body; - } - - @Nullable - @Override - public MediaType contentType() { - return contentType; - } - - @Override - public long contentLength() { - return -1; - } - - @Override - public boolean isOneShot() { - return true; - } - - @Override - public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException { - Throwable exception = null; - try { - bufferedSink.writeAll(body); - } - catch (Exception e) { - exception = e; - throw e; - } - finally { - if (exception == null) { - body.close(); - } - else { - try { - body.close(); - } - catch (Throwable closeException) { - // ignored, previous exception takes precendence - } - } - } - } -} diff --git a/engine/src/main/java/de/gesellix/docker/websocket/DefaultWebSocketListener.java b/engine/src/main/java/de/gesellix/docker/websocket/DefaultWebSocketListener.java deleted file mode 100644 index b8aa7c30..00000000 --- a/engine/src/main/java/de/gesellix/docker/websocket/DefaultWebSocketListener.java +++ /dev/null @@ -1,45 +0,0 @@ -package de.gesellix.docker.websocket; - -import okhttp3.Response; -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; -import okio.ByteString; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class DefaultWebSocketListener extends WebSocketListener { - - private final static Logger log = LoggerFactory.getLogger(DefaultWebSocketListener.class); - - @Override - public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { - log.debug("[onOpen]"); - } - - @Override - public void onFailure(@NotNull WebSocket webSocket, final Throwable t, Response response) { - log.debug("[onFailure] {}", t.getMessage()); - t.printStackTrace(); - } - - @Override - public void onMessage(@NotNull WebSocket webSocket, @NotNull final String text) { - log.debug("[onMessage.text] {}", text); - } - - @Override - public void onMessage(@NotNull WebSocket webSocket, final ByteString bytes) { - log.debug("[onMessage.binary] size: {}", bytes.size()); - } - - @Override - public void onClosing(@NotNull WebSocket webSocket, final int code, @NotNull final String reason) { - log.debug("[onClosing] {}/{}", code, reason); - } - - @Override - public void onClosed(@NotNull WebSocket webSocket, final int code, @NotNull final String reason) { - log.debug("[onClosed] {}/{}", code, reason); - } -} diff --git a/engine/src/main/java/de/gesellix/docker/websocket/WebsocketStatusCode.java b/engine/src/main/java/de/gesellix/docker/websocket/WebsocketStatusCode.java deleted file mode 100644 index d521b910..00000000 --- a/engine/src/main/java/de/gesellix/docker/websocket/WebsocketStatusCode.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.gesellix.docker.websocket; - -public enum WebsocketStatusCode { - NORMAL_CLOSURE(1000), - GOING_AWAY(1001), - PROTOCOL_ERROR(1002), - UNSUPPORTED_DATA(1003), - NO_STATUS_RCVD(1005), - ABNORMAL_CLOSURE(1006), - INVALID_FRAME_PAYLOAD_DATA(1007), - POLICY_VIOLATION(1008), - MESSAGE_TOO_BIG(1009), - MANDATORY_EXT(1010), - INTERNAL_SERVER_ERROR(1011), - TLS_HANDSHAKE(1015); - - private WebsocketStatusCode(int code) { - this.code = code; - } - - public int getCode() { - return code; - } - - private int code; -} diff --git a/engine/src/main/java/de/gesellix/util/IOUtils.java b/engine/src/main/java/de/gesellix/util/IOUtils.java index acd98256..be1485df 100644 --- a/engine/src/main/java/de/gesellix/util/IOUtils.java +++ b/engine/src/main/java/de/gesellix/util/IOUtils.java @@ -11,10 +11,6 @@ public class IOUtils { - public static long consumeToDevNull(InputStream source) throws IOException { - return copy(Okio.source(source), Okio.blackhole()); - } - public static long copy(InputStream source, OutputStream sink) throws IOException { return copy(Okio.source(source), Okio.sink(sink)); } diff --git a/engine/src/test/groovy/de/gesellix/docker/engine/OkDockerClientSpec.groovy b/engine/src/test/groovy/de/gesellix/docker/engine/OkDockerClientSpec.groovy deleted file mode 100644 index c319f6fb..00000000 --- a/engine/src/test/groovy/de/gesellix/docker/engine/OkDockerClientSpec.groovy +++ /dev/null @@ -1,1057 +0,0 @@ -package de.gesellix.docker.engine - -import de.gesellix.docker.rawstream.RawHeaderAndPayload -import de.gesellix.docker.rawstream.RawInputStream -import de.gesellix.docker.ssl.DockerSslSocket -import de.gesellix.docker.ssl.SslSocketConfigFactory -import de.gesellix.util.IOUtils -import groovy.util.logging.Slf4j -import okhttp3.Interceptor -import okhttp3.MediaType -import okhttp3.OkHttpClient -import okhttp3.Protocol -import okhttp3.Response -import okhttp3.ResponseBody -import okhttp3.WebSocketListener -import okhttp3.mockwebserver.MockResponse -import okhttp3.mockwebserver.MockWebServer -import okio.Buffer -import org.codehaus.groovy.runtime.MethodClosure -import spock.lang.IgnoreIf -import spock.lang.Specification -import spock.lang.Unroll - -import javax.net.ssl.HostnameVerifier -import javax.net.ssl.SSLSession -import javax.net.ssl.SSLSocket - -import static de.gesellix.docker.engine.RequestMethod.DELETE -import static de.gesellix.docker.engine.RequestMethod.GET -import static de.gesellix.docker.engine.RequestMethod.HEAD -import static de.gesellix.docker.engine.RequestMethod.OPTIONS -import static de.gesellix.docker.engine.RequestMethod.POST -import static de.gesellix.docker.engine.RequestMethod.PUT -import static de.gesellix.docker.rawstream.StreamType.STDOUT -import static java.net.Proxy.Type.DIRECT -import static java.net.Proxy.Type.HTTP - -@Slf4j -class OkDockerClientSpec extends Specification { - - @IgnoreIf({ System.getenv("DOCKER_HOST") }) - "getProtocolAndHost should fallback to unix:///var/run/docker.sock (npipe on Windows)"() { - given: - def client = new OkDockerClient() - def isWindows = System.properties['os.name'].toLowerCase().contains('windows') - def expectedScheme = isWindows ? "npipe" : "unix" - def defaultHost = isWindows ? "//./pipe/docker_engine" : "/var/run/docker.sock" - - expect: - client.dockerClientConfig.scheme == expectedScheme - client.dockerClientConfig.host == defaultHost - client.dockerClientConfig.port == -1 - } - - @IgnoreIf({ System.getenv("DOCKER_HOST") }) - "getProtocolAndHost should use to docker.host system property when set"() { - given: - def oldDockerHost = System.setProperty("docker.host", "http://127.0.0.1:2375") - def client = new OkDockerClient() - expect: - client.dockerClientConfig.scheme == "http" - client.dockerClientConfig.host == "127.0.0.1" - client.dockerClientConfig.port == 2375 - cleanup: - if (oldDockerHost) { - System.setProperty("docker.host", oldDockerHost) - } else { - System.clearProperty("docker.host") - } - } - - def "getProtocolAndHost should support tcp protocol"() { - def client = new OkDockerClient() - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "tcp://127.0.0.1:2375")) - when: - def protocol = client.dockerClientConfig.scheme - def host = client.dockerClientConfig.host - def port = client.dockerClientConfig.port - then: - protocol =~ /https?/ - and: - host == "127.0.0.1" - and: - port == 2375 - } - - def "getProtocolAndHost should support tls port"() { - given: - def certsPath = IOUtils.getResource("/certs").file - def oldDockerCertPath = System.setProperty("docker.cert.path", certsPath) - def client = new OkDockerClient() - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: "tcp://127.0.0.1:2376")) - when: - def protocol = client.dockerClientConfig.scheme - def host = client.dockerClientConfig.host - def port = client.dockerClientConfig.port - then: - protocol == "https" - and: - host == "127.0.0.1" - and: - port == 2376 - cleanup: - if (oldDockerCertPath) { - System.setProperty("docker.cert.path", oldDockerCertPath) - } else { - System.clearProperty("docker.cert.path") - } - } - - def "getProtocolAndHost should support https protocol"() { - given: - def certsPath = IOUtils.getResource("/certs").file - def oldDockerCertPath = System.setProperty("docker.cert.path", certsPath) - def client = new OkDockerClient() - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: "https://127.0.0.1:2376")) - expect: - client.dockerClientConfig.scheme == "https" - client.dockerClientConfig.host == "127.0.0.1" - client.dockerClientConfig.port == 2376 - cleanup: - if (oldDockerCertPath) { - System.setProperty("docker.cert.path", oldDockerCertPath) - } else { - System.clearProperty("docker.cert.path") - } - } - - def "getMimeType"() { - def client = new OkDockerClient() - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: "https://127.0.0.1:2376")) - expect: - client.getMimeType(contentType) == expectedMimeType - where: - contentType | expectedMimeType - null | null - "application/json" | "application/json" - "text/plain" | "text/plain" - "text/plain; charset=utf-8" | "text/plain" - "text/html" | "text/html" - "application/vnd.docker.raw-stream" | "application/vnd.docker.raw-stream" - "application/vnd.docker.multiplexed-stream" | "application/vnd.docker.multiplexed-stream" - } - - def "getCharset"() { - def client = new OkDockerClient() - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: "https://127.0.0.1:2376")) - expect: - client.getCharset(contentType) == expectedCharset - where: - contentType | expectedCharset - "application/json" | "utf-8" - "text/plain" | "utf-8" - "text/plain; charset=ISO-8859-1" | "ISO-8859-1" - "text/html" | "utf-8" - "application/vnd.docker.raw-stream" | "utf-8" - "application/vnd.docker.multiplexed-stream" | "utf-8" - } - - @Unroll - def "queryToString #parameters"() { - def client = new OkDockerClient() - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: "https://127.0.0.1:2376")) - expect: - client.queryToString(parameters) == query - where: - parameters | query - null | "" - [:] | "" - [param1: ["value-1"]] | "param1=value-1" - ["p 1": ["v 1"]] | "p+1=v+1" - [param1: ["v 1"], p2: ["v-2"]] | "param1=v+1&p2=v-2" - [params: ["v 1", "v-2"]] | "params=v+1¶ms=v-2" - } - - @Unroll - "generic request with bad config: #requestConfig"() { - def client = new OkDockerClient() - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: "https://127.0.0.1:2376")) - when: - client.request(requestConfig) - then: - def ex = thrown(RuntimeException) - ex.message == "bad request config" - where: - requestConfig << [null, new EngineRequest(OPTIONS, null)] - } - - @Unroll - "#method request with bad config: #requestConfig"() { - def client = new OkDockerClient() - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: "https://127.0.0.1:2376")) - when: - new MethodClosure(client, method).call(requestConfig as Map) - then: - def ex = thrown(RuntimeException) - ex.message == "bad request config" - where: - requestConfig | method - null | "get" - null | "post" - [:] | "get" - [:] | "post" - ["foo": "bar"] | "get" - ["foo": "bar"] | "post" - } - - def "head request uses the HEAD method"() { - given: - def recordedCall = [:] - def client = new OkDockerClient() { - - @Override - EngineResponse request(EngineRequest requestConfig) { - recordedCall['method'] = requestConfig.method - return null - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "https://127.0.0.1:2376")) - when: - client.head([path: "/foo"]) - then: - recordedCall['method'] == HEAD - } - - def "get request uses the GET method"() { - given: - def recordedCall = [:] - def client = new OkDockerClient() { - - @Override - EngineResponse request(EngineRequest requestConfig) { - recordedCall['method'] = requestConfig.method - return null - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "https://127.0.0.1:2376")) - when: - client.get([path: "/foo"]) - then: - recordedCall['method'] == GET - } - - def "put request uses the PUT method"() { - given: - def recordedCall = [:] - def client = new OkDockerClient() { - - @Override - EngineResponse request(EngineRequest requestConfig) { - recordedCall['method'] = requestConfig.method - return null - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "https://127.0.0.1:2376")) - when: - client.put([path: "/foo"]) - then: - recordedCall['method'] == PUT - } - - def "post request uses the POST method"() { - given: - def recordedCall = [:] - def client = new OkDockerClient() { - - @Override - EngineResponse request(EngineRequest requestConfig) { - recordedCall['method'] = requestConfig.method - return null - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "https://127.0.0.1:2376")) - when: - client.post([path: "/foo"]) - then: - recordedCall['method'] == POST - } - - def "delete request uses the DELETE method"() { - given: - def recordedCall = [:] - def client = new OkDockerClient() { - - @Override - EngineResponse request(EngineRequest requestConfig) { - recordedCall['method'] = requestConfig.method - return null - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "https://127.0.0.1:2376")) - when: - client.delete([path: "/foo"]) - then: - recordedCall['method'] == DELETE - } - - def "webSocket prepares a websocket call"() { - given: - def certsPath = IOUtils.getResource("/certs").file - def oldDockerCertPath = System.setProperty("docker.cert.path", certsPath) - def client = new OkDockerClient() - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "https://127.0.0.1:2376")) - - when: - def wsCall = client.webSocket([path: "/foo"], Mock(WebSocketListener)) - - then: - wsCall != null - - cleanup: - if (oldDockerCertPath) { - System.setProperty("docker.cert.path", oldDockerCertPath) - } else { - System.clearProperty("docker.cert.path") - } - } - - def "uses DIRECT proxy by default"() { - given: - def mockWebServer = new MockWebServer() - mockWebServer.enqueue(new MockResponse().setBody("mock-response")) - mockWebServer.start() - - def hasVerified = false - Closure verifyResponse = { Response res, Interceptor.Chain chain -> - if (chain.connection()?.route()?.proxy()?.type() != DIRECT) { - throw new RuntimeException("got ${chain.connection()?.route()?.proxy()}") - } - hasVerified = true - true - } - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addNetworkInterceptor(new TestInterceptor({ true }, verifyResponse)) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: mockWebServer.url("/").toString())) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - then: - response.status.success - and: - hasVerified - - cleanup: - mockWebServer.shutdown() - } - - def "uses configured proxy"() { - given: - def mockWebServer = new MockWebServer() - mockWebServer.enqueue(new MockResponse().setBody("mock-response")) - mockWebServer.start() - - def serverUrl = mockWebServer.url("/") - def proxy = new Proxy(HTTP, new InetSocketAddress(serverUrl.host(), serverUrl.port())) - - def hasVerified = false - Closure verifyResponse = { Response res, Interceptor.Chain chain -> - def actualProxy = chain.connection()?.route()?.proxy() - if (actualProxy != proxy) { - throw new RuntimeException("got ${actualProxy}") - } - hasVerified = true - true - } - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addNetworkInterceptor(new TestInterceptor({ true }, verifyResponse)) - .build() - } - } - client.proxy = proxy - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: "http://any.thi.ng:4711")) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - then: - response.status.success - and: - hasVerified - - cleanup: - mockWebServer.shutdown() - } - - def "request with path"() { - given: - def mockWebServer = new MockWebServer() - mockWebServer.enqueue(new MockResponse().setBody("mock-response")) - mockWebServer.start() - - def serverUrl = mockWebServer.url("/") - def hasVerified = false - Closure verifyResponse = { Response res, Interceptor.Chain chain -> - def expectedUrl = serverUrl.newBuilder() - .encodedPath("/a-resource") - .build() - if (expectedUrl != chain.request().url()) { - throw new RuntimeException("expected ${expectedUrl}, got ${chain.request().url()}") - } - hasVerified = true - true - } - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addNetworkInterceptor(new TestInterceptor({ true }, verifyResponse)) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: mockWebServer.url("/").toString())) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - then: - response.status.success - and: - hasVerified - - cleanup: - mockWebServer.shutdown() - } - - def "request with explicit api version"() { - given: - def mockWebServer = new MockWebServer() - mockWebServer.enqueue(new MockResponse().setBody("mock-response")) - mockWebServer.start() - - def serverUrl = mockWebServer.url("/") - def hasVerified = false - Closure verifyResponse = { Response res, Interceptor.Chain chain -> - def expectedUrl = serverUrl.newBuilder() - .encodedPath("/v1.23/a-resource") - .build() - if (expectedUrl != chain.request().url()) { - throw new RuntimeException("expected ${expectedUrl}, got ${chain.request().url()}") - } - hasVerified = true - true - } - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addNetworkInterceptor(new TestInterceptor({ true }, verifyResponse)) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: mockWebServer.url("/").toString())) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource") - .tap { apiVersion = "v1.23" }) - then: - response.status.success - and: - hasVerified - - cleanup: - mockWebServer.shutdown() - } - - def "request with path and query"() { - given: - def mockWebServer = new MockWebServer() - mockWebServer.enqueue(new MockResponse().setBody("mock-response")) - mockWebServer.start() - - def serverUrl = mockWebServer.url("/") - def hasVerified = false - Closure verifyResponse = { Response res, Interceptor.Chain chain -> - def expectedUrl = serverUrl.newBuilder() - .encodedPath("/a-resource") - .encodedQuery("baz=la%2Fla&answer=42") - .build() - if (expectedUrl != chain.request().url()) { - throw new RuntimeException("expected ${expectedUrl}, got ${chain.request().url()}") - } - hasVerified = true - true - } - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addNetworkInterceptor(new TestInterceptor({ true }, verifyResponse)) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: mockWebServer.url("/").toString())) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource") - .tap { query = [baz: ["la/la"], answer: ["42"]] }) - then: - response.status.success - and: - hasVerified - - cleanup: - mockWebServer.shutdown() - } - - def "connect via plain http connection"() { - given: - def mockWebServer = new MockWebServer() - mockWebServer.enqueue(new MockResponse().setBody("mock-response")) - mockWebServer.start() - - def hasVerified = false - Closure verifyResponse = { Response res, Interceptor.Chain chain -> - if (chain.connection()?.socket() instanceof SSLSocket) { - throw new RuntimeException("didn't expect a SSLSocket, got ${chain.connection()?.socket()}") - } - hasVerified = true - true - } - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addNetworkInterceptor(new TestInterceptor({ true }, verifyResponse)) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: mockWebServer.url("/").toString())) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - then: - response.status.success - and: - hasVerified - - cleanup: - mockWebServer.shutdown() - } - - def "connect via https connection"() { - given: - def certsPath = IOUtils.getResource("/certs").file - def oldDockerCertPath = System.setProperty("docker.cert.path", certsPath) - - def mockWebServer = new MockWebServer() - mockWebServer.useHttps(new SslSocketConfigFactory().createDockerSslSocket(certsPath).sslSocketFactory, false) - mockWebServer.enqueue(new MockResponse().setBody("mock-response")) - mockWebServer.start() - - def hasVerified = false - Closure verifyResponse = { Response res, Interceptor.Chain chain -> - if (!(chain.connection()?.socket() instanceof SSLSocket)) { - throw new RuntimeException("expected a SSLSocket, got ${chain.connection()?.socket()}") - } - hasVerified = true - true - } - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .hostnameVerifier(new LocalhostHostnameVerifier()) - .addNetworkInterceptor(new TestInterceptor({ true }, verifyResponse)) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv( - dockerHost: mockWebServer.url("/").toString(), - tlsVerify: "1")) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - - then: - response.status.success - and: - hasVerified - - cleanup: - mockWebServer.shutdown() - if (oldDockerCertPath) { - System.setProperty("docker.cert.path", oldDockerCertPath) - } else { - System.clearProperty("docker.cert.path") - } - } - - def "request should return statusLine"() { - given: - def code = statusCode - def message = statusMessage - def mediaType = MediaType.parse("text/plain") - def responseBody = "holy ship" - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addInterceptor(new ConstantResponseInterceptor(code, message, ResponseBody.create(responseBody, mediaType))) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "http://127.0.0.1:2375")) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - - then: - response.status == expectedStatus - - where: - statusMessage | statusCode | expectedStatus - "Continue" | 100 | new EngineResponseStatus(text: "Continue", code: 100, success: false) - "OK" | 200 | new EngineResponseStatus(text: "OK", code: 200, success: true) - "No Content" | 204 | new EngineResponseStatus(text: "No Content", code: 204, success: true) - "Found" | 302 | new EngineResponseStatus(text: "Found", code: 302, success: false) - "Not Found" | 404 | new EngineResponseStatus(text: "Not Found", code: 404, success: false) - "Internal Server Error" | 500 | new EngineResponseStatus(text: "Internal Server Error", code: 500, success: false) - } - - def "request should return headers"() { - given: - def mediaType = MediaType.parse("text/plain") - def responseBody = "holy ship" - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addInterceptor(new ConstantResponseInterceptor(ResponseBody.create(responseBody, mediaType))) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "http://127.0.0.1:2375")) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - - then: - response.headers['Content-Type'] == "text/plain; charset=utf-8" - response.headers['Content-Length'] == "9" - and: - response.contentType == "text/plain; charset=utf-8" - and: - response.mimeType == "text/plain" - and: - response.contentLength == "9" - } - - def "request should return consumed content"() { - given: - def mediaType = MediaType.parse("text/plain") - def responseBody = "holy ship" - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addInterceptor(new ConstantResponseInterceptor(ResponseBody.create(responseBody, mediaType))) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "http://127.0.0.1:2375")) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - - then: - response.stream == null - and: - response.content == "holy ship" - } - - def "request with stdout stream and known content length"() { - given: - def mediaType = MediaType.parse("text/html") - def responseBody = "holy ship" - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addInterceptor(new ConstantResponseInterceptor(ResponseBody.create(responseBody, mediaType))) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "http://127.0.0.1:2375")) - def stdout = new ByteArrayOutputStream() - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource") - .tap { it.stdout = stdout }) - - then: - stdout.toByteArray() == "holy ship".bytes - and: - response.stream == null - } - - def "request with stdout stream and unknown content length"() { - given: - def mediaType = MediaType.parse("text/html") - def responseBody = "holy ship" - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addInterceptor(new ConstantResponseInterceptor(ResponseBody.create(new Buffer().write(responseBody.bytes), mediaType, -1))) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "http://127.0.0.1:2375")) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - - then: - response.stream.available() == "holy ship".length() - and: - IOUtils.toString(response.stream as InputStream) == "holy ship" - } - - def "request with unknown mime type"() { - given: - def mediaType = MediaType.parse("unknown/mime-type") - def responseBody = "holy ship" - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addInterceptor(new ConstantResponseInterceptor(ResponseBody.create(new Buffer().write(responseBody.bytes), mediaType, -1))) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "http://127.0.0.1:2375")) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - - then: - response.stream.available() == "holy ship".length() - and: - IOUtils.toString(response.stream as InputStream) == "holy ship" - } - - def "request with unknown mime type and stdout"() { - given: - def mediaType = MediaType.parse("unknown/mime-type") - def responseBody = "holy ship" - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addInterceptor(new ConstantResponseInterceptor(ResponseBody.create(new Buffer().write(responseBody.bytes), mediaType, -1))) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "http://127.0.0.1:2375")) - def stdout = new ByteArrayOutputStream() - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource") - .tap { it.stdout = stdout }) - - then: - stdout.toByteArray() == "holy ship".bytes - and: - response.stream == null - } - - def "request with json response"() { - given: - def mediaType = MediaType.parse("application/json") - def responseBody = '{"holy":"ship"}' - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addInterceptor(new ConstantResponseInterceptor(ResponseBody.create(responseBody, mediaType))) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "http://127.0.0.1:2375")) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - - then: - response.stream == null - and: - response.content == [holy: 'ship'] - } - - def "request with docker raw-stream response"() { - given: - def actualText = "holy ship" - def headerAndPayload = new RawHeaderAndPayload(STDOUT, actualText.bytes) - def responseBody = new ByteArrayInputStream(headerAndPayload.bytes as byte[]) - def mediaType = MediaType.parse("application/vnd.docker.raw-stream") - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addInterceptor(new ConstantResponseInterceptor(ResponseBody.create(responseBody.bytes, mediaType))) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "http://127.0.0.1:2375")) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - - then: - response.stream instanceof RawInputStream - and: - IOUtils.toString(response.stream as RawInputStream) == actualText - and: - response.content == null - } - - def "request with docker raw-stream response on stdout"() { - given: - def actualText = "holy ship" - def headerAndPayload = new RawHeaderAndPayload(STDOUT, actualText.bytes) - def responseBody = new ByteArrayInputStream(headerAndPayload.bytes as byte[]) - def mediaType = MediaType.parse("application/vnd.docker.raw-stream") - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addInterceptor(new ConstantResponseInterceptor(ResponseBody.create(responseBody.bytes, mediaType))) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "https://127.0.0.1:2376")) - client.socketFactories["https"] = new SslSocketConfigFactory() { - - @Override - DockerSslSocket createDockerSslSocket(String certPath) { - return null - } - } - def stdout = new ByteArrayOutputStream() - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource") - .tap { it.stdout = stdout }) - - then: - stdout.toByteArray() == actualText.bytes - and: - responseBody.available() == 0 - and: - response.stream == null - and: - response.content == null - } - - def "request with docker multiplexed-stream response"() { - given: - def actualText = "holy ship" - def headerAndPayload = new RawHeaderAndPayload(STDOUT, actualText.bytes) - def responseBody = new ByteArrayInputStream(headerAndPayload.bytes as byte[]) - def mediaType = MediaType.parse("application/vnd.docker.multiplexed-stream") - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addInterceptor(new ConstantResponseInterceptor(ResponseBody.create(responseBody.bytes, mediaType))) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "http://127.0.0.1:2375")) - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource")) - - then: - response.stream instanceof RawInputStream - and: - IOUtils.toString(response.stream as RawInputStream) == actualText - and: - response.content == null - } - - def "request with docker multiplexed-stream response on stdout"() { - given: - def actualText = "holy ship" - def headerAndPayload = new RawHeaderAndPayload(STDOUT, actualText.bytes) - def responseBody = new ByteArrayInputStream(headerAndPayload.bytes as byte[]) - def mediaType = MediaType.parse("application/vnd.docker.multiplexed-stream") - def client = new OkDockerClient() { - - @Override - OkHttpClient newClient(OkHttpClient.Builder clientBuilder) { - clientBuilder - .addInterceptor(new ConstantResponseInterceptor(ResponseBody.create(responseBody.bytes, mediaType))) - .build() - } - } - client.dockerClientConfig.apply(new DockerEnv(dockerHost: "https://127.0.0.1:2376")) - client.socketFactories["https"] = new SslSocketConfigFactory() { - - @Override - DockerSslSocket createDockerSslSocket(String certPath) { - return null - } - } - def stdout = new ByteArrayOutputStream() - - when: - def response = client.request(new EngineRequest(OPTIONS, "/a-resource") - .tap { it.stdout = stdout }) - - then: - stdout.toByteArray() == actualText.bytes - and: - responseBody.available() == 0 - and: - response.stream == null - and: - response.content == null - } - - static class TestContentHandlerFactory implements ContentHandlerFactory { - - def contentHandler = null - - @Override - ContentHandler createContentHandler(String mimetype) { - return contentHandler - } - } - - static class TestContentHandler extends ContentHandler { - - def result = null - - @Override - Object getContent(URLConnection urlc) throws IOException { - return result - } - } - - static class ConstantResponseInterceptor implements Interceptor { - - int statusCode - String statusMessage - ResponseBody responseBody - - ConstantResponseInterceptor(ResponseBody responseBody) { - this(200, "OK", responseBody) - } - - ConstantResponseInterceptor(int statusCode, String statusMessage, ResponseBody responseBody) { - this.statusCode = statusCode - this.statusMessage = statusMessage - this.responseBody = responseBody - } - - @Override - Response intercept(Interceptor.Chain chain) throws IOException { - new Response.Builder() - .request(chain.request()) - .protocol(Protocol.HTTP_1_1) - .code(statusCode) - .message(statusMessage) - .body(responseBody) - .addHeader("Content-Type", responseBody.contentType().toString()) - .addHeader("Content-Length", Long.toString(responseBody.contentLength())) - .build() - } - } - - static class TestInterceptor implements Interceptor { - - Closure requestVerifier - Closure responseVerifier - - int statusCode - String statusMessage - ResponseBody responseBody - - TestInterceptor(Closure requestVerifier, Closure responseVerifier) { - this.requestVerifier = requestVerifier - this.responseVerifier = responseVerifier - this.statusCode = 200 - this.statusMessage = "OK" - this.responseBody = ResponseBody.create("ok by ${getClass().simpleName}", MediaType.parse("text/plain")) - } - - @Override - Response intercept(Interceptor.Chain chain) throws IOException { - requestVerifier(chain) - def response = chain.proceed(chain.request()) - responseVerifier(response, chain) - return response - } - } - - static class LocalhostHostnameVerifier implements HostnameVerifier { - - @Override - boolean verify(String host, SSLSession sslSession) { - def whitelist = ["localhost", - "127.0.0.1", - "stratum.antpool.com"] - def isWindows = System.properties['os.name'].toLowerCase().contains('windows') - def hosts = isWindows ? new File("${System.getenv("SystemRoot")}\\System32\\drivers\\etc\\hosts") : new File("/etc/hosts") - if (hosts.isFile()) { - whitelist.addAll(hosts.readLines() - .grep({ it.startsWith("127.0.0.1 ") }) - .collect({ it.substring("127.0.0.1 ".length()).toLowerCase() })) - } - def isAllowed = host.toLowerCase() in whitelist || host.endsWith(".internal") - if (!isAllowed) { - println("$host not in $whitelist") - } - return isAllowed - } - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 304d19c1..e843388f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,8 +7,6 @@ logback = "1.3.15" logbackVersionrange = "[1.2,2)" moshi = "1.15.2" moshiVersionrange = "[1.12.0,2)" -okhttp = "5.3.0" -okhttpVersionrange = "[4,6)" okio = "3.16.2" okioVersionrange = "[3,4)" slf4j = "2.0.17" @@ -27,8 +25,6 @@ kotlinReflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = kotlinTest = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } -okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } -okhttpMockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" } okio = { module = "com.squareup.okio:okio", version.ref = "okio" } okioJvm = { module = "com.squareup.okio:okio-jvm", version.ref = "okio" } slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } @@ -36,5 +32,4 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } [bundles] junixsocket = ["junixsocketCore", "junixsocketCommon"] kotlin = ["kotlin", "kotlinCommon", "kotlinJdk7", "kotlinJdk8", "kotlinReflect", "kotlinTest"] -okhttp = ["okhttp", "okhttpMockwebserver"] okio = ["okio", "okioJvm"] diff --git a/integrationtest/build.gradle.kts b/integrationtest/build.gradle.kts deleted file mode 100644 index b5eb565b..00000000 --- a/integrationtest/build.gradle.kts +++ /dev/null @@ -1,69 +0,0 @@ -plugins { - groovy - id("com.github.ben-manes.versions") - id("org.sonatype.gradle.plugins.scan") -} - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(8)) - } -} - -repositories { - mavenCentral() -} - -dependencies { - constraints { - implementation("org.slf4j:slf4j-api") { - version { - strictly(libs.versions.slf4jVersionrange.get()) - prefer(libs.versions.slf4j.get()) - } - } - listOf( - libs.bundles.okhttp - ).forEach { - implementation(it) { - version { - strictly(libs.versions.okhttpVersionrange.get()) - prefer(libs.versions.okhttp.get()) - } - } - } - implementation("com.squareup.okio:okio") { - version { - strictly(libs.versions.okioVersionrange.get()) - prefer(libs.versions.okio.get()) - } - } - listOf( - libs.bundles.kotlin - ).forEach { - implementation(it) { - version { - strictly(libs.versions.kotlinVersionrange.get()) - prefer(libs.versions.kotlin.get()) - } - } - } - } - implementation(project(":engine")) - testImplementation(libs.okhttp) - testImplementation(libs.slf4j) - testRuntimeOnly(libs.logback) - testImplementation("org.spockframework:spock-core:2.3-groovy-4.0") - testRuntimeOnly("net.bytebuddy:byte-buddy:1.17.8") - testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.13.4") -} -tasks.check.get().shouldRunAfter(project(":engine").tasks.check) - -tasks { - withType { - options.encoding = "UTF-8" - } - withType { - useJUnitPlatform() - } -} diff --git a/integrationtest/src/test/groovy/de/gesellix/docker/engine/LocalDocker.groovy b/integrationtest/src/test/groovy/de/gesellix/docker/engine/LocalDocker.groovy deleted file mode 100644 index 6bdd26b7..00000000 --- a/integrationtest/src/test/groovy/de/gesellix/docker/engine/LocalDocker.groovy +++ /dev/null @@ -1,82 +0,0 @@ -package de.gesellix.docker.engine - -import groovy.util.logging.Slf4j - -@Slf4j -class LocalDocker { - - static void main(String[] args) { - log.debug available() ? "connection success" : "failed to connect" - } - - static available() { - try { - return new OkDockerClient().get([path: "/_ping", timeout: 2000]).status.code == 200 - } - catch (Exception e) { - log.info("Docker not available", e) - return false - } - } - - static hasSwarmMode() { - try { - def version = getDockerVersion() - return version.major >= 1 && version.minor >= 12 - } - catch (Exception e) { - log.info("Docker not available", e) - return false - } - } - - static supportsStack() { - try { - def version = getDockerVersion() - return (version.major >= 1 && version.minor >= 13) || version.major >= 17 - } - catch (Exception e) { - log.info("Docker not available", e) - return false - } - } - - static DockerVersion getDockerVersion() { - try { - def version = new OkDockerClient().get([path: "/version"]).content.Version as String - return DockerVersion.parseDockerVersion(version) - } - catch (Exception e) { - log.info("Docker not available", e) - throw new RuntimeException(e) - } - } - - static boolean isNativeWindows() { - try { - def version = new OkDockerClient().get([path: "/version"]) - def arch = version.content.Arch as String - def os = version.content.Os as String - return "$os/$arch".toString() == "windows/amd64" - } - catch (Exception e) { - log.info("Docker not available", e) - throw new RuntimeException(e) - } - } - - static isNamedPipe() { - def dockerHost = new DockerEnv().dockerHost - return dockerHost.startsWith("npipe://") - } - - static isUnixSocket() { - def dockerHost = new DockerEnv().dockerHost - return dockerHost.startsWith("unix://") - } - - static isTcpSocket() { - def dockerHost = new DockerEnv().dockerHost - return dockerHost.startsWith("tcp://") || dockerHost.startsWith("http://") || dockerHost.startsWith("https://") - } -} diff --git a/integrationtest/src/test/groovy/de/gesellix/docker/engine/OkDockerClientIntegrationSpec.groovy b/integrationtest/src/test/groovy/de/gesellix/docker/engine/OkDockerClientIntegrationSpec.groovy deleted file mode 100644 index 154045e7..00000000 --- a/integrationtest/src/test/groovy/de/gesellix/docker/engine/OkDockerClientIntegrationSpec.groovy +++ /dev/null @@ -1,111 +0,0 @@ -package de.gesellix.docker.engine - -import groovy.util.logging.Slf4j -import okio.Okio -import spock.lang.IgnoreIf -import spock.lang.Requires -import spock.lang.Specification - -import static de.gesellix.docker.engine.RequestMethod.GET -import static de.gesellix.docker.engine.TestConstants.CONSTANTS - -@Slf4j -@Requires({ LocalDocker.available() }) -class OkDockerClientIntegrationSpec extends Specification { - - final static dockerHubUsername = "gesellix" - final static dockerHubPassword = "-yet-another-password-" - final static dockerHubEmail = "tobias@gesellix.de" - - def "should allow GET requests"() { - given: - def client = new OkDockerClient() - - when: - def ping = client.get([path: "/_ping"]) - def content = ping.content ?: Okio.buffer(Okio.source(client.get([path: "/_ping"]).stream)).readUtf8() - - then: - content == "OK" - } - - def "should allow POST requests"() { - given: - def client = new OkDockerClient() - def request = [path : "/images/create", - query: [fromImage: CONSTANTS.imageRepo, - tag : CONSTANTS.imageTag, - registry : ""]] - - when: - def response = client.post(request) - then: - response.content.last() in [ - [status: "Status: Image is up to date for ${CONSTANTS.imageName}".toString()], - [status: "Status: Downloaded newer image for ${CONSTANTS.imageName}".toString()] - ] - } - - @IgnoreIf({ dockerHubPassword == "-yet-another-password-" }) - def "should allow POST requests with body"() { - given: - def client = new OkDockerClient() - def authDetails = ["username" : dockerHubUsername, - "password" : dockerHubPassword, - "email" : dockerHubEmail, - "serveraddress": "https://index.docker.io/v1/"] - def request = [path : "/auth", - body : authDetails, - requestContentType: "application/json"] - when: - def response = client.post(request) - then: - response.content == [IdentityToken: "", - Status : "Login Succeeded"] - } - - def "should optionally stream a response"() { - def client = new OkDockerClient() - def outputStream = new ByteArrayOutputStream() - when: - client.get([path : "/_ping", - stdout: outputStream]) - then: - outputStream.toString() == "OK" - } - - def "should parse application/json"() { - def client = new OkDockerClient() - when: - def response = client.get([path: "/version"]) - then: - def content = response.content - - def nonMatchingEntries = CONSTANTS.versionDetails.findResults { key, matcher -> - !matcher(content[key]) ? [(key): content[key]] : null - } - nonMatchingEntries.empty - } - - @Requires({ LocalDocker.isUnixSocket() }) - def "should support unix socket connections (Linux native or Docker for Mac)"() { - def client = new OkDockerClient() - when: - def response = client.request(new EngineRequest(GET, "/info")) - then: - def dockerHost = client.getDockerClientConfig().getEnv().getDockerHost() - dockerHost.startsWith("unix://") - response.status.code == 200 - } - - @Requires({ LocalDocker.isNamedPipe() }) - def "should support named pipe socket connections (Docker for Windows)"() { - def client = new OkDockerClient() - when: - def response = client.request(new EngineRequest(GET, "/info")) - then: - def dockerHost = client.getDockerClientConfig().getEnv().getDockerHost() - dockerHost.startsWith("npipe://") - response.status.code == 200 - } -} diff --git a/integrationtest/src/test/groovy/de/gesellix/docker/engine/TeeOutputStream.java b/integrationtest/src/test/groovy/de/gesellix/docker/engine/TeeOutputStream.java deleted file mode 100644 index ca2080b3..00000000 --- a/integrationtest/src/test/groovy/de/gesellix/docker/engine/TeeOutputStream.java +++ /dev/null @@ -1,55 +0,0 @@ -package de.gesellix.docker.engine; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * An output stream which copies anything written into it to another stream. - */ -public class TeeOutputStream - extends OutputStream { - - private final OutputStream output1; - private final OutputStream output2; - - /** - * Base constructor. - * - * @param output1 the output stream that is wrapped. - * @param output2 a secondary stream that anything written to output1 is also written to. - */ - public TeeOutputStream(OutputStream output1, OutputStream output2) { - this.output1 = output1; - this.output2 = output2; - } - - public void write(byte[] buf) - throws IOException { - this.output1.write(buf); - this.output2.write(buf); - } - - public void write(byte[] buf, int off, int len) - throws IOException { - this.output1.write(buf, off, len); - this.output2.write(buf, off, len); - } - - public void write(int b) - throws IOException { - this.output1.write(b); - this.output2.write(b); - } - - public void flush() - throws IOException { - this.output1.flush(); - this.output2.flush(); - } - - public void close() - throws IOException { - this.output1.close(); - this.output2.close(); - } -} diff --git a/integrationtest/src/test/groovy/de/gesellix/docker/engine/TestConstants.groovy b/integrationtest/src/test/groovy/de/gesellix/docker/engine/TestConstants.groovy deleted file mode 100644 index 842affc3..00000000 --- a/integrationtest/src/test/groovy/de/gesellix/docker/engine/TestConstants.groovy +++ /dev/null @@ -1,33 +0,0 @@ -package de.gesellix.docker.engine - -class TestConstants { - - final String imageRepo - final String imageTag - final String imageName - - final Map> versionDetails = [:] - - public static TestConstants CONSTANTS = new TestConstants() - - TestConstants() { - // docker inspect --format "{{ json .Created }}, Id: {{ json .Id }}, Digests: {{ json .RepoDigests }}" gesellix/echo-server:2025-07-27T22-12-00 - imageRepo = "gesellix/echo-server" - imageTag = "2025-07-27T22-12-00" - imageName = "$imageRepo:$imageTag" - - versionDetails = [ - ApiVersion : { it in ["1.43", "1.44", "1.45", "1.46", "1.47", "1.48", "1.49", "1.50", "1.51"] }, - Arch : { it in ["amd64", "arm64"] }, - BuildTime : { it =~ "\\d{4}-\\d{2}-\\d{2}T\\w+" }, - GitCommit : { it =~ "\\w{6,}" }, - GoVersion : { it =~ "go\\d+.\\d+.\\d+" }, - KernelVersion: { it =~ "\\d.\\d{1,2}.\\d{1,2}\\w*" }, - MinAPIVersion: { it in ["1.12", "1.24"] }, - Os : { it == "linux" }, - Version : { it == "master" || it =~ "\\d{1,2}.\\d{1,2}.\\d{1,2}\\w*" }] - if (LocalDocker.isNativeWindows()) { - versionDetails.Os = { it == "windows" } - } - } -} diff --git a/integrationtest/src/test/resources/logback-test.xml b/integrationtest/src/test/resources/logback-test.xml deleted file mode 100644 index 418628e6..00000000 --- a/integrationtest/src/test/resources/logback-test.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - - - diff --git a/settings.gradle.kts b/settings.gradle.kts index fc8737d6..3f69e2f0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,5 @@ rootProject.name = "docker-engine" -include("engine", "integrationtest") +include("engine") // https://docs.gradle.org/current/userguide/toolchains.html#sub:download_repositories plugins {