From ccfb0bfb95e3418bef846de6e8ef5c53a9edc0c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Schollmeyer?= Date: Wed, 11 Oct 2017 10:10:22 +0200 Subject: [PATCH 01/13] feat(deployments): add function to create a deployment --- pom.xml | 2 +- .../bosh/client/deployments/Deployments.java | 9 ++++-- .../client/deployments/SpringDeployments.java | 28 ++++++++++++++----- .../internal/AbstractSpringOperations.java | 16 +++++------ 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index a31f4ce..db9160d 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 io.bosh.client bosh-java-client - 0.9.1-SNAPSHOT + 0.9.2-SNAPSHOT BOSH Java Client A Java client for the BOSH Director API. https://github.com/davidehringer/bosh-java-client diff --git a/src/main/java/io/bosh/client/deployments/Deployments.java b/src/main/java/io/bosh/client/deployments/Deployments.java index 6248605..454cccf 100644 --- a/src/main/java/io/bosh/client/deployments/Deployments.java +++ b/src/main/java/io/bosh/client/deployments/Deployments.java @@ -15,10 +15,11 @@ */ package io.bosh.client.deployments; -import java.util.List; - +import io.bosh.client.tasks.Task; import rx.Observable; +import java.util.List; + /** * @author David Ehringer */ @@ -27,6 +28,10 @@ public interface Deployments { Observable> list(); Observable get(String deploymentName); + + Observable create(Deployment deployment); + + Observable delete(Deployment deployment); Observable> cloudcheck(String deploymentName); } diff --git a/src/main/java/io/bosh/client/deployments/SpringDeployments.java b/src/main/java/io/bosh/client/deployments/SpringDeployments.java index 5aa28f5..e29d733 100644 --- a/src/main/java/io/bosh/client/deployments/SpringDeployments.java +++ b/src/main/java/io/bosh/client/deployments/SpringDeployments.java @@ -15,9 +15,16 @@ */ package io.bosh.client.deployments; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import io.bosh.client.DirectorException; import io.bosh.client.internal.AbstractSpringOperations; +import io.bosh.client.tasks.Task; import io.bosh.client.tasks.Tasks; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.web.client.RestTemplate; +import rx.Observable; import java.io.IOException; import java.net.URI; @@ -25,13 +32,6 @@ import java.util.List; import java.util.Map; -import org.springframework.web.client.RestTemplate; - -import rx.Observable; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; - /** * @author David Ehringer */ @@ -71,6 +71,20 @@ public Observable get(String deploymentName) { }); } + @Override + public Observable create(Deployment deployment) { + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Type", "text/yaml"); + return exchangeForEntity(deployment, Task.class, headers, HttpMethod.POST, + builder -> builder.path("deployments")) + .map(exchange -> exchange.getBody()); + } + + @Override + public Observable delete(Deployment deployment) { + return null; + } + @Override public Observable> cloudcheck(String deploymentName) { return postForEntity(Void.class, null, builder -> builder.pathSegment("deployments", deploymentName, "scans")) diff --git a/src/main/java/io/bosh/client/internal/AbstractSpringOperations.java b/src/main/java/io/bosh/client/internal/AbstractSpringOperations.java index 6ed1208..cf2b6c0 100644 --- a/src/main/java/io/bosh/client/internal/AbstractSpringOperations.java +++ b/src/main/java/io/bosh/client/internal/AbstractSpringOperations.java @@ -16,13 +16,6 @@ package io.bosh.client.internal; import io.bosh.client.DirectorException; - -import java.net.URI; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; @@ -32,9 +25,14 @@ import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.RestOperations; import org.springframework.web.util.UriComponentsBuilder; - import rx.Observable; +import java.net.URI; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * @author David Ehringer */ @@ -81,7 +79,7 @@ protected final Observable post(Class responseType, Object request, builderCallback.accept(builder); URI uri = builder.build().toUri(); - this.logger.debug("GET {}", uri); + this.logger.debug("POST {}", uri); return this.restOperations.postForObject(uri, request, responseType); }); } From fc8dd6c0c3929eb4019476d76a91d59fbf597de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Schollmeyer?= Date: Wed, 11 Oct 2017 10:51:54 +0200 Subject: [PATCH 02/13] feature(deployments): add function to delete a deployment --- .../java/io/bosh/client/deployments/Deployments.java | 5 +++-- .../io/bosh/client/deployments/SpringDeployments.java | 10 +++++----- .../bosh/client/internal/AbstractSpringOperations.java | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/bosh/client/deployments/Deployments.java b/src/main/java/io/bosh/client/deployments/Deployments.java index 454cccf..f373ec7 100644 --- a/src/main/java/io/bosh/client/deployments/Deployments.java +++ b/src/main/java/io/bosh/client/deployments/Deployments.java @@ -16,6 +16,7 @@ package io.bosh.client.deployments; import io.bosh.client.tasks.Task; +import org.springframework.http.HttpHeaders; import rx.Observable; import java.util.List; @@ -29,9 +30,9 @@ public interface Deployments { Observable get(String deploymentName); - Observable create(Deployment deployment); + Observable create(Deployment deployment, HttpHeaders headers); - Observable delete(Deployment deployment); + Observable delete(Deployment deployment, HttpHeaders headers); Observable> cloudcheck(String deploymentName); } diff --git a/src/main/java/io/bosh/client/deployments/SpringDeployments.java b/src/main/java/io/bosh/client/deployments/SpringDeployments.java index e29d733..12147cc 100644 --- a/src/main/java/io/bosh/client/deployments/SpringDeployments.java +++ b/src/main/java/io/bosh/client/deployments/SpringDeployments.java @@ -72,17 +72,17 @@ public Observable get(String deploymentName) { } @Override - public Observable create(Deployment deployment) { - HttpHeaders headers = new HttpHeaders(); - headers.add("Content-Type", "text/yaml"); + public Observable create(Deployment deployment, HttpHeaders headers) { return exchangeForEntity(deployment, Task.class, headers, HttpMethod.POST, builder -> builder.path("deployments")) .map(exchange -> exchange.getBody()); } @Override - public Observable delete(Deployment deployment) { - return null; + public Observable delete(Deployment deployment, HttpHeaders headers) { + return exchangeForEntity("", Task.class, headers, HttpMethod.DELETE, + builder -> builder.pathSegment("deployments", deployment.getName())) + .map(exchange -> exchange.getBody()); } @Override diff --git a/src/main/java/io/bosh/client/internal/AbstractSpringOperations.java b/src/main/java/io/bosh/client/internal/AbstractSpringOperations.java index cf2b6c0..18944d6 100644 --- a/src/main/java/io/bosh/client/internal/AbstractSpringOperations.java +++ b/src/main/java/io/bosh/client/internal/AbstractSpringOperations.java @@ -103,7 +103,7 @@ protected final Observable> exchangeForEntity(T request builderCallback.accept(builder); URI uri = builder.build().toUri(); - RequestEntity requestEntity = new RequestEntity(request, headers, HttpMethod.PUT, uri); + RequestEntity requestEntity = new RequestEntity(request, headers, method, uri); this.logger.debug("{} {}", method, uri); return this.restOperations.exchange( requestEntity, responseType); }); From 8d9ef8c5d25c8f366135b99d0568a9526f7eca1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Schollmeyer?= Date: Wed, 11 Oct 2017 10:56:13 +0200 Subject: [PATCH 03/13] refactor(deployments): remove headers from delete method --- src/main/java/io/bosh/client/deployments/Deployments.java | 2 +- .../java/io/bosh/client/deployments/SpringDeployments.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/bosh/client/deployments/Deployments.java b/src/main/java/io/bosh/client/deployments/Deployments.java index f373ec7..ecc7d9c 100644 --- a/src/main/java/io/bosh/client/deployments/Deployments.java +++ b/src/main/java/io/bosh/client/deployments/Deployments.java @@ -32,7 +32,7 @@ public interface Deployments { Observable create(Deployment deployment, HttpHeaders headers); - Observable delete(Deployment deployment, HttpHeaders headers); + Observable delete(Deployment deployment); Observable> cloudcheck(String deploymentName); } diff --git a/src/main/java/io/bosh/client/deployments/SpringDeployments.java b/src/main/java/io/bosh/client/deployments/SpringDeployments.java index 12147cc..9781d2a 100644 --- a/src/main/java/io/bosh/client/deployments/SpringDeployments.java +++ b/src/main/java/io/bosh/client/deployments/SpringDeployments.java @@ -79,8 +79,8 @@ public Observable create(Deployment deployment, HttpHeaders headers) { } @Override - public Observable delete(Deployment deployment, HttpHeaders headers) { - return exchangeForEntity("", Task.class, headers, HttpMethod.DELETE, + public Observable delete(Deployment deployment) { + return exchangeForEntity("", Task.class, null, HttpMethod.DELETE, builder -> builder.pathSegment("deployments", deployment.getName())) .map(exchange -> exchange.getBody()); } From 813517095bab5b140fad95e46174eedd48cb7d61 Mon Sep 17 00:00:00 2001 From: Yannic Remmet Date: Wed, 18 Oct 2017 08:31:01 +0200 Subject: [PATCH 04/13] chore(Deployment) updated deployment API --- .../java/io/bosh/client/deployments/Deployment.java | 2 ++ .../io/bosh/client/deployments/Deployments.java | 4 ++++ .../bosh/client/deployments/SpringDeployments.java | 13 ++++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/bosh/client/deployments/Deployment.java b/src/main/java/io/bosh/client/deployments/Deployment.java index d66df6f..2cc8829 100644 --- a/src/main/java/io/bosh/client/deployments/Deployment.java +++ b/src/main/java/io/bosh/client/deployments/Deployment.java @@ -43,6 +43,8 @@ public String getRawManifest() { return manifest; } + public void setRawManifest(String manifest) { this.manifest = manifest; } + public Map getManifest() { return Collections.unmodifiableMap(manifestMap); } diff --git a/src/main/java/io/bosh/client/deployments/Deployments.java b/src/main/java/io/bosh/client/deployments/Deployments.java index ecc7d9c..7134624 100644 --- a/src/main/java/io/bosh/client/deployments/Deployments.java +++ b/src/main/java/io/bosh/client/deployments/Deployments.java @@ -32,6 +32,10 @@ public interface Deployments { Observable create(Deployment deployment, HttpHeaders headers); + Observable create(Deployment deployment); + + Observable update(Deployment deployment); + Observable delete(Deployment deployment); Observable> cloudcheck(String deploymentName); diff --git a/src/main/java/io/bosh/client/deployments/SpringDeployments.java b/src/main/java/io/bosh/client/deployments/SpringDeployments.java index 9781d2a..aeae8e0 100644 --- a/src/main/java/io/bosh/client/deployments/SpringDeployments.java +++ b/src/main/java/io/bosh/client/deployments/SpringDeployments.java @@ -71,9 +71,20 @@ public Observable get(String deploymentName) { }); } + @Override + public Observable create(Deployment deployment) { + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Type", "text/yaml"); + return create(deployment, headers); + } + + public Observable update(Deployment deployment) { + return create(deployment); + } + @Override public Observable create(Deployment deployment, HttpHeaders headers) { - return exchangeForEntity(deployment, Task.class, headers, HttpMethod.POST, + return exchangeForEntity(deployment.getRawManifest(), Task.class, headers, HttpMethod.POST, builder -> builder.path("deployments")) .map(exchange -> exchange.getBody()); } From 5bc8faae5921a4e6c66b719ce4618f9a44a02800 Mon Sep 17 00:00:00 2001 From: Yannic Remmet Date: Fri, 20 Oct 2017 11:17:11 +0200 Subject: [PATCH 05/13] fix(deploments) fixes the issue that create deployments wont get a Task as return because the bosh director redirects to a wrong endpoint instead of returning the Task. --- .../client/deployments/SpringDeployments.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/bosh/client/deployments/SpringDeployments.java b/src/main/java/io/bosh/client/deployments/SpringDeployments.java index aeae8e0..c5d95e8 100644 --- a/src/main/java/io/bosh/client/deployments/SpringDeployments.java +++ b/src/main/java/io/bosh/client/deployments/SpringDeployments.java @@ -23,7 +23,11 @@ import io.bosh.client.tasks.Tasks; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; import rx.Observable; import java.io.IOException; @@ -31,6 +35,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.function.Consumer; /** * @author David Ehringer @@ -41,6 +46,7 @@ public class SpringDeployments extends AbstractSpringOperations implements Deplo public SpringDeployments(RestTemplate restTemplate, URI root, Tasks tasks) { super(restTemplate, root); + restTemplate.getRequestFactory(); this.tasks = tasks; } @@ -84,7 +90,10 @@ public Observable update(Deployment deployment) { @Override public Observable create(Deployment deployment, HttpHeaders headers) { - return exchangeForEntity(deployment.getRawManifest(), Task.class, headers, HttpMethod.POST, + return exchange(deployment.getRawManifest(), + Task.class, + headers, + HttpMethod.POST, builder -> builder.path("deployments")) .map(exchange -> exchange.getBody()); } @@ -104,4 +113,11 @@ public Observable> cloudcheck(String deploymentName) { .map(problems -> Arrays.asList(problems)); } + protected final Observable> exchange(T request, + Class responseType, HttpHeaders headers, HttpMethod method, Consumer builderCallback) { + return super.exchangeForEntity(request,responseType,headers,method,builderCallback).map(r -> { + String taskId = getTaskId(r); + return getEntity(responseType, builder -> builder.pathSegment("tasks", taskId)).toBlocking().first(); + }); + } } From 5668ba9bab3b2e6f823ba3a835fbb8d9b16b3d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Schollmeyer?= Date: Mon, 23 Oct 2017 11:51:21 +0200 Subject: [PATCH 06/13] fix(deployments): fix the issue that delete deployments wont get a Task as return --- src/main/java/io/bosh/client/deployments/SpringDeployments.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/bosh/client/deployments/SpringDeployments.java b/src/main/java/io/bosh/client/deployments/SpringDeployments.java index c5d95e8..44312b1 100644 --- a/src/main/java/io/bosh/client/deployments/SpringDeployments.java +++ b/src/main/java/io/bosh/client/deployments/SpringDeployments.java @@ -100,7 +100,7 @@ public Observable create(Deployment deployment, HttpHeaders headers) { @Override public Observable delete(Deployment deployment) { - return exchangeForEntity("", Task.class, null, HttpMethod.DELETE, + return exchange("", Task.class, null, HttpMethod.DELETE, builder -> builder.pathSegment("deployments", deployment.getName())) .map(exchange -> exchange.getBody()); } From d68ee55575ca6e8ee41df8b61b8fd9aa00d0d740 Mon Sep 17 00:00:00 2001 From: Yannic Remmet Date: Fri, 27 Oct 2017 09:21:11 +0200 Subject: [PATCH 07/13] feat(Errands): adds the possibility to run errand jobs --- .../client/deployments/SpringDeployments.java | 18 +++++------------- .../java/io/bosh/client/errands/Errands.java | 7 +++++++ .../io/bosh/client/errands/SpringErrands.java | 18 ++++++++++++++++++ .../internal/AbstractSpringOperations.java | 8 ++++++++ 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/bosh/client/deployments/SpringDeployments.java b/src/main/java/io/bosh/client/deployments/SpringDeployments.java index c5d95e8..3494834 100644 --- a/src/main/java/io/bosh/client/deployments/SpringDeployments.java +++ b/src/main/java/io/bosh/client/deployments/SpringDeployments.java @@ -23,9 +23,7 @@ import io.bosh.client.tasks.Tasks; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; -import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import rx.Observable; @@ -90,10 +88,10 @@ public Observable update(Deployment deployment) { @Override public Observable create(Deployment deployment, HttpHeaders headers) { - return exchange(deployment.getRawManifest(), - Task.class, - headers, - HttpMethod.POST, + return exchangeWithTaskRedirect(deployment.getRawManifest(), + Task.class, + headers, + HttpMethod.POST, builder -> builder.path("deployments")) .map(exchange -> exchange.getBody()); } @@ -113,11 +111,5 @@ public Observable> cloudcheck(String deploymentName) { .map(problems -> Arrays.asList(problems)); } - protected final Observable> exchange(T request, - Class responseType, HttpHeaders headers, HttpMethod method, Consumer builderCallback) { - return super.exchangeForEntity(request,responseType,headers,method,builderCallback).map(r -> { - String taskId = getTaskId(r); - return getEntity(responseType, builder -> builder.pathSegment("tasks", taskId)).toBlocking().first(); - }); - } + } diff --git a/src/main/java/io/bosh/client/errands/Errands.java b/src/main/java/io/bosh/client/errands/Errands.java index c70ae15..febbe5f 100644 --- a/src/main/java/io/bosh/client/errands/Errands.java +++ b/src/main/java/io/bosh/client/errands/Errands.java @@ -17,6 +17,7 @@ import java.util.List; +import io.bosh.client.tasks.Task; import rx.Observable; /** @@ -25,4 +26,10 @@ public interface Errands { Observable> list(String deploymentName); + + default Observable runErrand(String deploymentName, String errandName){ + return runErrand(deploymentName,errandName,false,false); + } + + Observable runErrand(String deploymentName, String errandName, boolean keep_alive, boolean when_changed); } diff --git a/src/main/java/io/bosh/client/errands/SpringErrands.java b/src/main/java/io/bosh/client/errands/SpringErrands.java index ea0ede9..14f38e4 100644 --- a/src/main/java/io/bosh/client/errands/SpringErrands.java +++ b/src/main/java/io/bosh/client/errands/SpringErrands.java @@ -19,8 +19,13 @@ import java.net.URI; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import io.bosh.client.tasks.Task; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.web.client.RestOperations; import rx.Observable; @@ -39,4 +44,17 @@ public Observable> list(String deploymentName) { return get( ErrandSummary[].class, builder -> builder.pathSegment("deployments", deploymentName, "errands")) .map(response -> Arrays.asList(response)); } + + public Observable runErrand(String deploymentName, String errandName, boolean keep_alive, boolean when_changed) { + HashMap body = new HashMap(); + body.put("keep_alive", keep_alive); + body.put("when_changed", when_changed); + return exchangeWithTaskRedirect(body, + Task.class, + new HttpHeaders(), + HttpMethod.POST, + builder -> builder.pathSegment("deployments", deploymentName, "errands", errandName, "runs") + ) + .map(HttpEntity::getBody); + } } diff --git a/src/main/java/io/bosh/client/internal/AbstractSpringOperations.java b/src/main/java/io/bosh/client/internal/AbstractSpringOperations.java index 18944d6..e3e262a 100644 --- a/src/main/java/io/bosh/client/internal/AbstractSpringOperations.java +++ b/src/main/java/io/bosh/client/internal/AbstractSpringOperations.java @@ -130,4 +130,12 @@ protected String getTaskId(ResponseEntity response) { } throw new IllegalArgumentException("Response does not have a redirect header for a task"); } + + protected final Observable> exchangeWithTaskRedirect (T request, + Class responseType, HttpHeaders headers, HttpMethod method, Consumer builderCallback) { + return exchangeForEntity(request,responseType,headers,method,builderCallback).map(r -> { + String taskId = getTaskId(r); + return getEntity(responseType, builder -> builder.pathSegment("tasks", taskId)).toBlocking().first(); + }); + } } From a364efbe25ff156477f6da83481e619ba25e6802 Mon Sep 17 00:00:00 2001 From: Yannic Remmet Date: Wed, 8 Nov 2017 09:56:00 +0100 Subject: [PATCH 08/13] chroe(maven): prepare maven release --- pom.xml | 12 ++++++------ .../bosh/client/deployments/SpringDeployments.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index db9160d..3df3794 100644 --- a/pom.xml +++ b/pom.xml @@ -1,11 +1,11 @@ 4.0.0 - io.bosh.client + de.evoila bosh-java-client - 0.9.2-SNAPSHOT + 1.0.1-SNAPSHOT BOSH Java Client A Java client for the BOSH Director API. - https://github.com/davidehringer/bosh-java-client + https://github.com/evoila/bosh-java-client Cloud Foundry Community @@ -27,9 +27,9 @@ - scm:git:https://github.com/davidehringer/bosh-java-client.git - scm:git:https://github.com/davidehringer/bosh-java-client.git - https://github.com/davidehringer/bosh-java-client.git + scm:git:https://github.com/evoila/bosh-java-client + scm:git:https://github.com/evoila/bosh-java-client + https://github.com/evoila/bosh-java-client HEAD diff --git a/src/main/java/io/bosh/client/deployments/SpringDeployments.java b/src/main/java/io/bosh/client/deployments/SpringDeployments.java index 0227067..0d2d57f 100644 --- a/src/main/java/io/bosh/client/deployments/SpringDeployments.java +++ b/src/main/java/io/bosh/client/deployments/SpringDeployments.java @@ -98,7 +98,7 @@ public Observable create(Deployment deployment, HttpHeaders headers) { @Override public Observable delete(Deployment deployment) { - return exchange("", Task.class, null, HttpMethod.DELETE, + return exchangeWithTaskRedirect("", Task.class, null, HttpMethod.DELETE, builder -> builder.pathSegment("deployments", deployment.getName())) .map(exchange -> exchange.getBody()); } From b7534cff7c2127808e9740426a507e3fcc6cdc03 Mon Sep 17 00:00:00 2001 From: Yannic Remmet Date: Thu, 9 Nov 2017 11:16:18 +0100 Subject: [PATCH 09/13] fix(SpringDeployments): checks if the deployment manifest requested from the director is null befor parsing the YAML --- .../client/deployments/SpringDeployments.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/bosh/client/deployments/SpringDeployments.java b/src/main/java/io/bosh/client/deployments/SpringDeployments.java index 0d2d57f..b6aadc6 100644 --- a/src/main/java/io/bosh/client/deployments/SpringDeployments.java +++ b/src/main/java/io/bosh/client/deployments/SpringDeployments.java @@ -62,15 +62,16 @@ public Observable get(String deploymentName) { builder -> builder.pathSegment("deployments", deploymentName)) .map(response -> { response.setName(deploymentName); - - ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - Map manifestMap = null; - try { - manifestMap = mapper.readValue(response.getRawManifest(), Map.class); - } catch (IOException e) { - throw new DirectorException("Unable to parse deployment manifest", e); + if(response.getManifest() != null) { + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + Map manifestMap = null; + try { + manifestMap = mapper.readValue(response.getRawManifest(), Map.class); + } catch (IOException e) { + throw new DirectorException("Unable to parse deployment manifest", e); + } + response.setManifestMap(manifestMap); } - response.setManifestMap(manifestMap); return response; }); } From 93a1499ffe9dadb3c787c057375458ec95024071 Mon Sep 17 00:00:00 2001 From: Yannic Remmet Date: Fri, 19 Jan 2018 17:15:43 +0100 Subject: [PATCH 10/13] feat(ssh) adds bosh ssh --- pom.xml | 17 +++- .../bosh/client/deployments/Deployment.java | 1 + .../io/bosh/client/deployments/SSHConfig.java | 85 +++++++++++++++++++ .../client/deployments/SpringDeployments.java | 6 ++ .../java/io/bosh/client/vms/SpringVms.java | 46 ++++++++++ src/main/java/io/bosh/client/vms/Vms.java | 8 +- 6 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 src/main/java/io/bosh/client/deployments/SSHConfig.java diff --git a/pom.xml b/pom.xml index 3df3794..404c4eb 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 de.evoila bosh-java-client - 1.0.1-SNAPSHOT + 1.0.2-SNAPSHOT BOSH Java Client A Java client for the BOSH Director API. https://github.com/evoila/bosh-java-client @@ -30,7 +30,7 @@ scm:git:https://github.com/evoila/bosh-java-client scm:git:https://github.com/evoila/bosh-java-client https://github.com/evoila/bosh-java-client - HEAD + bosh-java-client-1.0.1 @@ -107,6 +107,19 @@ logback-classic test + + + com.hierynomus + sshj + 0.23.0 + + + + com.jcraft + jsch + 0.1.54 + + diff --git a/src/main/java/io/bosh/client/deployments/Deployment.java b/src/main/java/io/bosh/client/deployments/Deployment.java index 2cc8829..905711a 100644 --- a/src/main/java/io/bosh/client/deployments/Deployment.java +++ b/src/main/java/io/bosh/client/deployments/Deployment.java @@ -20,6 +20,7 @@ import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; /** * @author David Ehringer diff --git a/src/main/java/io/bosh/client/deployments/SSHConfig.java b/src/main/java/io/bosh/client/deployments/SSHConfig.java new file mode 100644 index 0000000..2ce9926 --- /dev/null +++ b/src/main/java/io/bosh/client/deployments/SSHConfig.java @@ -0,0 +1,85 @@ +package io.bosh.client.deployments; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class SSHConfig { + + public SSHConfig (String deploymentName, String username, String publicKey, String jobTarget, int indexes){ + this.command = "setup"; + this.deploymentName = deploymentName; + this.target = new SSHTarget(jobTarget, indexes); + this.params = new SSHParams(username, publicKey); + } + + + private String command; + @JsonProperty("deployment_name") + String deploymentName; + private SSHParams params; + private SSHTarget target; + + public SSHConfig (SSHConfig config, String pubKey) { + this.command = config.getCommand(); + this.deploymentName = config.getDeploymentName(); + this.target = new SSHTarget(config.getTarget()); + this.params = new SSHParams(config.getParams().getUser(), pubKey); + } + + public String getCommand () { + return command; + } + + public String getDeploymentName () { + return deploymentName; + } + + public SSHParams getParams () { + return params; + } + + public SSHTarget getTarget () { + return target; + } + + public class SSHParams { + private String user; + @JsonProperty("public_key") + private String publicKey; + + public SSHParams (String username, String publicKey) { + this.user = username; + this.publicKey = publicKey; + } + + public String getUser () { + return user; + } + + public String getPublicKey () { + return publicKey; + } + } + + public class SSHTarget { + String job; + int indexes; + + public SSHTarget (String jobTarget, int indexes) { + this.job = jobTarget; + this.indexes =indexes; + } + + public SSHTarget (SSHTarget target) { + this.job = target.getJob(); + this.indexes = target.getIndexes(); + } + + public String getJob () { + return job; + } + + public int getIndexes () { + return indexes; + } + } +} diff --git a/src/main/java/io/bosh/client/deployments/SpringDeployments.java b/src/main/java/io/bosh/client/deployments/SpringDeployments.java index b6aadc6..0241909 100644 --- a/src/main/java/io/bosh/client/deployments/SpringDeployments.java +++ b/src/main/java/io/bosh/client/deployments/SpringDeployments.java @@ -17,10 +17,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; import io.bosh.client.DirectorException; import io.bosh.client.internal.AbstractSpringOperations; import io.bosh.client.tasks.Task; import io.bosh.client.tasks.Tasks; + import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; @@ -30,6 +33,8 @@ import java.io.IOException; import java.net.URI; +import java.security.PrivateKey; +import java.security.PublicKey; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -113,4 +118,5 @@ public Observable> cloudcheck(String deploymentName) { } + } diff --git a/src/main/java/io/bosh/client/vms/SpringVms.java b/src/main/java/io/bosh/client/vms/SpringVms.java index f5b08ed..316341f 100644 --- a/src/main/java/io/bosh/client/vms/SpringVms.java +++ b/src/main/java/io/bosh/client/vms/SpringVms.java @@ -15,16 +15,25 @@ */ package io.bosh.client.vms; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; import io.bosh.client.DirectorException; +import io.bosh.client.deployments.SSHConfig; import io.bosh.client.internal.AbstractSpringOperations; +import io.bosh.client.tasks.Task; import io.bosh.client.tasks.Tasks; import java.io.IOException; import java.net.URI; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.springframework.http.HttpMethod; import org.springframework.web.client.RestOperations; import rx.Observable; @@ -73,4 +82,41 @@ public Observable> listDetails(String deploymentName) { })); } + + public Observable ssh(SSHConfig config) { + KeyPairGenerator keyGen = null; + try { + keyGen = KeyPairGenerator.getInstance("RSA"); + } catch (NoSuchAlgorithmException e) { + throw new DirectorException("Unable to generate SSH-Keypair" , e); + } + keyGen.initialize(1024); + KeyPair keyPair =keyGen.generateKeyPair(); + config = new SSHConfig(config, keyPair.getPublic().getEncoded().toString()); + return this.ssh(config, keyPair.getPrivate().getEncoded().toString()); + } + + public Observable ssh(SSHConfig config, String privateKey){ + return exchangeWithTaskRedirect(config, + Task.class, + null, + HttpMethod.POST, + builder -> builder.pathSegment("deployments", config.getDeploymentName(), "ssh")) + .map(exchange -> exchange.getBody()) + .map(body -> { + List vms = listDetails(config.getDeploymentName()).toBlocking().first(); + Vm vm = vms.get(config.getTarget().getIndexes()); + + JSch jsch=new JSch(); + Session session = null; + try { + jsch.addIdentity(privateKey); + session = jsch.getSession(config.getParams().getUser(), vm.getIps().get(0), 22); + } catch (JSchException e) { + throw new DirectorException("Unable to create ssh connection to " + vm.getJobName() + vm.getIndex(), e); + } + return session; + }); + } + } \ No newline at end of file diff --git a/src/main/java/io/bosh/client/vms/Vms.java b/src/main/java/io/bosh/client/vms/Vms.java index 8471696..b7183d7 100644 --- a/src/main/java/io/bosh/client/vms/Vms.java +++ b/src/main/java/io/bosh/client/vms/Vms.java @@ -17,6 +17,8 @@ import java.util.List; +import com.jcraft.jsch.Session; +import io.bosh.client.deployments.SSHConfig; import rx.Observable; /** @@ -26,5 +28,9 @@ public interface Vms { Observable> list(String deploymentName); - Observable> listDetails(String deploymentName); + Observable> listDetails(String deploymentName); + + Observable ssh(SSHConfig config, String privateKey); + + Observable ssh(SSHConfig config); } From 11b95f14487bfcddeac38dac06d6103d200a4592 Mon Sep 17 00:00:00 2001 From: Jannik Heyl Date: Mon, 12 Feb 2018 22:24:02 +0100 Subject: [PATCH 11/13] feat(Oauth): Added Oauth Support to Java UAA-Client --- pom.xml | 5 ++ .../java/io/bosh/client/Authentication.java | 8 ++ .../bosh/client/OAuthCredentialsProvider.java | 79 +++++++++++++++++++ .../client/RequestLoggingInterceptor.java | 33 ++++++++ .../client/SpringDirectorClientBuilder.java | 53 +++++++++---- ...eClientCredentialsAccessTokenProvider.java | 56 +++++++++++++ .../io/bosh/client/AbstractDirectorTest.java | 2 +- 7 files changed, 219 insertions(+), 17 deletions(-) create mode 100644 src/main/java/io/bosh/client/Authentication.java create mode 100644 src/main/java/io/bosh/client/OAuthCredentialsProvider.java create mode 100644 src/main/java/io/bosh/client/RequestLoggingInterceptor.java create mode 100644 src/main/java/io/bosh/client/UnsecureClientCredentialsAccessTokenProvider.java diff --git a/pom.xml b/pom.xml index 404c4eb..c887f70 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,11 @@ org.apache.httpcomponents httpclient + + org.springframework.security.oauth + spring-security-oauth2 + 2.0.7.RELEASE + org.slf4j slf4j-api diff --git a/src/main/java/io/bosh/client/Authentication.java b/src/main/java/io/bosh/client/Authentication.java new file mode 100644 index 0000000..e88f541 --- /dev/null +++ b/src/main/java/io/bosh/client/Authentication.java @@ -0,0 +1,8 @@ +package io.bosh.client; + +/** + * Created by jannikheyl on 09.02.18. + */ +public enum Authentication { + BASIC,OAUTH +} diff --git a/src/main/java/io/bosh/client/OAuthCredentialsProvider.java b/src/main/java/io/bosh/client/OAuthCredentialsProvider.java new file mode 100644 index 0000000..7121d02 --- /dev/null +++ b/src/main/java/io/bosh/client/OAuthCredentialsProvider.java @@ -0,0 +1,79 @@ +package io.bosh.client; + +import org.apache.http.Header; +import org.apache.http.HeaderElement; +import org.apache.http.ParseException; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.Credentials; +import org.apache.http.client.CredentialsProvider; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.token.AccessTokenProvider; +import org.springframework.security.oauth2.client.token.AccessTokenProviderChain; +import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; +import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; +import org.springframework.security.oauth2.common.AuthenticationScheme; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.util.Assert; + +import java.util.Arrays; + +/** + * Created by jannikheyl on 09.02.18. + */ +public class OAuthCredentialsProvider implements Header { + private OAuth2AccessToken token; + private AccessTokenProviderChain CHAIN = new AccessTokenProviderChain( + Arrays. asList(new UnsecureClientCredentialsAccessTokenProvider())); + private ClientCredentialsResourceDetails credentials = new ClientCredentialsResourceDetails(); + + + public OAuthCredentialsProvider(String host, String username, + String password){ + host="https://" + host + ":8443/oauth/token"; + getCredentials(host, username, password); + requestToken(); + } + private void requestToken(){ + + if (token == null) { + token = CHAIN.obtainAccessToken(credentials, new DefaultAccessTokenRequest()); + } + else if (token.isExpired()) { + refreshAccessToken(); + } + + } + private void refreshAccessToken() { + Assert.notNull(token); + + token = CHAIN.refreshAccessToken(credentials, token.getRefreshToken(), new DefaultAccessTokenRequest()); + } + private ClientCredentialsResourceDetails getCredentials(String host, String username, + String password){ + credentials.setAccessTokenUri(host); + credentials.setClientAuthenticationScheme(AuthenticationScheme.form); + credentials.setClientId(username); + credentials.setClientSecret(password); + credentials.setGrantType("client_credentials"); + return credentials; + } + + @Override + public String getName() { + return "Authorization"; + } + + @Override + public String getValue() { + return token.getTokenType()+ " "+ token.getValue(); + } + + @Override + public HeaderElement[] getElements() throws ParseException { + return new HeaderElement[0]; + } +} diff --git a/src/main/java/io/bosh/client/RequestLoggingInterceptor.java b/src/main/java/io/bosh/client/RequestLoggingInterceptor.java new file mode 100644 index 0000000..633b385 --- /dev/null +++ b/src/main/java/io/bosh/client/RequestLoggingInterceptor.java @@ -0,0 +1,33 @@ +package io.bosh.client; + +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; + +import java.io.IOException; +import java.nio.charset.Charset; + +/** + * Created by jannikheyl on 12.02.18. + */ +public class RequestLoggingInterceptor implements ClientHttpRequestInterceptor { + private final static org.slf4j.Logger log = LoggerFactory.getLogger(RequestLoggingInterceptor.class); + + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + ClientHttpResponse response = execution.execute(request, body); + + log.debug("request method: {}, request URI: {}, request headers: {}, request body: {}, response status code: {}, response headers: {}, response body: {}", + request.getMethod(), + request.getURI(), + request.getHeaders(), + new String(body, Charset.forName("UTF-8")), + response.getStatusCode(), + response.getHeaders(), + response.getBody()); + + return response; + } +} diff --git a/src/main/java/io/bosh/client/SpringDirectorClientBuilder.java b/src/main/java/io/bosh/client/SpringDirectorClientBuilder.java index 4a227b3..7bd99b8 100644 --- a/src/main/java/io/bosh/client/SpringDirectorClientBuilder.java +++ b/src/main/java/io/bosh/client/SpringDirectorClientBuilder.java @@ -24,10 +24,12 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import javax.net.ssl.SSLContext; +import org.apache.http.Header; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; @@ -40,16 +42,19 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.springframework.http.HttpRequest; import org.springframework.http.MediaType; -import org.springframework.http.client.ClientHttpRequestExecution; -import org.springframework.http.client.ClientHttpRequestFactory; -import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.*; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.security.oauth2.client.token.AccessTokenProvider; +import org.springframework.security.oauth2.client.token.AccessTokenProviderChain; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.security.oauth2.common.*; /** * @author David Ehringer @@ -59,10 +64,12 @@ public class SpringDirectorClientBuilder { private String host; private String username; private String password; - - public SpringDirectorClientBuilder withCredentials(String username, String password){ + private Authentication auth; + + public SpringDirectorClientBuilder withCredentials(String username, String password, Authentication auth){ this.username = username; this.password = password; + this.auth = auth; return this; } @@ -75,17 +82,15 @@ public SpringDirectorClient build(){ // TODO validate URI root = UriComponentsBuilder.newInstance().scheme("https").host(host).port(25555) .build().toUri(); - RestTemplate restTemplate = new RestTemplate(createRequestFactory(host, username, password)); + RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(createRequestFactory(host, username, password, auth))); restTemplate.getInterceptors().add(new ContentTypeClientHttpRequestInterceptor()); + restTemplate.getInterceptors().add(new RequestLoggingInterceptor()); handleTextHtmlResponses(restTemplate); return new SpringDirectorClient(root, restTemplate); } private ClientHttpRequestFactory createRequestFactory(String host, String username, - String password) { - CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(new AuthScope(host, 25555), - new UsernamePasswordCredentials(username, password)); + String password, Authentication auth) { SSLContext sslContext = null; try { @@ -98,10 +103,26 @@ private ClientHttpRequestFactory createRequestFactory(String host, String userna SSLConnectionSocketFactory connectionFactory = new SSLConnectionSocketFactory(sslContext, new AllowAllHostnameVerifier()); - // disabling redirect handling is critical for the way BOSH uses 302's - HttpClient httpClient = HttpClientBuilder.create().disableRedirectHandling() - .setDefaultCredentialsProvider(credentialsProvider) - .setSSLSocketFactory(connectionFactory).build(); + HttpClient httpClient; + + if(auth.equals(Authentication.BASIC)){ + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(new AuthScope(host, 25555), + new UsernamePasswordCredentials(username, password)); + + // disabling redirect handling is critical for the way BOSH uses 302's + httpClient = HttpClientBuilder.create().disableRedirectHandling() + .setDefaultCredentialsProvider(credentialsProvider) + .setSSLSocketFactory(connectionFactory).build(); + } else { + + // disabling redirect handling is critical for the way BOSH uses 302's + httpClient = HttpClientBuilder.create().disableRedirectHandling() + .setDefaultHeaders(Arrays.asList(new OAuthCredentialsProvider(host, username, password))) + .setSSLSocketFactory(connectionFactory).build(); + + } + return new HttpComponentsClientHttpRequestFactory(httpClient); } diff --git a/src/main/java/io/bosh/client/UnsecureClientCredentialsAccessTokenProvider.java b/src/main/java/io/bosh/client/UnsecureClientCredentialsAccessTokenProvider.java new file mode 100644 index 0000000..ff5f326 --- /dev/null +++ b/src/main/java/io/bosh/client/UnsecureClientCredentialsAccessTokenProvider.java @@ -0,0 +1,56 @@ +package io.bosh.client; + +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +import javax.net.ssl.SSLContext; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; + +/** + * Created by jannikheyl on 12.02.18. + */ +public class UnsecureClientCredentialsAccessTokenProvider extends ClientCredentialsAccessTokenProvider { + @Override + protected RestOperations getRestTemplate(){ + RestTemplate restOperations = (RestTemplate) super.getRestTemplate(); + TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true; + + SSLContext sslContext = null; + try { + sslContext = org.apache.http.conn.ssl.SSLContexts.custom() + .loadTrustMaterial(null, acceptingTrustStrategy) + .build(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (KeyManagementException e) { + e.printStackTrace(); + } catch (KeyStoreException e) { + e.printStackTrace(); + } + + SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext); + + CloseableHttpClient httpClient = HttpClients.custom() + .setSSLSocketFactory(csf) + .build(); + + HttpComponentsClientHttpRequestFactory requestFactory = + new HttpComponentsClientHttpRequestFactory(); + + requestFactory.setHttpClient(httpClient); + + restOperations.setRequestFactory(requestFactory); + return restOperations; + } +} diff --git a/src/test/java/io/bosh/client/AbstractDirectorTest.java b/src/test/java/io/bosh/client/AbstractDirectorTest.java index d42ad84..4bcb659 100644 --- a/src/test/java/io/bosh/client/AbstractDirectorTest.java +++ b/src/test/java/io/bosh/client/AbstractDirectorTest.java @@ -36,7 +36,7 @@ public abstract class AbstractDirectorTest { { SpringDirectorClient springClient = new SpringDirectorClientBuilder() - .withHost("192.168.50.4").withCredentials("admin", "admin").build(); + .withHost("192.168.50.4").withCredentials("admin", "admin", Authentication.BASIC).build(); mockServer = MockRestServiceServer.createServer(springClient.restTemplate()); client = springClient; } From 71d49dd84e0e10a6baa954e8160c9db127332cdd Mon Sep 17 00:00:00 2001 From: Jannik Heyl Date: Tue, 20 Feb 2018 17:38:28 +0100 Subject: [PATCH 12/13] Refactor(OAUTH): Added ability to change Port, change URL Scheme and the ability to enable strict host-key checking --- .../bosh/client/OAuthCredentialsProvider.java | 33 +++++++---- src/main/java/io/bosh/client/Scheme.java | 5 ++ .../client/SpringDirectorClientBuilder.java | 59 +++++++++++-------- .../io/bosh/client/AbstractDirectorTest.java | 4 +- 4 files changed, 64 insertions(+), 37 deletions(-) create mode 100644 src/main/java/io/bosh/client/Scheme.java diff --git a/src/main/java/io/bosh/client/OAuthCredentialsProvider.java b/src/main/java/io/bosh/client/OAuthCredentialsProvider.java index 7121d02..dca0ea6 100644 --- a/src/main/java/io/bosh/client/OAuthCredentialsProvider.java +++ b/src/main/java/io/bosh/client/OAuthCredentialsProvider.java @@ -19,6 +19,8 @@ import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.util.Assert; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Arrays; /** @@ -26,34 +28,43 @@ */ public class OAuthCredentialsProvider implements Header { private OAuth2AccessToken token; - private AccessTokenProviderChain CHAIN = new AccessTokenProviderChain( - Arrays. asList(new UnsecureClientCredentialsAccessTokenProvider())); - private ClientCredentialsResourceDetails credentials = new ClientCredentialsResourceDetails(); + private AccessTokenProviderChain CHAIN; + private ClientCredentialsResourceDetails credentials = new ClientCredentialsResourceDetails(); public OAuthCredentialsProvider(String host, String username, - String password){ - host="https://" + host + ":8443/oauth/token"; - getCredentials(host, username, password); + String password, String scheme, boolean unsecure) throws URISyntaxException { + if (unsecure) { + CHAIN = new AccessTokenProviderChain( + Arrays.asList(new UnsecureClientCredentialsAccessTokenProvider())); + } else { + CHAIN = new AccessTokenProviderChain( + Arrays.asList(new ClientCredentialsAccessTokenProvider())); + } + URI uri = new URI(scheme + host + ":8443/oauth/token"); + + getCredentials(uri.toString(), username, password); requestToken(); } - private void requestToken(){ + + private void requestToken() { if (token == null) { token = CHAIN.obtainAccessToken(credentials, new DefaultAccessTokenRequest()); - } - else if (token.isExpired()) { + } else if (token.isExpired()) { refreshAccessToken(); } } + private void refreshAccessToken() { Assert.notNull(token); token = CHAIN.refreshAccessToken(credentials, token.getRefreshToken(), new DefaultAccessTokenRequest()); } + private ClientCredentialsResourceDetails getCredentials(String host, String username, - String password){ + String password) { credentials.setAccessTokenUri(host); credentials.setClientAuthenticationScheme(AuthenticationScheme.form); credentials.setClientId(username); @@ -69,7 +80,7 @@ public String getName() { @Override public String getValue() { - return token.getTokenType()+ " "+ token.getValue(); + return token.getTokenType() + " " + token.getValue(); } @Override diff --git a/src/main/java/io/bosh/client/Scheme.java b/src/main/java/io/bosh/client/Scheme.java new file mode 100644 index 0000000..6668d47 --- /dev/null +++ b/src/main/java/io/bosh/client/Scheme.java @@ -0,0 +1,5 @@ +package io.bosh.client; + +public enum Scheme { + https,http +} diff --git a/src/main/java/io/bosh/client/SpringDirectorClientBuilder.java b/src/main/java/io/bosh/client/SpringDirectorClientBuilder.java index 7bd99b8..a05e112 100644 --- a/src/main/java/io/bosh/client/SpringDirectorClientBuilder.java +++ b/src/main/java/io/bosh/client/SpringDirectorClientBuilder.java @@ -19,17 +19,16 @@ import java.io.IOException; import java.net.URI; +import java.net.URISyntaxException; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import javax.net.ssl.SSLContext; -import org.apache.http.Header; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; @@ -46,15 +45,9 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.security.oauth2.client.token.AccessTokenProvider; -import org.springframework.security.oauth2.client.token.AccessTokenProviderChain; -import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; -import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider; -import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; -import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; -import org.springframework.security.oauth2.common.*; + /** * @author David Ehringer @@ -65,32 +58,50 @@ public class SpringDirectorClientBuilder { private String username; private String password; private Authentication auth; + private Scheme scheme; + private boolean unsecure; // Field to Disable HostnameVerifer + private Integer boshPort; - public SpringDirectorClientBuilder withCredentials(String username, String password, Authentication auth){ + public SpringDirectorClientBuilder withCredentials(String username, String password, Authentication auth, + Scheme scheme, Integer boshPort, boolean unsecure) { this.username = username; this.password = password; this.auth = auth; + this.scheme = scheme; + this.boshPort = boshPort; + this.unsecure = unsecure; return this; } - - public SpringDirectorClientBuilder withHost(String host){ + + public SpringDirectorClientBuilder withCredentials(String username, String password, Authentication auth, + Scheme scheme, Integer boshPort) { + return withCredentials(username, password, auth, scheme, boshPort, true); + } + + + public SpringDirectorClientBuilder withHost(String host) { this.host = host; return this; } - - public SpringDirectorClient build(){ + + public SpringDirectorClient build() { // TODO validate - URI root = UriComponentsBuilder.newInstance().scheme("https").host(host).port(25555) + URI root = UriComponentsBuilder.newInstance().scheme(scheme.name()).host(host).port(boshPort) .build().toUri(); - RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(createRequestFactory(host, username, password, auth))); + RestTemplate restTemplate = null; + try { + restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(createRequestFactory(host, username, password, auth, scheme, boshPort, unsecure))); + } catch (URISyntaxException e) { + e.printStackTrace(); + } restTemplate.getInterceptors().add(new ContentTypeClientHttpRequestInterceptor()); restTemplate.getInterceptors().add(new RequestLoggingInterceptor()); handleTextHtmlResponses(restTemplate); return new SpringDirectorClient(root, restTemplate); } - + private ClientHttpRequestFactory createRequestFactory(String host, String username, - String password, Authentication auth) { + String password, Authentication auth, Scheme scheme, Integer boshPort, boolean unsecure) throws URISyntaxException { SSLContext sslContext = null; try { @@ -105,9 +116,9 @@ private ClientHttpRequestFactory createRequestFactory(String host, String userna HttpClient httpClient; - if(auth.equals(Authentication.BASIC)){ + if (auth.equals(Authentication.BASIC)) { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(new AuthScope(host, 25555), + credentialsProvider.setCredentials(new AuthScope(host, boshPort), new UsernamePasswordCredentials(username, password)); // disabling redirect handling is critical for the way BOSH uses 302's @@ -118,7 +129,7 @@ private ClientHttpRequestFactory createRequestFactory(String host, String userna // disabling redirect handling is critical for the way BOSH uses 302's httpClient = HttpClientBuilder.create().disableRedirectHandling() - .setDefaultHeaders(Arrays.asList(new OAuthCredentialsProvider(host, username, password))) + .setDefaultHeaders(Arrays.asList(new OAuthCredentialsProvider(host, username, password, scheme.name(), unsecure))) .setSSLSocketFactory(connectionFactory).build(); } @@ -132,17 +143,17 @@ private void handleTextHtmlResponses(RestTemplate restTemplate) { messageConverters.add(new StringHttpMessageConverter()); MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter(); messageConverter.setSupportedMediaTypes(Arrays.asList(new MediaType("application", "json", - DEFAULT_CHARSET), new MediaType("application", "*+json", DEFAULT_CHARSET), + DEFAULT_CHARSET), new MediaType("application", "*+json", DEFAULT_CHARSET), new MediaType("text", "html", DEFAULT_CHARSET))); messageConverters.add(messageConverter); restTemplate.setMessageConverters(messageConverters); } - + private static class ContentTypeClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, - ClientHttpRequestExecution execution) throws IOException { + ClientHttpRequestExecution execution) throws IOException { ClientHttpResponse response = execution.execute(request, body); // some BOSH resources return text/plain and this modifies this response // so we can use Jackson diff --git a/src/test/java/io/bosh/client/AbstractDirectorTest.java b/src/test/java/io/bosh/client/AbstractDirectorTest.java index 4bcb659..91a166d 100644 --- a/src/test/java/io/bosh/client/AbstractDirectorTest.java +++ b/src/test/java/io/bosh/client/AbstractDirectorTest.java @@ -36,11 +36,11 @@ public abstract class AbstractDirectorTest { { SpringDirectorClient springClient = new SpringDirectorClientBuilder() - .withHost("192.168.50.4").withCredentials("admin", "admin", Authentication.BASIC).build(); + .withHost("192.168.50.4").withCredentials("admin", "admin", Authentication.BASIC, Scheme.https, 25555).build(); mockServer = MockRestServiceServer.createServer(springClient.restTemplate()); client = springClient; } - + protected String url(String url) { return "https://192.168.50.4:25555" + url; From 655aae0b3753f4bb1cfb64b9b5a85f99ab1fe0df Mon Sep 17 00:00:00 2001 From: Jannik Heyl Date: Thu, 22 Feb 2018 13:59:09 +0100 Subject: [PATCH 13/13] refactor(OAUTH): Changed Method Interface to be more modular --- .../java/io/bosh/client/Authentication.java | 8 -------- .../bosh/client/OAuthCredentialsProvider.java | 6 ++++-- src/main/java/io/bosh/client/Scheme.java | 2 +- .../io/bosh/client/SpringDirectorClient.java | 6 +++--- .../client/SpringDirectorClientBuilder.java | 20 +++++++++---------- ...eClientCredentialsAccessTokenProvider.java | 2 +- .../client/authentication/Authentication.java | 8 ++++++++ .../bosh/client/authentication/BasicAuth.java | 5 +++++ .../io/bosh/client/authentication/OAuth.java | 18 +++++++++++++++++ .../io/bosh/client/AbstractDirectorTest.java | 8 +++----- 10 files changed, 52 insertions(+), 31 deletions(-) delete mode 100644 src/main/java/io/bosh/client/Authentication.java create mode 100644 src/main/java/io/bosh/client/authentication/Authentication.java create mode 100644 src/main/java/io/bosh/client/authentication/BasicAuth.java create mode 100644 src/main/java/io/bosh/client/authentication/OAuth.java diff --git a/src/main/java/io/bosh/client/Authentication.java b/src/main/java/io/bosh/client/Authentication.java deleted file mode 100644 index e88f541..0000000 --- a/src/main/java/io/bosh/client/Authentication.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.bosh.client; - -/** - * Created by jannikheyl on 09.02.18. - */ -public enum Authentication { - BASIC,OAUTH -} diff --git a/src/main/java/io/bosh/client/OAuthCredentialsProvider.java b/src/main/java/io/bosh/client/OAuthCredentialsProvider.java index dca0ea6..92a4649 100644 --- a/src/main/java/io/bosh/client/OAuthCredentialsProvider.java +++ b/src/main/java/io/bosh/client/OAuthCredentialsProvider.java @@ -1,5 +1,7 @@ package io.bosh.client; +import io.bosh.client.authentication.Authentication; +import io.bosh.client.authentication.OAuth; import org.apache.http.Header; import org.apache.http.HeaderElement; import org.apache.http.ParseException; @@ -33,8 +35,8 @@ public class OAuthCredentialsProvider implements Header { public OAuthCredentialsProvider(String host, String username, - String password, String scheme, boolean unsecure) throws URISyntaxException { - if (unsecure) { + String password, String scheme, OAuth auth) throws URISyntaxException { + if (!auth.isStrictHostKeyChecking()) { CHAIN = new AccessTokenProviderChain( Arrays.asList(new UnsecureClientCredentialsAccessTokenProvider())); } else { diff --git a/src/main/java/io/bosh/client/Scheme.java b/src/main/java/io/bosh/client/Scheme.java index 6668d47..305a5f4 100644 --- a/src/main/java/io/bosh/client/Scheme.java +++ b/src/main/java/io/bosh/client/Scheme.java @@ -1,5 +1,5 @@ package io.bosh.client; public enum Scheme { - https,http + https, http } diff --git a/src/main/java/io/bosh/client/SpringDirectorClient.java b/src/main/java/io/bosh/client/SpringDirectorClient.java index 77b6822..a24958d 100644 --- a/src/main/java/io/bosh/client/SpringDirectorClient.java +++ b/src/main/java/io/bosh/client/SpringDirectorClient.java @@ -40,7 +40,7 @@ * @author David Ehringer */ public class SpringDirectorClient implements DirectorClient { - + private final RestTemplate restTemplate; private final Info info; @@ -63,8 +63,8 @@ public class SpringDirectorClient implements DirectorClient { this.jobs = new SpringJobs(restTemplate, root, tasks, deployments); this.vms = new SpringVms(restTemplate, root, tasks); } - - public RestTemplate restTemplate(){ + + public RestTemplate restTemplate() { return restTemplate; } diff --git a/src/main/java/io/bosh/client/SpringDirectorClientBuilder.java b/src/main/java/io/bosh/client/SpringDirectorClientBuilder.java index a05e112..461c0d2 100644 --- a/src/main/java/io/bosh/client/SpringDirectorClientBuilder.java +++ b/src/main/java/io/bosh/client/SpringDirectorClientBuilder.java @@ -29,6 +29,10 @@ import javax.net.ssl.SSLContext; +import io.bosh.client.authentication.Authentication; +import io.bosh.client.authentication.BasicAuth; +import io.bosh.client.authentication.OAuth; +import org.apache.http.auth.AUTH; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; @@ -59,24 +63,18 @@ public class SpringDirectorClientBuilder { private String password; private Authentication auth; private Scheme scheme; - private boolean unsecure; // Field to Disable HostnameVerifer private Integer boshPort; public SpringDirectorClientBuilder withCredentials(String username, String password, Authentication auth, - Scheme scheme, Integer boshPort, boolean unsecure) { + Scheme scheme, Integer boshPort) { this.username = username; this.password = password; this.auth = auth; this.scheme = scheme; this.boshPort = boshPort; - this.unsecure = unsecure; return this; } - public SpringDirectorClientBuilder withCredentials(String username, String password, Authentication auth, - Scheme scheme, Integer boshPort) { - return withCredentials(username, password, auth, scheme, boshPort, true); - } public SpringDirectorClientBuilder withHost(String host) { @@ -90,7 +88,7 @@ public SpringDirectorClient build() { .build().toUri(); RestTemplate restTemplate = null; try { - restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(createRequestFactory(host, username, password, auth, scheme, boshPort, unsecure))); + restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(createRequestFactory(host, username, password, auth, scheme, boshPort))); } catch (URISyntaxException e) { e.printStackTrace(); } @@ -101,7 +99,7 @@ public SpringDirectorClient build() { } private ClientHttpRequestFactory createRequestFactory(String host, String username, - String password, Authentication auth, Scheme scheme, Integer boshPort, boolean unsecure) throws URISyntaxException { + String password, Authentication auth, Scheme scheme, Integer boshPort) throws URISyntaxException { SSLContext sslContext = null; try { @@ -116,7 +114,7 @@ private ClientHttpRequestFactory createRequestFactory(String host, String userna HttpClient httpClient; - if (auth.equals(Authentication.BASIC)) { + if (auth.getClass().equals(BasicAuth.class)) { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(new AuthScope(host, boshPort), new UsernamePasswordCredentials(username, password)); @@ -129,7 +127,7 @@ private ClientHttpRequestFactory createRequestFactory(String host, String userna // disabling redirect handling is critical for the way BOSH uses 302's httpClient = HttpClientBuilder.create().disableRedirectHandling() - .setDefaultHeaders(Arrays.asList(new OAuthCredentialsProvider(host, username, password, scheme.name(), unsecure))) + .setDefaultHeaders(Arrays.asList(new OAuthCredentialsProvider(host, username, password, scheme.name(), (OAuth) auth))) .setSSLSocketFactory(connectionFactory).build(); } diff --git a/src/main/java/io/bosh/client/UnsecureClientCredentialsAccessTokenProvider.java b/src/main/java/io/bosh/client/UnsecureClientCredentialsAccessTokenProvider.java index ff5f326..ab51bed 100644 --- a/src/main/java/io/bosh/client/UnsecureClientCredentialsAccessTokenProvider.java +++ b/src/main/java/io/bosh/client/UnsecureClientCredentialsAccessTokenProvider.java @@ -22,7 +22,7 @@ */ public class UnsecureClientCredentialsAccessTokenProvider extends ClientCredentialsAccessTokenProvider { @Override - protected RestOperations getRestTemplate(){ + protected RestOperations getRestTemplate() { RestTemplate restOperations = (RestTemplate) super.getRestTemplate(); TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true; diff --git a/src/main/java/io/bosh/client/authentication/Authentication.java b/src/main/java/io/bosh/client/authentication/Authentication.java new file mode 100644 index 0000000..c3bad2d --- /dev/null +++ b/src/main/java/io/bosh/client/authentication/Authentication.java @@ -0,0 +1,8 @@ +package io.bosh.client.authentication; + +/** + * Created by jannikheyl on 09.02.18. + */ +public interface Authentication { + +} diff --git a/src/main/java/io/bosh/client/authentication/BasicAuth.java b/src/main/java/io/bosh/client/authentication/BasicAuth.java new file mode 100644 index 0000000..2b2fc9c --- /dev/null +++ b/src/main/java/io/bosh/client/authentication/BasicAuth.java @@ -0,0 +1,5 @@ +package io.bosh.client.authentication; + +public class BasicAuth implements Authentication { + +} diff --git a/src/main/java/io/bosh/client/authentication/OAuth.java b/src/main/java/io/bosh/client/authentication/OAuth.java new file mode 100644 index 0000000..d768d30 --- /dev/null +++ b/src/main/java/io/bosh/client/authentication/OAuth.java @@ -0,0 +1,18 @@ +package io.bosh.client.authentication; + +public class OAuth implements Authentication { + + private boolean strictHostKeyChecking; + + public OAuth(boolean strictHostKeyChecking) { + this.strictHostKeyChecking = strictHostKeyChecking; + } + + public boolean isStrictHostKeyChecking() { + return strictHostKeyChecking; + } + + public void setStrictHostKeyChecking(boolean strictHostKeyChecking) { + this.strictHostKeyChecking = strictHostKeyChecking; + } +} diff --git a/src/test/java/io/bosh/client/AbstractDirectorTest.java b/src/test/java/io/bosh/client/AbstractDirectorTest.java index 91a166d..7d0081a 100644 --- a/src/test/java/io/bosh/client/AbstractDirectorTest.java +++ b/src/test/java/io/bosh/client/AbstractDirectorTest.java @@ -18,10 +18,8 @@ import java.io.IOException; import java.io.InputStreamReader; -import io.bosh.client.DirectorClient; -import io.bosh.client.SpringDirectorClient; -import io.bosh.client.SpringDirectorClientBuilder; - +import io.bosh.client.authentication.Authentication; +import io.bosh.client.authentication.BasicAuth; import org.springframework.core.io.ClassPathResource; import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.util.FileCopyUtils; @@ -36,7 +34,7 @@ public abstract class AbstractDirectorTest { { SpringDirectorClient springClient = new SpringDirectorClientBuilder() - .withHost("192.168.50.4").withCredentials("admin", "admin", Authentication.BASIC, Scheme.https, 25555).build(); + .withHost("192.168.50.4").withCredentials("admin", "admin", new BasicAuth(), Scheme.https, 25555).build(); mockServer = MockRestServiceServer.createServer(springClient.restTemplate()); client = springClient; }