http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/machine/KubernetesEmptyMachineLocation.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/machine/KubernetesEmptyMachineLocation.java b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/machine/KubernetesEmptyMachineLocation.java new file mode 100644 index 0000000..8875ef3 --- /dev/null +++ b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/machine/KubernetesEmptyMachineLocation.java @@ -0,0 +1,68 @@ +package io.cloudsoft.amp.containerservice.kubernetes.location.machine; + +import java.net.InetAddress; +import java.util.Set; + +import org.apache.brooklyn.api.location.MachineDetails; +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.api.location.OsDetails; +import org.apache.brooklyn.location.ssh.SshMachineLocation; +import org.apache.brooklyn.util.net.Networking; + +import com.google.common.collect.ImmutableSet; + +/** + * A {@link MachineLocation} represemnting a Kubernetes resource that does not support SSH access. + * + * @see {@link KubernetesSshMachineLocation} + */ +public class KubernetesEmptyMachineLocation extends SshMachineLocation implements KubernetesMachineLocation { + + @Override + public String getHostname() { + return getResourceName(); + } + + @Override + public Set<String> getPublicAddresses() { + return ImmutableSet.of("0.0.0.0"); + } + + @Override + public Set<String> getPrivateAddresses() { + return ImmutableSet.of("0.0.0.0"); + } + + @Override + public InetAddress getAddress() { + return Networking.getInetAddressWithFixedName("0.0.0.0"); + } + + @Override + public OsDetails getOsDetails() { + return null; + // throw new UnsupportedOperationException("No OS details for empty KubernetesMachineLocation"); + } + + @Override + public MachineDetails getMachineDetails() { + return null; + // throw new UnsupportedOperationException("No machine details for empty KubernetesMachineLocation"); + } + + @Override + public String getResourceName() { + return config().get(KUBERNETES_RESOURCE_NAME); + } + + @Override + public String getResourceType() { + return config().get(KUBERNETES_RESOURCE_TYPE); + } + + @Override + public String getNamespace() { + return config().get(KUBERNETES_NAMESPACE); + } + +}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/machine/KubernetesMachineLocation.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/machine/KubernetesMachineLocation.java b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/machine/KubernetesMachineLocation.java new file mode 100644 index 0000000..6d8838b --- /dev/null +++ b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/machine/KubernetesMachineLocation.java @@ -0,0 +1,27 @@ +package io.cloudsoft.amp.containerservice.kubernetes.location.machine; + +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; + +public interface KubernetesMachineLocation extends MachineLocation { + + ConfigKey<String> KUBERNETES_NAMESPACE = ConfigKeys.builder(String.class, "kubernetes.namespace") + .description("Namespace for the KubernetesMachineLocation") + .build(); + + ConfigKey<String> KUBERNETES_RESOURCE_NAME = ConfigKeys.builder(String.class, "kubernetes.name") + .description("Name of the resource represented by the KubernetesMachineLocation") + .build(); + + ConfigKey<String> KUBERNETES_RESOURCE_TYPE = ConfigKeys.builder(String.class, "kubernetes.type") + .description("Type of the resource represented by the KubernetesMachineLocation") + .build(); + + public String getResourceName(); + + public String getResourceType(); + + public String getNamespace(); + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/machine/KubernetesSshMachineLocation.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/machine/KubernetesSshMachineLocation.java b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/machine/KubernetesSshMachineLocation.java new file mode 100644 index 0000000..ad83f8f --- /dev/null +++ b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/machine/KubernetesSshMachineLocation.java @@ -0,0 +1,28 @@ +package io.cloudsoft.amp.containerservice.kubernetes.location.machine; + +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.location.ssh.SshMachineLocation; + +/** + * A {@link MachineLocation} represemnting a Kubernetes resource that allows SSH access. + * + * @see {@link KubernetesSshMachineLocation} + */ +public class KubernetesSshMachineLocation extends SshMachineLocation implements KubernetesMachineLocation { + + @Override + public String getResourceName() { + return config().get(KUBERNETES_RESOURCE_NAME); + } + + @Override + public String getResourceType() { + return config().get(KUBERNETES_RESOURCE_TYPE); + } + + @Override + public String getNamespace() { + return config().get(KUBERNETES_NAMESPACE); + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/ImageChooserTest.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/ImageChooserTest.java b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/ImageChooserTest.java new file mode 100644 index 0000000..8f28929 --- /dev/null +++ b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/ImageChooserTest.java @@ -0,0 +1,67 @@ +package io.cloudsoft.amp.containerservice.kubernetes.location; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +public class ImageChooserTest { + + private ImageChooser chooser; + + @BeforeMethod(alwaysRun=true) + public void setUp() { + chooser = new ImageChooser(); + } + + @Test + public void testDefault() throws Exception { + assertEquals(chooser.chooseImage((String)null, null).get(), "cloudsoft/centos:7"); + } + + @Test + public void testCentos() throws Exception { + assertEquals(chooser.chooseImage("cEnToS", null).get(), "cloudsoft/centos:7"); + } + + @Test + public void testCentos7() throws Exception { + assertEquals(chooser.chooseImage("cEnToS", "7").get(), "cloudsoft/centos:7"); + } + + @Test + public void testUbnutu() throws Exception { + assertEquals(chooser.chooseImage("uBuNtU", null).get(), "cloudsoft/ubuntu:14.04"); + } + + @Test + public void testUbnutu14() throws Exception { + assertEquals(chooser.chooseImage("uBuNtU", "14.*").get(), "cloudsoft/ubuntu:14.04"); + } + + @Test + public void testUbnutu16() throws Exception { + assertEquals(chooser.chooseImage("uBuNtU", "16.*").get(), "cloudsoft/ubuntu:16.04"); + } + + @Test + public void testAbsentForCentos6() throws Exception { + assertFalse(chooser.chooseImage("cEnToS", "6").isPresent()); + } + + @Test + public void testAbsentForUbuntu15() throws Exception { + assertFalse(chooser.chooseImage("uBuNtU", "15").isPresent()); + } + + @Test + public void testAbsentForDebian() throws Exception { + assertFalse(chooser.chooseImage("debian", null).isPresent()); + } + + @Test + public void testAbsentForWrongOsFamily() throws Exception { + assertFalse(chooser.chooseImage("weirdOsFamily", null).isPresent()); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesCertsTest.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesCertsTest.java b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesCertsTest.java new file mode 100644 index 0000000..27c1b79 --- /dev/null +++ b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesCertsTest.java @@ -0,0 +1,146 @@ +package io.cloudsoft.amp.containerservice.kubernetes.location; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; + +import java.io.File; +import java.util.List; + +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.test.LogWatcher; +import org.apache.brooklyn.test.LogWatcher.EventPredicates; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.text.Identifiers; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.base.Charsets; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.io.Files; + +import ch.qos.logback.classic.spi.ILoggingEvent; + +public class KubernetesCertsTest { + + private List<File> tempFiles; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + tempFiles = Lists.newArrayList(); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + if (tempFiles != null) { + for (File tempFile : tempFiles) { + tempFile.delete(); + } + } + } + + @Test + public void testCertsAbsent() throws Exception { + ConfigBag config = ConfigBag.newInstance(); + KubernetesCerts certs = new KubernetesCerts(config); + + assertFalse(certs.caCertData.isPresent()); + assertFalse(certs.clientCertData.isPresent()); + assertFalse(certs.clientKeyData.isPresent()); + assertFalse(certs.clientKeyAlgo.isPresent()); + assertFalse(certs.clientKeyPassphrase.isPresent()); + } + + @Test + public void testCertsFromData() throws Exception { + ConfigBag config = ConfigBag.newInstance(ImmutableMap.builder() + .put(KubernetesLocationConfig.CA_CERT_DATA, "myCaCertData") + .put(KubernetesLocationConfig.CLIENT_CERT_DATA, "myClientCertData") + .put(KubernetesLocationConfig.CLIENT_KEY_DATA, "myClientKeyData") + .put(KubernetesLocationConfig.CLIENT_KEY_ALGO, "myClientKeyAlgo") + .put(KubernetesLocationConfig.CLIENT_KEY_PASSPHRASE, "myClientKeyPassphrase") + .build()); + KubernetesCerts certs = new KubernetesCerts(config); + + assertEquals(certs.caCertData.get(), "myCaCertData"); + assertEquals(certs.clientCertData.get(), "myClientCertData"); + assertEquals(certs.clientKeyData.get(), "myClientKeyData"); + assertEquals(certs.clientKeyAlgo.get(), "myClientKeyAlgo"); + assertEquals(certs.clientKeyPassphrase.get(), "myClientKeyPassphrase"); + } + + @Test + public void testCertsFromFile() throws Exception { + ConfigBag config = ConfigBag.newInstance(ImmutableMap.builder() + .put(KubernetesLocationConfig.CA_CERT_FILE, newTempFile("myCaCertData").getAbsolutePath()) + .put(KubernetesLocationConfig.CLIENT_CERT_FILE, newTempFile("myClientCertData").getAbsolutePath()) + .put(KubernetesLocationConfig.CLIENT_KEY_FILE, newTempFile("myClientKeyData").getAbsolutePath()) + .build()); + KubernetesCerts certs = new KubernetesCerts(config); + + assertEquals(certs.caCertData.get(), "myCaCertData"); + assertEquals(certs.clientCertData.get(), "myClientCertData"); + assertEquals(certs.clientKeyData.get(), "myClientKeyData"); + } + + @Test + public void testCertsFailsIfConflictingConfig() throws Exception { + ConfigBag config = ConfigBag.newInstance(ImmutableMap.builder() + .put(KubernetesLocationConfig.CA_CERT_DATA, "myCaCertData") + .put(KubernetesLocationConfig.CA_CERT_FILE, newTempFile("differentCaCertData").getAbsolutePath()) + .build()); + try { + new KubernetesCerts(config); + Asserts.shouldHaveFailedPreviously(); + } catch (Exception e) { + Asserts.expectedFailureContains(e, "Duplicate conflicting configuration for caCertData and caCertFile"); + } + } + + @Test + public void testCertsWarnsIfConflictingConfig() throws Exception { + ConfigBag config = ConfigBag.newInstance(ImmutableMap.builder() + .put(KubernetesLocationConfig.CA_CERT_DATA, "myCaCertData") + .put(KubernetesLocationConfig.CA_CERT_FILE, newTempFile("myCaCertData").getAbsolutePath()) + .build()); + + String loggerName = KubernetesCerts.class.getName(); + ch.qos.logback.classic.Level logLevel = ch.qos.logback.classic.Level.WARN; + Predicate<ILoggingEvent> filter = EventPredicates.containsMessage("Duplicate (matching) configuration for " + + "caCertData and caCertFile (continuing)"); + LogWatcher watcher = new LogWatcher(loggerName, logLevel, filter); + + watcher.start(); + KubernetesCerts certs; + try { + certs = new KubernetesCerts(config); + watcher.assertHasEvent(); + } finally { + watcher.close(); + } + + assertEquals(certs.caCertData.get(), "myCaCertData"); + } + + @Test + public void testCertsFailsIfFileNotFound() throws Exception { + ConfigBag config = ConfigBag.newInstance(ImmutableMap.builder() + .put(KubernetesLocationConfig.CA_CERT_FILE, "/path/to/fileDoesNotExist-"+Identifiers.makeRandomId(8)) + .build()); + try { + new KubernetesCerts(config); + Asserts.shouldHaveFailedPreviously(); + } catch (Exception e) { + Asserts.expectedFailureContains(e, "not found on classpath or filesystem"); + } + } + + private File newTempFile(String contents) throws Exception { + File file = File.createTempFile("KubernetesCertsTest", ".txt"); + tempFiles.add(file); + Files.write(contents, file, Charsets.UTF_8); + return file; + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationLiveTest.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationLiveTest.java b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationLiveTest.java new file mode 100644 index 0000000..4fb416f --- /dev/null +++ b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationLiveTest.java @@ -0,0 +1,226 @@ +package io.cloudsoft.amp.containerservice.kubernetes.location; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.List; +import java.util.Map; + +import org.apache.brooklyn.api.location.MachineDetails; +import org.apache.brooklyn.api.location.OsDetails; +import org.apache.brooklyn.core.location.BasicMachineDetails; +import org.apache.brooklyn.core.location.LocationConfigKeys; +import org.apache.brooklyn.core.location.access.PortForwardManager; +import org.apache.brooklyn.core.location.access.PortForwardManagerLocationResolver; +import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport; +import org.apache.brooklyn.location.ssh.SshMachineLocation; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.collections.MutableMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.net.HostAndPort; + +import io.cloudsoft.amp.containerservice.kubernetes.location.machine.KubernetesMachineLocation; +import io.cloudsoft.amp.containerservice.kubernetes.location.machine.KubernetesSshMachineLocation; + +/** +/** + * Live tests for deploying simple containers. Particularly useful during dev, but not so useful + * after that (because assumes the existence of a kubernetes endpoint). It needs configured with + * something like: + * + * {@code -Dtest.amp.kubernetes.endpoint=http://10.104.2.206:8080}). + * + * The QA Framework is more important for that - hence these tests (trying to be) kept simple + * and focused. + */ +public class KubernetesLocationLiveTest extends BrooklynAppLiveTestSupport { + + private static final Logger LOG = LoggerFactory.getLogger(KubernetesLocationLiveTest.class); + + public static final String KUBERNETES_ENDPOINT = System.getProperty("test.amp.kubernetes.endpoint", ""); + public static final String IDENTITY = System.getProperty("test.amp.kubernetes.identity", ""); + public static final String CREDENTIAL = System.getProperty("test.amp.kubernetes.credential", ""); + + protected KubernetesLocation loc; + protected List<KubernetesMachineLocation> machines; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + machines = Lists.newCopyOnWriteArrayList(); + } + + // FIXME: Clear up properly: Test leaves deployment, replicas and pods behind if obtain fails. + @AfterMethod(alwaysRun=true) + @Override + public void tearDown() throws Exception { + for (KubernetesMachineLocation machine : machines) { + try { + loc.release(machine); + } catch (Exception e) { + LOG.error("Error releasing machine "+machine+" in location "+loc, e); + } + } + super.tearDown(); + } + + protected KubernetesLocation newKubernetesLocation(Map<String, ?> flags) throws Exception { + Map<String,?> allFlags = MutableMap.<String,Object>builder() + .put("identity", IDENTITY) + .put("credential", CREDENTIAL) + .put("endpoint", KUBERNETES_ENDPOINT) + .putAll(flags) + .build(); + return (KubernetesLocation) mgmt.getLocationRegistry().getLocationManaged("kubernetes", allFlags); + } + + @Test(groups={"Live"}) + public void testDefault() throws Exception { + // Default is "cloudsoft/centos:7" + runImage(ImmutableMap.<String, Object>of(), "centos", "7"); + } + + @Test(groups={"Live"}) + public void testMatchesCentos() throws Exception { + runImage(ImmutableMap.<String, Object>of(KubernetesLocationConfig.OS_FAMILY.getName(), "centos"), "centos", "7"); + } + + @Test(groups={"Live"}) + public void testMatchesCentos7() throws Exception { + ImmutableMap<String, Object> conf = ImmutableMap.<String, Object>of( + KubernetesLocationConfig.OS_FAMILY.getName(), "centos", + KubernetesLocationConfig.OS_VERSION_REGEX.getName(), "7.*"); + runImage(conf, "centos", "7"); + } + + @Test(groups={"Live"}) + public void testMatchesUbuntu() throws Exception { + runImage(ImmutableMap.<String, Object>of(KubernetesLocationConfig.OS_FAMILY.getName(), "ubuntu"), "ubuntu", "14.04"); + } + + @Test(groups={"Live"}) + public void testMatchesUbuntu16() throws Exception { + ImmutableMap<String, Object> conf = ImmutableMap.<String, Object>of( + KubernetesLocationConfig.OS_FAMILY.getName(), "ubuntu", + KubernetesLocationConfig.OS_VERSION_REGEX.getName(), "16.*"); + runImage(conf, "ubuntu", "16.04"); + } + + @Test(groups={"Live"}) + public void testCloudsoftCentos7() throws Exception { + runImage(ImmutableMap.of(KubernetesLocationConfig.IMAGE.getName(), "cloudsoft/centos:7"), "centos", "7"); + } + + @Test(groups={"Live"}) + public void testCloudsoftUbuntu14() throws Exception { + runImage(ImmutableMap.of(KubernetesLocationConfig.IMAGE.getName(), "cloudsoft/ubuntu:14.04"), "ubuntu", "14.04"); + } + + @Test(groups={"Live"}) + public void testCloudsoftUbuntu16() throws Exception { + runImage(ImmutableMap.of(KubernetesLocationConfig.IMAGE.getName(), "cloudsoft/ubuntu:16.04"), "ubuntu", "16.04"); + } + + @Test(groups={"Live"}) + public void testFailsForNonMatching() throws Exception { + ImmutableMap<String, Object> conf = ImmutableMap.<String, Object>of( + KubernetesLocationConfig.OS_FAMILY.getName(), "weirdOsFamiliy"); + try { + runImage(conf, null, null); + Asserts.shouldHaveFailedPreviously(); + } catch (Exception e) { + Asserts.expectedFailureContains(e, "No matching image found"); + } + } + + protected void runImage(Map<String, ?> config, String expectedOs, String expectedVersion) throws Exception { + loc = newKubernetesLocation(ImmutableMap.<String, Object>of()); + SshMachineLocation machine = newContainerMachine(loc, ImmutableMap.<String, Object>builder() + .putAll(config) + .put(LocationConfigKeys.CALLER_CONTEXT.getName(), app) + .build()); + + assertTrue(machine.isSshable(), "not sshable machine="+machine); + assertOsNameContains(machine, expectedOs, expectedVersion); + assertMachinePasswordSecure(machine); + } + + @Test(groups={"Live"}) + protected void testUsesSuppliedLoginPassword() throws Exception { + // Because defaulting to "cloudsoft/centos:7", it knows to set the loginUserPassword + // on container creation. + String password = "myCustomP4ssword"; + loc = newKubernetesLocation(ImmutableMap.<String, Object>of()); + SshMachineLocation machine = newContainerMachine(loc, ImmutableMap.<String, Object>builder() + .put(KubernetesLocationConfig.LOGIN_USER_PASSWORD.getName(), password) + .put(LocationConfigKeys.CALLER_CONTEXT.getName(), app) + .build()); + + assertTrue(machine.isSshable(), "not sshable machine="+machine); + assertEquals(machine.config().get(SshMachineLocation.PASSWORD), password); + } + + @Test(groups={"Live"}) + public void testOpenPorts() throws Exception { + List<Integer> inboundPorts = ImmutableList.of(22, 443, 8000, 8081); + loc = newKubernetesLocation(ImmutableMap.<String, Object>of()); + SshMachineLocation machine = newContainerMachine(loc, ImmutableMap.<String, Object>builder() + .put(KubernetesLocationConfig.IMAGE.getName(), "cloudsoft/centos:7") + .put(KubernetesLocationConfig.LOGIN_USER_PASSWORD.getName(), "p4ssw0rd") + .put(KubernetesLocationConfig.INBOUND_PORTS.getName(), inboundPorts) + .put(LocationConfigKeys.CALLER_CONTEXT.getName(), app) + .build()); + assertTrue(machine.isSshable()); + + String publicHostText = machine.getSshHostAndPort().getHostText(); + PortForwardManager pfm = (PortForwardManager) mgmt.getLocationRegistry().getLocationManaged(PortForwardManagerLocationResolver.PFM_GLOBAL_SPEC); + for (int targetPort : inboundPorts) { + HostAndPort mappedPort = pfm.lookup(machine, targetPort); + assertNotNull(mappedPort, "no mapping for targetPort "+targetPort); + assertEquals(mappedPort.getHostText(), publicHostText); + assertTrue(mappedPort.hasPort(), "no port-part in "+mappedPort+" for targetPort "+targetPort); + } + } + + protected void assertOsNameContains(SshMachineLocation machine, String expectedNamePart, String expectedVersionPart) { + MachineDetails machineDetails = app.getExecutionContext() + .submit(BasicMachineDetails.taskForSshMachineLocation(machine)) + .getUnchecked(); + OsDetails osDetails = machineDetails.getOsDetails(); + String osName = osDetails.getName(); + String osVersion = osDetails.getVersion(); + assertTrue(osName != null && osName.toLowerCase().contains(expectedNamePart), "osDetails="+osDetails); + assertTrue(osVersion != null && osVersion.toLowerCase().contains(expectedVersionPart), "osDetails="+osDetails); + } + + protected SshMachineLocation newContainerMachine(KubernetesLocation loc, Map<?, ?> flags) throws Exception { + KubernetesMachineLocation result = loc.obtain(flags); + machines.add(result); + assertTrue(result instanceof KubernetesSshMachineLocation); + return (SshMachineLocation) result; + } + + protected void assertMachinePasswordSecure(SshMachineLocation machine) { + String password = machine.config().get(SshMachineLocation.PASSWORD); + assertTrue(password.length() > 10, "password="+password); + boolean hasUpper = false; + boolean hasLower = false; + boolean hasNonAlphabetic = false; + for (char c : password.toCharArray()) { + if (Character.isUpperCase(c)) hasUpper = true; + if (Character.isLowerCase(c)) hasLower = true; + if (!Character.isAlphabetic(c)) hasNonAlphabetic = true; + } + assertTrue(hasUpper && hasLower && hasNonAlphabetic, "password="+password); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationResolverTest.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationResolverTest.java b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationResolverTest.java new file mode 100644 index 0000000..acd366b --- /dev/null +++ b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationResolverTest.java @@ -0,0 +1,84 @@ +package io.cloudsoft.amp.containerservice.kubernetes.location; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Map; + +import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.core.internal.BrooklynProperties; +import org.apache.brooklyn.core.test.BrooklynMgmtUnitTestSupport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +public class KubernetesLocationResolverTest extends BrooklynMgmtUnitTestSupport { + + private static final Logger LOG = LoggerFactory.getLogger(KubernetesLocationResolverTest.class); + + private BrooklynProperties brooklynProperties; + + @BeforeMethod(alwaysRun = true) + public void setUp() throws Exception { + super.setUp(); + brooklynProperties = mgmt.getBrooklynProperties(); + + brooklynProperties.put("brooklyn.location.kubernetes.identity", "kubernetes-id"); + brooklynProperties.put("brooklyn.location.kubernetes.credential", "kubernetes-cred"); + } + + @Test + public void testGivesCorrectLocationType() { + LocationSpec<?> spec = getLocationSpec("kubernetes"); + assertEquals(spec.getType(), KubernetesLocation.class); + + KubernetesLocation loc = resolve("kubernetes"); + assertTrue(loc instanceof KubernetesLocation, "loc="+loc); + } + + @Test + public void testParametersInSpecString() { + KubernetesLocation loc = resolve("kubernetes(endpoint=myMasterUrl)"); + assertEquals(loc.getConfig(KubernetesLocation.MASTER_URL), "myMasterUrl"); + } + + @Test + public void testTakesDotSeparateProperty() { + brooklynProperties.put("brooklyn.location.kubernetes.endpoint", "myMasterUrl"); + KubernetesLocation loc = resolve("kubernetes"); + assertEquals(loc.getConfig(KubernetesLocation.MASTER_URL), "myMasterUrl"); + } + + @Test + public void testPropertiesPrecedence() { + // prefer those in "spec" over everything else + brooklynProperties.put("brooklyn.location.named.mykubernetes", "kubernetes:(loginUser=\"loginUser-inSpec\")"); + + brooklynProperties.put("brooklyn.location.named.mykubernetes.loginUser", "loginUser-inNamed"); + brooklynProperties.put("brooklyn.location.kubernetes.loginUser", "loginUser-inDocker"); + + // prefer those in "named" over everything else + brooklynProperties.put("brooklyn.location.named.mykubernetes.privateKeyFile", "privateKeyFile-inNamed"); + brooklynProperties.put("brooklyn.location.kubernetes.privateKeyFile", "privateKeyFile-inDocker"); + + // prefer those in kubernetes-specific + brooklynProperties.put("brooklyn.location.kubernetes.publicKeyFile", "publicKeyFile-inDocker"); + + Map<String, Object> conf = resolve("named:mykubernetes").config().getBag().getAllConfig(); + + assertEquals(conf.get("loginUser"), "loginUser-inSpec"); + assertEquals(conf.get("privateKeyFile"), "privateKeyFile-inNamed"); + assertEquals(conf.get("publicKeyFile"), "publicKeyFile-inDocker"); + } + + private LocationSpec<?> getLocationSpec(String spec) { + LOG.debug("Obtaining location spec '{}'", spec); + return mgmt.getLocationRegistry().getLocationSpec(spec).get(); + } + + private KubernetesLocation resolve(String spec) { + LOG.debug("Resolving location spec '{}'", spec); + return (KubernetesLocation) mgmt.getLocationRegistry().getLocationManaged(spec); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationYamlLiveTest.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationYamlLiveTest.java b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationYamlLiveTest.java new file mode 100644 index 0000000..6c09b16 --- /dev/null +++ b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationYamlLiveTest.java @@ -0,0 +1,518 @@ +package io.cloudsoft.amp.containerservice.kubernetes.location; + +import static com.google.common.base.Predicates.and; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.not; +import static com.google.common.base.Predicates.notNull; +import static io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationLiveTest.CREDENTIAL; +import static io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationLiveTest.IDENTITY; +import static io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationLiveTest.KUBERNETES_ENDPOINT; +import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEquals; +import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEqualsEventually; +import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEventually; +import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEventuallyNonNull; +import static org.apache.brooklyn.core.entity.EntityAsserts.assertEntityHealthy; +import static org.apache.brooklyn.test.Asserts.succeedsEventually; +import static org.apache.brooklyn.util.http.HttpAsserts.assertHttpStatusCodeEventuallyEquals; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.List; +import java.util.Map; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.api.location.MachineProvisioningLocation; +import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest; +import org.apache.brooklyn.core.entity.Attributes; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.entity.EntityPredicates; +import org.apache.brooklyn.core.location.Machines; +import org.apache.brooklyn.core.network.OnPublicNetworkEnricher; +import org.apache.brooklyn.core.sensor.Sensors; +import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess; +import org.apache.brooklyn.entity.software.base.SoftwareProcess; +import org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess; +import org.apache.brooklyn.entity.stock.BasicStartable; +import org.apache.brooklyn.location.ssh.SshMachineLocation; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.net.Networking; +import org.apache.brooklyn.util.text.Identifiers; +import org.apache.logging.log4j.util.Strings; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.net.HostAndPort; + +import io.cloudsoft.amp.containerservice.dockercontainer.DockerContainer; +import io.cloudsoft.amp.containerservice.kubernetes.entity.KubernetesPod; +import io.cloudsoft.amp.containerservice.kubernetes.entity.KubernetesResource; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.client.KubernetesClient; + +/** + * Live tests for deploying simple blueprints. Particularly useful during dev, but not so useful + * after that (because assumes the existence of a kubernetes endpoint). It needs configured with + * something like: + * + * {@code -Dtest.amp.kubernetes.endpoint=http://10.104.2.206:8080}). + * + * The QA Framework is more important for that - hence these tests (trying to be) kept simple + * and focused. + */ +public class KubernetesLocationYamlLiveTest extends AbstractYamlTest { + + protected KubernetesLocation loc; + protected List<MachineLocation> machines; + protected String locationYaml; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + + locationYaml = Joiner.on("\n").join( + "location:", + " kubernetes:", + " " + KubernetesLocationConfig.MASTER_URL.getName() + ": \"" + KUBERNETES_ENDPOINT + "\"", + " " + (Strings.isBlank(IDENTITY) ? "" : "identity: "+IDENTITY), + " " + (Strings.isBlank(CREDENTIAL) ? "" : "credential: "+CREDENTIAL)); + } + + @Test(groups={"Live"}) + public void testLoginPasswordOverride() throws Exception { + String customPassword = "myDifferentPassword"; + + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + EmptySoftwareProcess.class.getName(), + " brooklyn.config:", + " provisioning.properties:", + " " + KubernetesLocationConfig.LOGIN_USER_PASSWORD.getName() + ": " + customPassword); + + Entity app = createStartWaitAndLogApplication(yaml); + EmptySoftwareProcess entity = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, EmptySoftwareProcess.class)); + + SshMachineLocation machine = Machines.findUniqueMachineLocation(entity.getLocations(), SshMachineLocation.class).get(); + assertEquals(machine.config().get(SshMachineLocation.PASSWORD), customPassword); + assertTrue(machine.isSshable()); + } + + @Test(groups={"Live"}) + public void testNetcatServer() throws Exception { + // Runs as root user (hence not `sudo yum install ...`) + // breaks if shell.env uses attributeWhenReady, so not doing that - see testNetcatServerWithDslInShellEnv() + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + VanillaSoftwareProcess.class.getName(), + " brooklyn.parameters:", + " - name: netcat.port", + " type: port", + " default: 8081", + " brooklyn.config:", + " install.command: |", + " yum install -y nc", + " launch.command: |", + " echo $MESSAGE | nc -l $NETCAT_PORT &", + " echo $! > $PID_FILE", + " shell.env:", + " MESSAGE: mymessage", + " NETCAT_PORT: $brooklyn:attributeWhenReady(\"netcat.port\")", + " brooklyn.enrichers:", + " - type: " + OnPublicNetworkEnricher.class.getName(), + " brooklyn.config:", + " " + OnPublicNetworkEnricher.SENSORS.getName() + ":", + " - netcat.port"); + + Entity app = createStartWaitAndLogApplication(yaml); + VanillaSoftwareProcess entity = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, VanillaSoftwareProcess.class)); + + String publicMapped = assertAttributeEventuallyNonNull(entity, Sensors.newStringSensor("netcat.endpoint.mapped.public")); + HostAndPort publicPort = HostAndPort.fromString(publicMapped); + + assertTrue(Networking.isReachable(publicPort), "publicPort="+publicPort); + } + + @Test(groups={"Live"}) + public void testInterContainerNetworking() throws Exception { + String message = "mymessage"; + int netcatPort = 8081; + + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + VanillaSoftwareProcess.class.getName(), + " name: server1", + " brooklyn.parameters:", + " - name: netcat.port", + " type: port", + " default: " + netcatPort, + " brooklyn.config:", + " install.command: |", + " yum install -y nc", + " launch.command: |", + " echo " + message + " | nc -l " + netcatPort + " > netcat.out &", + " echo $! > $PID_FILE", + " - type: " + VanillaSoftwareProcess.class.getName(), + " name: server2", + " brooklyn.config:", + " install.command: |", + " yum install -y nc", + " launch.command: true", + " checkRunning.command: true"); + + Entity app = createStartWaitAndLogApplication(yaml); + Entities.dumpInfo(app); + + Entity server1 = Iterables.find(Entities.descendantsAndSelf(app), EntityPredicates.displayNameEqualTo("server1")); + Entity server2 = Iterables.find(Entities.descendantsAndSelf(app), EntityPredicates.displayNameEqualTo("server2")); + + SshMachineLocation machine1 = Machines.findUniqueMachineLocation(server1.getLocations(), SshMachineLocation.class).get(); + SshMachineLocation machine2 = Machines.findUniqueMachineLocation(server2.getLocations(), SshMachineLocation.class).get(); + + String addr1 = server1.sensors().get(Attributes.SUBNET_ADDRESS); + String addr2 = server2.sensors().get(Attributes.SUBNET_ADDRESS); + + // Ping between containers + int result1 = machine1.execCommands("ping-server2", ImmutableList.of("ping -c 4 " + addr2)); + int result2 = machine2.execCommands("ping-server1", ImmutableList.of("ping -c 4 " + addr1)); + + // Reach netcat port from other container + int result3 = machine2.execCommands("nc-to-server1", ImmutableList.of( + "echo \"fromServer2\" | nc " + addr1 + " " + netcatPort + " > netcat.out", + "cat netcat.out", + "grep " + message + " netcat.out")); + + String errMsg = "result1="+result1+"; result2="+result2+"; result3="+result3; + assertEquals(result1, 0, errMsg); + assertEquals(result2, 0, errMsg); + assertEquals(result3, 0, errMsg); + } + + @Test(groups={"Live"}) + public void testTomcatPod() throws Exception { + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + KubernetesPod.class.getName(), + " brooklyn.config:", + " docker.container.imageName: tomcat", + " docker.container.inboundPorts: [ \"8080\" ]"); + + runTomcat(yaml, KubernetesPod.class); + } + + @Test(groups={"Live"}) + public void testTomcatPodExtras() throws Exception { + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + KubernetesPod.class.getName(), + " brooklyn.config:", + " docker.container.imageName: tomcat", + " docker.container.inboundPorts: [ \"8080\" ]", + " metadata:", + " extra: test"); + + KubernetesPod entity = runTomcat(yaml, KubernetesPod.class); + + String namespace = entity.sensors().get(KubernetesPod.KUBERNETES_NAMESPACE); + String podName = entity.sensors().get(KubernetesPod.KUBERNETES_POD); + KubernetesClient client = getClient(entity); + Pod pod = client.pods().inNamespace(namespace).withName(podName).get(); + Map<String, String> labels = pod.getMetadata().getLabels(); + assertTrue(labels.containsKey("extra")); + assertEquals(labels.get("extra"), "test"); + } + + @Test(groups={"Live"}) + public void testTomcatContainer() throws Exception { + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + DockerContainer.class.getName(), + " brooklyn.config:", + " docker.container.imageName: tomcat", + " docker.container.inboundPorts: [ \"8080\" ]"); + + runTomcat(yaml, DockerContainer.class); + } + + /** + * Assumes that the container entity uses port 8080. + */ + protected <T extends Entity> T runTomcat(String yaml, Class<T> type) throws Exception { + Entity app = createStartWaitAndLogApplication(yaml); + T entity = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, type)); + + Entities.dumpInfo(app); + String publicMapped = assertAttributeEventuallyNonNull(entity, Sensors.newStringSensor("docker.port.8080.mapped.public")); + HostAndPort publicPort = HostAndPort.fromString(publicMapped); + + assertReachableEventually(publicPort); + assertHttpStatusCodeEventuallyEquals("http://"+publicPort.getHostText()+":"+publicPort.getPort(), 200); + + return entity; + } + + @Test(groups={"Live"}) + public void testWordpressInContainersWithStartableParent() throws Exception { + // TODO docker.container.inboundPorts doesn't accept list of ints - need to use quotes + String randomId = Identifiers.makeRandomLowercaseId(4); + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + BasicStartable.class.getName(), + " brooklyn.children:", + " - type: " + DockerContainer.class.getName(), + " id: wordpress-mysql", + " name: mysql", + " brooklyn.config:", + " docker.container.imageName: mysql:5.6", + " docker.container.inboundPorts:", + " - \"3306\"", + " docker.container.environment:", + " MYSQL_ROOT_PASSWORD: \"password\"", + " provisioning.properties:", + " deployment: wordpress-mysql-" + randomId, + " - type: " + DockerContainer.class.getName(), + " id: wordpress", + " name: wordpress", + " brooklyn.config:", + " docker.container.imageName: wordpress:4-apache", + " docker.container.inboundPorts:", + " - \"80\"", + " docker.container.environment:", + " WORDPRESS_DB_HOST: \"wordpress-mysql-" + randomId + "\"", + " WORDPRESS_DB_PASSWORD: \"password\"", + " provisioning.properties:", + " deployment: wordpress-" + randomId); + + runWordpress(yaml, randomId); + } + + @Test(groups={"Live"}) + public void testWordpressInPodsWithStartableParent() throws Exception { + // TODO docker.container.inboundPorts doesn't accept list of ints - need to use quotes + String randomId = Identifiers.makeRandomLowercaseId(4); + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + BasicStartable.class.getName(), + " brooklyn.children:", + " - type: " + KubernetesPod.class.getName(), + " id: wordpress-mysql", + " name: mysql", + " brooklyn.config:", + " docker.container.imageName: mysql:5.6", + " docker.container.inboundPorts:", + " - \"3306\"", + " docker.container.environment:", + " MYSQL_ROOT_PASSWORD: \"password\"", + " deployment: wordpress-mysql-" + randomId, + " - type: " + KubernetesPod.class.getName(), + " id: wordpress", + " name: wordpress", + " brooklyn.config:", + " docker.container.imageName: wordpress:4-apache", + " docker.container.inboundPorts:", + " - \"80\"", + " docker.container.environment:", + " WORDPRESS_DB_HOST: \"wordpress-mysql-" + randomId + "\"", + " WORDPRESS_DB_PASSWORD: \"password\"", + " deployment: wordpress-" + randomId); + + runWordpress(yaml, randomId); + } + + @Test(groups={"Live"}) + public void testWordpressInPods() throws Exception { + // TODO docker.container.inboundPorts doesn't accept list of ints - need to use quotes + String randomId = Identifiers.makeRandomLowercaseId(4); + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + KubernetesPod.class.getName(), + " id: wordpress-mysql", + " name: mysql", + " brooklyn.config:", + " docker.container.imageName: mysql:5.6", + " docker.container.inboundPorts:", + " - \"3306\"", + " docker.container.environment:", + " MYSQL_ROOT_PASSWORD: \"password\"", + " deployment: wordpress-mysql-" + randomId, + " - type: " + KubernetesPod.class.getName(), + " id: wordpress", + " name: wordpress", + " brooklyn.config:", + " docker.container.imageName: wordpress:4-apache", + " docker.container.inboundPorts:", + " - \"80\"", + " docker.container.environment:", + " WORDPRESS_DB_HOST: \"wordpress-mysql-" + randomId + "\"", + " WORDPRESS_DB_PASSWORD: \"password\"", + " deployment: wordpress-" + randomId); + + runWordpress(yaml, randomId); + } + + /** + * Assumes that the {@link DockerContainer} entities have display names of "mysql" and "wordpress", + * and that they use ports 3306 and 80 respectively. + */ + protected void runWordpress(String yaml, String randomId) throws Exception { + Entity app = createStartWaitAndLogApplication(yaml); + Entities.dumpInfo(app); + + Iterable<DockerContainer> containers = Entities.descendantsAndSelf(app, DockerContainer.class); + DockerContainer mysql = Iterables.find(containers, EntityPredicates.displayNameEqualTo("mysql")); + DockerContainer wordpress = Iterables.find(containers, EntityPredicates.displayNameEqualTo("wordpress")); + + String mysqlPublicPort = assertAttributeEventuallyNonNull(mysql, Sensors.newStringSensor("docker.port.3306.mapped.public")); + assertReachableEventually(HostAndPort.fromString(mysqlPublicPort)); + assertAttributeEquals(mysql, KubernetesPod.KUBERNETES_NAMESPACE, "amp"); + assertAttributeEquals(mysql, KubernetesPod.KUBERNETES_SERVICE, "wordpress-mysql-" + randomId); + + String wordpressPublicPort = assertAttributeEventuallyNonNull(wordpress, Sensors.newStringSensor("docker.port.80.mapped.public")); + assertReachableEventually(HostAndPort.fromString(wordpressPublicPort)); + assertAttributeEquals(wordpress, KubernetesPod.KUBERNETES_NAMESPACE, "amp"); + assertAttributeEquals(wordpress, KubernetesPod.KUBERNETES_SERVICE, "wordpress-" + randomId); + + // TODO more assertions (e.g. wordpress can successfully reach the database) + } + + @Test(groups={"Live"}) + public void testPod() throws Exception { + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + KubernetesPod.class.getName(), + " brooklyn.config:", + " docker.container.imageName: tomcat", + " docker.container.inboundPorts:", + " - \"8080\"", + " shell.env:", + " CLUSTER_ID: \"id\"", + " CLUSTER_TOKEN: \"token\""); + + Entity app = createStartWaitAndLogApplication(yaml); + checkPod(app, KubernetesPod.class); + } + + /* Test disabled as QA framework AMP does not have catalog entries deployed yet */ + @Test(groups={"Live"}, enabled=false) + public void testPodCatalogEntry() throws Exception { + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: kubernetes-pod-entity", + " brooklyn.config:", + " docker.container.imageName: tomcat", + " docker.container.inboundPorts:", + " - \"8080\"", + " shell.env:", + " CLUSTER_ID: \"id\"", + " CLUSTER_TOKEN: \"token\""); + + Entity app = createStartWaitAndLogApplication(yaml); + checkPod(app, KubernetesPod.class); + } + + protected <T extends Entity> void checkPod(Entity app, Class<T> type) { + T container = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, type)); + + Entities.dumpInfo(app); + + String publicMapped = assertAttributeEventuallyNonNull(container, Sensors.newStringSensor("docker.port.8080.mapped.public")); + HostAndPort publicPort = HostAndPort.fromString(publicMapped); + + assertReachableEventually(publicPort); + assertHttpStatusCodeEventuallyEquals("http://"+publicPort.getHostText()+":"+publicPort.getPort(), 200); + } + + @Test(groups={"Live"}) + public void testNginxReplicationController() throws Exception { + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + KubernetesResource.class.getName(), + " id: nginx-replication-controller", + " name: \"nginx-replication-controller\"", + " brooklyn.config:", + " resource: classpath://nginx-replication-controller.yaml"); + + Entity app = createStartWaitAndLogApplication(yaml); + checkNginxResource(app, KubernetesResource.class); + } + + protected <T extends Entity> void checkNginxResource(Entity app, Class<T> type) { + T entity = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, type)); + + Entities.dumpInfo(app); + + assertEntityHealthy(entity); + assertAttributeEqualsEventually(entity, KubernetesResource.RESOURCE_NAME, "nginx-replication-controller"); + assertAttributeEqualsEventually(entity, KubernetesResource.RESOURCE_TYPE, "ReplicationController"); + assertAttributeEqualsEventually(entity, KubernetesResource.KUBERNETES_NAMESPACE, "default"); + assertAttributeEventually(entity, SoftwareProcess.ADDRESS, and(notNull(), not(equalTo("0.0.0.0")))); + assertAttributeEventually(entity, SoftwareProcess.SUBNET_ADDRESS, and(notNull(), not(equalTo("0.0.0.0")))); + } + + @Test(groups={"Live"}) + public void testNginxService() throws Exception { + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + KubernetesResource.class.getName(), + " id: nginx-replication-controller", + " name: \"nginx-replication-controller\"", + " brooklyn.config:", + " resource: classpath://nginx-replication-controller.yaml", + " - type: " + KubernetesResource.class.getName(), + " id: nginx-service", + " name: \"nginx-service\"", + " brooklyn.config:", + " resource: classpath://nginx-service.yaml"); + Entity app = createStartWaitAndLogApplication(yaml); + + Iterable<KubernetesResource> resources = Entities.descendantsAndSelf(app, KubernetesResource.class); + KubernetesResource nginxReplicationController = Iterables.find(resources, EntityPredicates.displayNameEqualTo("nginx-replication-controller")); + KubernetesResource nginxService = Iterables.find(resources, EntityPredicates.displayNameEqualTo("nginx-service")); + + assertEntityHealthy(nginxReplicationController); + assertEntityHealthy(nginxService); + + Entities.dumpInfo(app); + + Integer httpPort = assertAttributeEventuallyNonNull(nginxService, Sensors.newIntegerSensor("kubernetes.http.port")); + assertEquals(httpPort, Integer.valueOf(80)); + String httpPublicPort = assertAttributeEventuallyNonNull(nginxService, Sensors.newStringSensor("kubernetes.http.endpoint.mapped.public")); + assertReachableEventually(HostAndPort.fromString(httpPublicPort)); + } + + protected void assertReachableEventually(final HostAndPort hostAndPort) { + succeedsEventually(new Runnable() { + public void run() { + assertTrue(Networking.isReachable(hostAndPort), "publicPort="+hostAndPort); + }}); + } + + public KubernetesClient getClient(Entity entity) { + MachineProvisioningLocation location = entity.sensors().get(SoftwareProcess.PROVISIONING_LOCATION); + if (location instanceof KubernetesLocation) { + KubernetesLocation kubernetes = (KubernetesLocation) location; + ConfigBag config = kubernetes.config().getBag(); + KubernetesClientRegistry registry = kubernetes.config().get(KubernetesLocationConfig.KUBERNETES_CLIENT_REGISTRY); + KubernetesClient client = registry.getKubernetesClient(config); + return client; + } + throw new IllegalStateException("Cannot find KubernetesLocation on entity: " + Iterables.toString(entity.getLocations())); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPod.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPod.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPod.java deleted file mode 100644 index bf5444d..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPod.java +++ /dev/null @@ -1,82 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.entity; - -import java.util.List; -import java.util.Map; - -import org.apache.brooklyn.api.entity.ImplementedBy; -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.BasicConfigInheritance; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.core.config.MapConfigKey; -import org.apache.brooklyn.core.sensor.Sensors; -import org.apache.brooklyn.util.math.MathPredicates; - -import com.google.common.collect.ImmutableMap; -import com.google.common.reflect.TypeToken; - -import io.cloudsoft.amp.containerservice.dockercontainer.DockerContainer; -import io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationConfig; - -@ImplementedBy(KubernetesPodImpl.class) -public interface KubernetesPod extends DockerContainer { - - ConfigKey<String> NAMESPACE = KubernetesLocationConfig.NAMESPACE; - - ConfigKey<Boolean> PRIVILEGED = KubernetesLocationConfig.PRIVILEGED; - - @SuppressWarnings("serial") - ConfigKey<List<String>> PERSISTENT_VOLUMES = ConfigKeys.builder(new TypeToken<List<String>>() {}) - .name("persistentVolumes") - .description("Persistent volumes used by the pod") - .build(); - - ConfigKey<String> DEPLOYMENT = ConfigKeys.builder(String.class) - .name("deployment") - .description("The name of the service the deployed pod will use") - .build(); - - ConfigKey<Integer> REPLICAS = ConfigKeys.builder(Integer.class) - .name("replicas") - .description("Number of replicas in the pod") - .constraint(MathPredicates.greaterThanOrEqual(1d)) - .defaultValue(1) - .build(); - - @SuppressWarnings("serial") - ConfigKey<Map<String, String>> SECRETS = ConfigKeys.builder(new TypeToken<Map<String, String>>() {}) - .name("secrets") - .description("Secrets to be added to the pod") - .build(); - - @SuppressWarnings("serial") - ConfigKey<Map<String, String>> LIMITS = ConfigKeys.builder(new TypeToken<Map<String, String>>() {}) - .name("limits") - .description("Container resource limits for the pod") - .build(); - - MapConfigKey<Object> METADATA = new MapConfigKey.Builder<Object>(Object.class, "metadata") - .description("Metadata to set on the pod") - .defaultValue(ImmutableMap.<String, Object>of()) - .typeInheritance(BasicConfigInheritance.DEEP_MERGE) - .runtimeInheritance(BasicConfigInheritance.NOT_REINHERITED_ELSE_DEEP_MERGE) - .build(); - - AttributeSensor<String> KUBERNETES_DEPLOYMENT = Sensors.builder(String.class, "kubernetes.deployment") - .description("Deployment resources run in") - .build(); - - AttributeSensor<String> KUBERNETES_NAMESPACE = Sensors.builder(String.class, "kubernetes.namespace") - .description("Namespace that resources run in") - .build(); - - AttributeSensor<String> KUBERNETES_SERVICE = Sensors.builder(String.class, "kubernetes.service") - .description("Service that exposes the deployment") - .build(); - - AttributeSensor<String> KUBERNETES_POD = Sensors.builder(String.class, "kubernetes.pod") - .description("Pod running the deployment") - .build(); - - String EMPTY = "Empty"; -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPodImpl.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPodImpl.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPodImpl.java deleted file mode 100644 index 2acf734..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPodImpl.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.entity; - -import io.cloudsoft.amp.containerservice.dockercontainer.DockerContainerImpl; - -public class KubernetesPodImpl extends DockerContainerImpl implements KubernetesPod { - -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResource.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResource.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResource.java deleted file mode 100644 index 320c924..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResource.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.entity; - -import org.apache.brooklyn.api.entity.ImplementedBy; -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.core.sensor.Sensors; -import org.apache.brooklyn.entity.software.base.SoftwareProcess; -import org.apache.brooklyn.util.core.ResourcePredicates; - -@ImplementedBy(KubernetesResourceImpl.class) -public interface KubernetesResource extends SoftwareProcess { - - ConfigKey<String> RESOURCE_FILE = ConfigKeys.builder(String.class) - .name("resource") - .description("Kubernetes resource YAML file URI") - .constraint(ResourcePredicates.urlExists()) - .build(); - - AttributeSensor<String> RESOURCE_TYPE = Sensors.builder(String.class, "kubernetes.resource.type") - .description("Kubernetes resource type") - .build(); - - AttributeSensor<String> RESOURCE_NAME = Sensors.builder(String.class, "kubernetes.resource.name") - .description("Kubernetes resource name") - .build(); - - AttributeSensor<String> KUBERNETES_NAMESPACE = KubernetesPod.KUBERNETES_NAMESPACE; - - String POD = "Pod"; - String DEPLOYMENT = "Deployment"; - String REPLICA_SET = "ReplicaSet"; - String CONFIG_MAP = "ConfigMap"; - String PERSISTENT_VOLUME = "PersistentVolume"; - String SECRET = "Secret"; - String SERVICE = "Service"; - String REPLICATION_CONTROLLER = "ReplicationController"; - String NAMESPACE = "Namespace"; - -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResourceImpl.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResourceImpl.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResourceImpl.java deleted file mode 100644 index 3e7dad3..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResourceImpl.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.entity; - -import org.apache.brooklyn.core.entity.BrooklynConfigKeys; -import org.apache.brooklyn.entity.software.base.EmptySoftwareProcessImpl; - -public class KubernetesResourceImpl extends EmptySoftwareProcessImpl implements KubernetesResource { - - @Override - public void init() { - super.init(); - - config().set(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true); - config().set(PROVISIONING_PROPERTIES.subKey("useJcloudsSshInit"), false); - config().set(PROVISIONING_PROPERTIES.subKey("waitForSshable"), false); - config().set(PROVISIONING_PROPERTIES.subKey("pollForFirstReachableAddress"), false); - config().set(EmptySoftwareProcessImpl.USE_SSH_MONITORING, false); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/ImageChooser.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/ImageChooser.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/ImageChooser.java deleted file mode 100644 index 1bda432..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/ImageChooser.java +++ /dev/null @@ -1,71 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.location; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.List; - -import javax.annotation.Nullable; - -import org.jclouds.compute.domain.OsFamily; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableList; - -public class ImageChooser { - - private static final Logger LOG = LoggerFactory.getLogger(ImageChooser.class); - - public static class ImageMetadata { - private final OsFamily osFamily; - private final String osVersion; - private final String imageName; - - public ImageMetadata(OsFamily osFamily, String osVersion, String imageName) { - this.osFamily = checkNotNull(osFamily, "osFamily"); - this.osVersion = checkNotNull(osVersion, "osVersion"); - this.imageName = checkNotNull(imageName, "imageName"); - } - - public boolean matches(@Nullable OsFamily osFamily, @Nullable String osVersionRegex) { - if (osFamily != null && osFamily != this.osFamily) return false; - if (osVersionRegex != null && !osVersion.matches(osVersionRegex)) return false; - return true; - } - - public String getImageName() { - return imageName; - } - } - - private static final List<ImageMetadata> DEFAULT_IMAGES = ImmutableList.of( - new ImageMetadata(OsFamily.CENTOS, "7", "cloudsoft/centos:7"), - new ImageMetadata(OsFamily.UBUNTU, "14.04", "cloudsoft/ubuntu:14.04"), - new ImageMetadata(OsFamily.UBUNTU, "16.04", "cloudsoft/ubuntu:16.04")); - - private final List<ImageMetadata> images; - - public ImageChooser() { - this.images = DEFAULT_IMAGES; - } - - public ImageChooser(List<? extends ImageMetadata> images) { - this.images = ImmutableList.copyOf(images); - } - - public Optional<String> chooseImage(String osFamily, String osVersionRegex) { - return chooseImage((osFamily == null ? (OsFamily)null : OsFamily.fromValue(osFamily)), osVersionRegex); - } - - public Optional<String> chooseImage(OsFamily osFamily, String osVersionRegex) { - for (ImageMetadata imageMetadata : images) { - if (imageMetadata.matches(osFamily, osVersionRegex)) { - String imageName = imageMetadata.getImageName(); - LOG.debug("Choosing container image {}, for osFamily={} and osVersionRegex={}", new Object[] {imageName, osFamily, osVersionRegex}); - return Optional.of(imageName); - } - } - return Optional.absent(); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesCerts.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesCerts.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesCerts.java deleted file mode 100644 index 9bb840f..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesCerts.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.location; - -import static io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationConfig.CA_CERT_DATA; -import static io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationConfig.CA_CERT_FILE; -import static io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationConfig.CLIENT_CERT_DATA; -import static io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationConfig.CLIENT_CERT_FILE; -import static io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationConfig.CLIENT_KEY_ALGO; -import static io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationConfig.CLIENT_KEY_DATA; -import static io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationConfig.CLIENT_KEY_FILE; -import static io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationConfig.CLIENT_KEY_PASSPHRASE; - -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.util.core.ResourceUtils; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.text.Strings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Optional; - -class KubernetesCerts { - - private static final Logger LOG = LoggerFactory.getLogger(KubernetesCerts.class); - - public final Optional<String> caCertData; - public final Optional<String> clientCertData; - public final Optional<String> clientKeyData; - public final Optional<String> clientKeyAlgo; - public final Optional<String> clientKeyPassphrase; - - public KubernetesCerts(ConfigBag config) { - caCertData = getData(CA_CERT_DATA, CA_CERT_FILE, config); - clientCertData = getData(CLIENT_CERT_DATA, CLIENT_CERT_FILE, config); - clientKeyData = getData(CLIENT_KEY_DATA, CLIENT_KEY_FILE, config); - clientKeyAlgo = getNonBlankOptional(CLIENT_KEY_ALGO, config); - clientKeyPassphrase = getNonBlankOptional(CLIENT_KEY_PASSPHRASE, config); - } - - protected Optional<String> getData(ConfigKey<String> dataKey, ConfigKey<String> fileKey, ConfigBag config) { - String data = Strings.isNonBlank(config.get(dataKey)) ? config.get(dataKey).trim() : null; - String file = config.get(fileKey); - String fileData = Strings.isNonBlank(file) ? getFileContents(file).trim() : null; - - if (Strings.isNonBlank(data) && Strings.isNonBlank(fileData)) { - if (data.equals(fileData)) { - LOG.warn("Duplicate (matching) configuration for " + dataKey.getName() + " and " + fileKey.getName() + " (continuing)"); - } else { - throw new IllegalStateException("Duplicate conflicting configuration for " + dataKey.getName() + " and " + fileKey.getName()); - } - } - - String result = Strings.isNonBlank(data) ? data : (Strings.isNonBlank(fileData) ? fileData : null); - return Optional.fromNullable(result); - } - - protected Optional<String> getNonBlankOptional(ConfigKey<? extends String> key, ConfigBag config) { - String result = config.get(key); - return Optional.fromNullable(Strings.isNonBlank(result) ? result : null); - } - - protected String getFileContents(String file) { - return ResourceUtils.create(this).getResourceAsString(file); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesClientRegistry.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesClientRegistry.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesClientRegistry.java deleted file mode 100644 index c240378..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesClientRegistry.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.location; - -import org.apache.brooklyn.util.core.config.ConfigBag; - -import io.fabric8.kubernetes.client.KubernetesClient; - -public interface KubernetesClientRegistry { - - KubernetesClient getKubernetesClient(ConfigBag conf); - -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesClientRegistryImpl.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesClientRegistryImpl.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesClientRegistryImpl.java deleted file mode 100644 index 32bfee6..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesClientRegistryImpl.java +++ /dev/null @@ -1,78 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.location; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.net.MalformedURLException; -import java.net.URL; - -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.text.Strings; -import org.apache.brooklyn.util.time.Duration; - -import com.google.common.base.Throwables; -import com.google.common.io.BaseEncoding; - -import io.fabric8.kubernetes.client.ConfigBuilder; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClient; - -public class KubernetesClientRegistryImpl implements KubernetesClientRegistry { - - public static final KubernetesClientRegistryImpl INSTANCE = new KubernetesClientRegistryImpl(); - - @Override - public KubernetesClient getKubernetesClient(ConfigBag conf) { - String masterUrl = checkNotNull(conf.get(KubernetesLocationConfig.MASTER_URL), "master url must not be null"); - - URL url; - try { - url = new URL(masterUrl); - } catch (MalformedURLException e) { - throw Throwables.propagate(e); - } - - ConfigBuilder configBuilder = new ConfigBuilder() - .withMasterUrl(masterUrl) - .withTrustCerts(false); - - if (url.getProtocol().equals("https")) { - KubernetesCerts certs = new KubernetesCerts(conf); - if (certs.caCertData.isPresent()) configBuilder.withCaCertData(toBase64Encoding(certs.caCertData.get())); - if (certs.clientCertData.isPresent()) configBuilder.withClientCertData(toBase64Encoding(certs.clientCertData.get())); - if (certs.clientKeyData.isPresent()) configBuilder.withClientKeyData(toBase64Encoding(certs.clientKeyData.get())); - if (certs.clientKeyAlgo.isPresent()) configBuilder.withClientKeyAlgo(certs.clientKeyAlgo.get()); - if (certs.clientKeyPassphrase.isPresent()) configBuilder.withClientKeyPassphrase(certs.clientKeyPassphrase.get()); - // TODO Should we also set configBuilder.withTrustCerts(true) here? - } - - String username = conf.get(KubernetesLocationConfig.ACCESS_IDENTITY); - if (Strings.isNonBlank(username)) configBuilder.withUsername(username); - - String password = conf.get(KubernetesLocationConfig.ACCESS_CREDENTIAL); - if (Strings.isNonBlank(password)) configBuilder.withPassword(password); - - String token = conf.get(KubernetesLocationConfig.OAUTH_TOKEN); - if (Strings.isNonBlank(token)) configBuilder.withOauthToken(token); - - Duration clientTimeout = conf.get(KubernetesLocationConfig.CLIENT_TIMEOUT); - if (clientTimeout.isPositive()) { - configBuilder.withConnectionTimeout((int) clientTimeout.toMilliseconds()); - configBuilder.withRequestTimeout((int) clientTimeout.toMilliseconds()); - } else { - throw new IllegalArgumentException("Kubernetes client timeout should be a positive duration: " + clientTimeout.toString()); - } - Duration actionTimeout = conf.get(KubernetesLocationConfig.ACTION_TIMEOUT); - if (actionTimeout.isPositive()) { - configBuilder.withRollingTimeout(actionTimeout.toMilliseconds()); - configBuilder.withScaleTimeout(actionTimeout.toMilliseconds()); - } else { - throw new IllegalArgumentException("Kubernetes action timeout should be a positive duration: " + actionTimeout.toString()); - } - - return new DefaultKubernetesClient(configBuilder.build()); - } - - private String toBase64Encoding(String val) { - return BaseEncoding.base64().encode(val.getBytes()); - } -}
