Repository: brooklyn-server Updated Branches: refs/heads/master 12e6b0045 -> 0debf391d
Add support for kubernetes helm - bump microbean-helm version - support default stable ChartRepository - use auto-generated release name Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/6ddc595d Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/6ddc595d Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/6ddc595d Branch: refs/heads/master Commit: 6ddc595dd549e13343fc4896fc05224c420ed136 Parents: 12e6b00 Author: Andrea Turli <andrea.tu...@gmail.com> Authored: Mon Aug 21 15:21:52 2017 +0200 Committer: andreaturli <andrea.tu...@gmail.com> Committed: Mon Mar 12 11:06:27 2018 +0100 ---------------------------------------------------------------------- locations/container/pom.xml | 88 ++++++- .../entity/kubernetes/KubernetesHelmChart.java | 37 +++ .../kubernetes/KubernetesHelmChartImpl.java | 35 +++ .../location/kubernetes/KubernetesLocation.java | 258 ++++++++++++++----- .../container/supplier/TillerSupplier.java | 43 ++++ .../kubernetes/KubernetesLocationLiveTest.java | 2 + .../KubernetesLocationYamlLiveTest.java | 24 ++ 7 files changed, 417 insertions(+), 70 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6ddc595d/locations/container/pom.xml ---------------------------------------------------------------------- diff --git a/locations/container/pom.xml b/locations/container/pom.xml index 6bd4be1..da1434b 100644 --- a/locations/container/pom.xml +++ b/locations/container/pom.xml @@ -32,11 +32,43 @@ </parent> <properties> - <kubernetes-client.version>1.4.27</kubernetes-client.version> + <kubernetes-client.version>2.5.6</kubernetes-client.version> </properties> <dependencies> <dependency> + <groupId>org.microbean</groupId> + <artifactId>microbean-helm</artifactId> + <version>2.7.2.1.0.0</version> + <exclusions> + <exclusion> + <groupId>com.google.api.grpc</groupId> + <artifactId>proto-google-common-protos</artifactId> + </exclusion> + <exclusion> + <groupId>com.google.errorprone</groupId> + <artifactId>error_prone_annotations</artifactId> + </exclusion> + <exclusion> + <groupId>io.grpc</groupId> + <artifactId>grpc-context</artifactId> + </exclusion> + </exclusions> + </dependency> + + + <dependency> + <groupId>com.google.errorprone</groupId> + <artifactId>error_prone_annotations</artifactId> + <version>2.0.19</version> + </dependency> + <dependency> + <groupId>io.grpc</groupId> + <artifactId>grpc-context</artifactId> + <version>1.4.0</version> + </dependency> + + <dependency> <groupId>io.fabric8</groupId> <artifactId>kubernetes-client</artifactId> <version>${kubernetes-client.version}</version> @@ -69,6 +101,10 @@ <groupId>javax.ws.rs</groupId> <artifactId>jsr311-api</artifactId> </exclusion> + <exclusion> + <groupId>com.squareup.okio</groupId> + <artifactId>okio</artifactId> + </exclusion> </exclusions> </dependency> @@ -88,7 +124,20 @@ <groupId>org.apache.brooklyn</groupId> <artifactId>brooklyn-locations-jclouds</artifactId> <version>${brooklyn.version}</version> + <exclusions> + <exclusion> + <groupId>com.squareup.okio</groupId> + <artifactId>okio</artifactId> + </exclusion> + </exclusions> + </dependency> + + <dependency> + <groupId>com.squareup.okio</groupId> + <artifactId>okio</artifactId> + <version>1.13.0</version> </dependency> + <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> @@ -152,4 +201,41 @@ <scope>test</scope> </dependency> </dependencies> + + <build> + <extensions> + <!-- Use os-maven-plugin to initialize the "os.detected" properties --> + <extension> + <groupId>kr.motd.maven</groupId> + <artifactId>os-maven-plugin</artifactId> + <version>1.5.0.Final</version> + </extension> + </extensions> + <plugins> + <!-- Use Ant to configure the appropriate "tcnative.classifier" property --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <execution> + <phase>initialize</phase> + <configuration> + <exportAntProperties>true</exportAntProperties> + <target> + <condition property="tcnative.classifier" + value="${os.detected.classifier}-fedora" + else="${os.detected.classifier}"> + <isset property="os.detected.release.fedora"/> + </condition> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </project> http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6ddc595d/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesHelmChart.java ---------------------------------------------------------------------- diff --git a/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesHelmChart.java b/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesHelmChart.java new file mode 100644 index 0000000..e44a4bc --- /dev/null +++ b/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesHelmChart.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.brooklyn.container.entity.kubernetes; + +import org.apache.brooklyn.api.entity.ImplementedBy; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.entity.software.base.SoftwareProcess; +import org.apache.brooklyn.util.core.ResourcePredicates; + +@ImplementedBy(KubernetesHelmChartImpl.class) +public interface KubernetesHelmChart extends SoftwareProcess { + + ConfigKey<String> CHART_NAME = ConfigKeys.builder(String.class) + .name("chartName") + .description("Helm Chart name") + .build(); + + ConfigKey<String> CHART_VERSION = ConfigKeys.builder(String.class) + .name("chartVersion") + .description("Helm Chart version") + .build(); +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6ddc595d/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesHelmChartImpl.java ---------------------------------------------------------------------- diff --git a/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesHelmChartImpl.java b/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesHelmChartImpl.java new file mode 100644 index 0000000..9d3484e --- /dev/null +++ b/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesHelmChartImpl.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.brooklyn.container.entity.kubernetes; + +import org.apache.brooklyn.core.entity.BrooklynConfigKeys; +import org.apache.brooklyn.entity.software.base.EmptySoftwareProcessImpl; + +public class KubernetesHelmChartImpl extends EmptySoftwareProcessImpl implements KubernetesHelmChart { + + @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/6ddc595d/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocation.java ---------------------------------------------------------------------- diff --git a/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocation.java b/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocation.java index 1c62b6b..0b6c2b5 100644 --- a/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocation.java +++ b/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocation.java @@ -18,69 +18,13 @@ */ package org.apache.brooklyn.container.location.kubernetes; -import java.io.InputStream; -import java.net.InetAddress; -import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nullable; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.location.LocationSpec; -import org.apache.brooklyn.api.location.MachineLocation; -import org.apache.brooklyn.api.location.MachineProvisioningLocation; -import org.apache.brooklyn.api.location.NoMachinesAvailableException; -import org.apache.brooklyn.api.location.PortRange; -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.api.sensor.EnricherSpec; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.container.entity.docker.DockerContainer; -import org.apache.brooklyn.container.entity.kubernetes.KubernetesPod; -import org.apache.brooklyn.container.entity.kubernetes.KubernetesResource; -import org.apache.brooklyn.container.location.docker.DockerJcloudsLocation; -import org.apache.brooklyn.container.location.kubernetes.machine.KubernetesEmptyMachineLocation; -import org.apache.brooklyn.container.location.kubernetes.machine.KubernetesMachineLocation; -import org.apache.brooklyn.container.location.kubernetes.machine.KubernetesSshMachineLocation; -import org.apache.brooklyn.core.entity.BrooklynConfigKeys; -import org.apache.brooklyn.core.entity.EntityInternal; -import org.apache.brooklyn.core.location.AbstractLocation; -import org.apache.brooklyn.core.location.LocationConfigKeys; -import org.apache.brooklyn.core.location.PortRanges; -import org.apache.brooklyn.core.location.access.PortForwardManager; -import org.apache.brooklyn.core.location.access.PortForwardManagerLocationResolver; -import org.apache.brooklyn.core.location.cloud.CloudLocationConfig; -import org.apache.brooklyn.core.network.OnPublicNetworkEnricher; -import org.apache.brooklyn.core.sensor.Sensors; -import org.apache.brooklyn.location.ssh.SshMachineLocation; -import org.apache.brooklyn.util.collections.MutableList; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.core.ResourceUtils; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.core.config.ResolvingConfigBag; -import org.apache.brooklyn.util.core.internal.ssh.SshTool; -import org.apache.brooklyn.util.core.text.TemplateProcessor; -import org.apache.brooklyn.util.exceptions.ReferenceWithError; -import org.apache.brooklyn.util.net.Networking; -import org.apache.brooklyn.util.repeat.Repeater; -import org.apache.brooklyn.util.stream.Streams; -import org.apache.brooklyn.util.text.Identifiers; -import org.apache.brooklyn.util.text.Strings; -import org.apache.brooklyn.util.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.google.common.base.Functions; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Stopwatch; +import com.google.common.base.Suppliers; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -91,7 +35,12 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.io.BaseEncoding; import com.google.common.net.HostAndPort; - +import hapi.chart.ChartOuterClass.Chart; +import hapi.release.ReleaseOuterClass.Release; +import hapi.services.tiller.Tiller.InstallReleaseRequest; +import hapi.services.tiller.Tiller.InstallReleaseResponse; +import hapi.services.tiller.Tiller.UninstallReleaseRequest; +import hapi.services.tiller.Tiller.UninstallReleaseResponse; import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.ContainerBuilder; import io.fabric8.kubernetes.api.model.ContainerPort; @@ -104,9 +53,11 @@ import io.fabric8.kubernetes.api.model.EnvVarBuilder; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.Namespace; import io.fabric8.kubernetes.api.model.NamespaceBuilder; +import io.fabric8.kubernetes.api.model.Node; import io.fabric8.kubernetes.api.model.PersistentVolume; import io.fabric8.kubernetes.api.model.PersistentVolumeBuilder; import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodCondition; import io.fabric8.kubernetes.api.model.PodList; import io.fabric8.kubernetes.api.model.PodTemplateSpec; import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder; @@ -123,8 +74,75 @@ import io.fabric8.kubernetes.api.model.ServicePortBuilder; import io.fabric8.kubernetes.api.model.extensions.Deployment; import io.fabric8.kubernetes.api.model.extensions.DeploymentBuilder; import io.fabric8.kubernetes.api.model.extensions.DeploymentStatus; +import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientException; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.api.location.MachineProvisioningLocation; +import org.apache.brooklyn.api.location.NoMachinesAvailableException; +import org.apache.brooklyn.api.location.PortRange; +import org.apache.brooklyn.api.sensor.AttributeSensor; +import org.apache.brooklyn.api.sensor.EnricherSpec; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.container.entity.docker.DockerContainer; +import org.apache.brooklyn.container.entity.kubernetes.KubernetesHelmChart; +import org.apache.brooklyn.container.entity.kubernetes.KubernetesPod; +import org.apache.brooklyn.container.entity.kubernetes.KubernetesResource; +import org.apache.brooklyn.container.location.docker.DockerJcloudsLocation; +import org.apache.brooklyn.container.location.kubernetes.machine.KubernetesEmptyMachineLocation; +import org.apache.brooklyn.container.location.kubernetes.machine.KubernetesMachineLocation; +import org.apache.brooklyn.container.location.kubernetes.machine.KubernetesSshMachineLocation; +import org.apache.brooklyn.container.supplier.TillerSupplier; +import org.apache.brooklyn.core.entity.BrooklynConfigKeys; +import org.apache.brooklyn.core.entity.EntityInternal; +import org.apache.brooklyn.core.location.AbstractLocation; +import org.apache.brooklyn.core.location.LocationConfigKeys; +import org.apache.brooklyn.core.location.PortRanges; +import org.apache.brooklyn.core.location.access.PortForwardManager; +import org.apache.brooklyn.core.location.access.PortForwardManagerLocationResolver; +import org.apache.brooklyn.core.location.cloud.CloudLocationConfig; +import org.apache.brooklyn.core.network.OnPublicNetworkEnricher; +import org.apache.brooklyn.core.sensor.Sensors; +import org.apache.brooklyn.location.ssh.SshMachineLocation; +import org.apache.brooklyn.util.collections.MutableList; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.ResourceUtils; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.core.config.ResolvingConfigBag; +import org.apache.brooklyn.util.core.internal.ssh.SshTool; +import org.apache.brooklyn.util.core.text.TemplateProcessor; +import org.apache.brooklyn.util.exceptions.ReferenceWithError; +import org.apache.brooklyn.util.net.Networking; +import org.apache.brooklyn.util.repeat.Repeater; +import org.apache.brooklyn.util.stream.Streams; +import org.apache.brooklyn.util.text.Identifiers; +import org.apache.brooklyn.util.text.Strings; +import org.apache.brooklyn.util.time.Duration; +import org.microbean.helm.ReleaseManager; +import org.microbean.helm.Tiller; +import org.microbean.helm.TillerInstaller; +import org.microbean.helm.chart.repository.ChartRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.URI; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static org.apache.brooklyn.test.Asserts.assertNotNull; public class KubernetesLocation extends AbstractLocation implements MachineProvisioningLocation<KubernetesMachineLocation>, KubernetesLocationConfig { @@ -163,6 +181,7 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi public static final String BROOKLYN_ROOT_PASSWORD = "BROOKLYN_ROOT_PASSWORD"; private static final Logger LOG = LoggerFactory.getLogger(KubernetesLocation.class); private KubernetesClient client; + private Tiller tiller; public KubernetesLocation() { super(); @@ -202,9 +221,12 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi ConfigBag setup = ResolvingConfigBag.newInstanceExtending(getManagementContext(), setupRaw); client = getClient(setup); + Entity entity = validateCallerContext(setup); if (isKubernetesResource(entity)) { return createKubernetesResourceLocation(entity, setup); + } else if (isKubernetesHelmChart(entity)) { + return createKubernetesHelmChartLocation(entity, setup); } else { return createKubernetesContainerLocation(entity, setup); } @@ -215,6 +237,8 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi Entity entity = validateCallerContext(machine); if (isKubernetesResource(entity)) { deleteKubernetesResourceLocation(entity); + } else if (isKubernetesHelmChart(entity)) { + deleteKubernetesHelmChartLocation(entity); } else { deleteKubernetesContainerLocation(entity, machine); } @@ -284,6 +308,21 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi return false; } + protected void deleteKubernetesHelmChartLocation(Entity entity) { + final String releaseName = entity.sensors().get(KubernetesResource.RESOURCE_NAME); + ReleaseManager chartManager = new ReleaseManager(tiller); + try { + Future<UninstallReleaseResponse> response = chartManager.uninstall(UninstallReleaseRequest.newBuilder() + .setTimeout(300L) + .setName(releaseName) + .setPurge(true) + .build()); + LOG.debug("Release {} uninstalled", response); + } catch (IOException e) { + throw Throwables.propagate(e); + } + } + protected void undeploy(final String namespace, final String deployment, final String pod) { client.extensions().deployments().inNamespace(namespace).withName(deployment).delete(); ExitCondition exitCondition = new ExitCondition() { @@ -347,8 +386,8 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi if (result.isEmpty()) { return false; } - List<HasMetadata> check = client.resource(result.get(0)).inNamespace(result.get(0).getMetadata().getNamespace()).get(); - if (result.size() > 1 || check.size() != 1 || check.get(0).getMetadata() == null) { + HasMetadata check = client.resource(result.get(0)).inNamespace(result.get(0).getMetadata().getNamespace()).get(); + if (result.size() > 1 || check != null || check.getMetadata() == null) { return false; } return true; @@ -385,7 +424,7 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi if (resourceType.equals(KubernetesResource.SERVICE) && machine instanceof KubernetesSshMachineLocation) { Service service = getService(namespace, resourceName); - registerPortMappings((KubernetesSshMachineLocation) machine, entity, service); + registerPortMappings(machine, entity, service); } return machine; @@ -482,7 +521,7 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi entity.sensors().set(KubernetesPod.KUBERNETES_POD, pod.getMetadata().getName()); entity.sensors().set(KubernetesPod.KUBERNETES_SERVICE, service.getMetadata().getName()); - LocationSpec<KubernetesSshMachineLocation> locationSpec = prepareSshableLocationSpec(entity, setup, namespace, deploymentName, service, pod) + LocationSpec<KubernetesSshMachineLocation> locationSpec = prepareSshableLocationSpec(entity, setup, service, pod) .configure(KubernetesMachineLocation.KUBERNETES_NAMESPACE, namespace.getMetadata().getName()) .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_NAME, deploymentName) .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_TYPE, getContainerResourceType()); @@ -500,6 +539,72 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi return KubernetesResource.DEPLOYMENT; } + protected KubernetesMachineLocation createKubernetesHelmChartLocation(Entity entity, ConfigBag setup) { + Map<String, String> podLabels = ImmutableMap.of("name", "tiller", "app", "helm"); + if (!isTillerInstalled("kube-system", podLabels)) { + installTillerPodAndService("kube-system", "tiller-deploy", podLabels); + } + tiller = Suppliers.memoize(new TillerSupplier((DefaultKubernetesClient) client)).get(); + String chartName = entity.config().get(KubernetesHelmChart.CHART_NAME); + String chartVersion = entity.config().get(KubernetesHelmChart.CHART_VERSION); + + ReleaseManager chartManager = new ReleaseManager(tiller); + try { + InstallReleaseRequest.Builder requestBuilder = InstallReleaseRequest.newBuilder(); + requestBuilder.setTimeout(300L); + requestBuilder.setWait(true); + + ChartRepository chartRepository = new ChartRepository("stable", new URI("https://kubernetes-charts.storage.googleapis.com/")); + if (chartVersion == null) { + ChartRepository.Index.Entry latest = chartRepository.getIndex().getEntries().get(chartName).first(); + chartVersion = latest.getVersion(); + } + + Chart.Builder chartBuilder = chartRepository.resolve(chartName, chartVersion); + Future<InstallReleaseResponse> releaseFuture = chartManager.install(requestBuilder, chartBuilder); + Release release = releaseFuture.get().getRelease(); + + String resourceName = release.getName(); + String namespace = release.getNamespace(); + LOG.debug("Resource {} (from chart {}) deployed to {}", new Object[]{resourceName, chartName, namespace}); + + Node node = Iterables.getFirst(client.nodes().list().getItems(), null); // null should never happen here + String nodeAddress = node.getStatus().getAddresses().get(0).getAddress(); + InetAddress inetAddress = Networking.getInetAddressWithFixedName(nodeAddress); + + entity.sensors().set(KubernetesPod.KUBERNETES_NAMESPACE, namespace); + entity.sensors().set(KubernetesResource.RESOURCE_NAME, resourceName); + + LocationSpec<? extends KubernetesMachineLocation> locationSpec = LocationSpec.create(KubernetesEmptyMachineLocation.class); + locationSpec.configure(CALLER_CONTEXT, setup.get(CALLER_CONTEXT)) + .configure(KubernetesMachineLocation.KUBERNETES_NAMESPACE, namespace) + .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_NAME, resourceName) + .configure("address", inetAddress) + .configure(SshMachineLocation.PRIVATE_ADDRESSES, ImmutableSet.of(nodeAddress)); + + KubernetesMachineLocation machine = getManagementContext().getLocationManager().createLocation(locationSpec); + + String serviceName = String.format("%s-%s", resourceName, chartName); + Service service = getService(namespace, serviceName); + registerPortMappings(machine, entity, service); + return machine; + + } catch (Exception e) { + throw Throwables.propagate(e); + } + } + + private void installTillerPodAndService(String namespace, String serviceName, Map<String, String> podLabels) { + TillerInstaller installer = new TillerInstaller(); + installer.init(true); + getPod(namespace, podLabels); + getService(namespace, serviceName); + } + + private boolean isTillerInstalled(String namespace, Map<String, String> podLabels) { + return !Iterables.isEmpty(client.pods().inNamespace(namespace).withLabels(podLabels).list().getItems()); + } + protected void waitForSshable(final SshMachineLocation machine, Duration timeout) { Callable<Boolean> checker = new Callable<Boolean>() { public Boolean call() { @@ -530,11 +635,16 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi } } - protected void registerPortMappings(KubernetesSshMachineLocation machine, Entity entity, Service service) { + protected void registerPortMappings(KubernetesMachineLocation machine, Entity entity, Service service) { PortForwardManager portForwardManager = (PortForwardManager) getManagementContext().getLocationRegistry() .getLocationManaged(PortForwardManagerLocationResolver.PFM_GLOBAL_SPEC); List<ServicePort> ports = service.getSpec().getPorts(); - String publicHostText = ((SshMachineLocation) machine).getSshHostAndPort().getHostText(); + String publicHostText; + if (machine instanceof SshMachineLocation) { + publicHostText = ((SshMachineLocation) machine).getSshHostAndPort().getHostText(); + } else { + publicHostText = Iterables.getFirst(machine.config().get(SshMachineLocation.PRIVATE_ADDRESSES), null); + } LOG.debug("Recording port-mappings for container {} of {}: {}", new Object[]{machine, this, ports}); for (ServicePort port : ports) { @@ -607,7 +717,13 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi @Override public Boolean call() { PodList result = client.pods().inNamespace(namespace).withLabels(metadata).list(); - return result.getItems().size() >= 1 && result.getItems().get(0).getStatus().getPodIP() != null; + return result.getItems().size() >= 1 && + Iterables.any(result.getItems().get(0).getStatus().getConditions(), new Predicate<PodCondition>() { + @Override + public boolean apply(@Nullable PodCondition input) { + return input.getStatus().equals("True"); + } + }); } @Override @@ -777,7 +893,7 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi return false; } for (EndpointSubset subset : endpoints.getSubsets()) { - if (subset.getNotReadyAddresses().size() > 0) { + if (subset.getAddresses().isEmpty() && subset.getNotReadyAddresses().size() > 0) { return false; } } @@ -795,7 +911,7 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi return client.services().inNamespace(namespace).withName(serviceName).get(); } - protected LocationSpec<KubernetesSshMachineLocation> prepareSshableLocationSpec(Entity entity, ConfigBag setup, Namespace namespace, String deploymentName, Service service, Pod pod) { + protected LocationSpec<KubernetesSshMachineLocation> prepareSshableLocationSpec(Entity entity, ConfigBag setup, Service service, Pod pod) { InetAddress node = Networking.getInetAddressWithFixedName(pod.getSpec().getNodeName()); String podAddress = pod.getStatus().getPodIP(); LocationSpec<KubernetesSshMachineLocation> locationSpec = LocationSpec.create(KubernetesSshMachineLocation.class) @@ -994,6 +1110,10 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi return implementsInterface(entity, KubernetesResource.class); } + protected boolean isKubernetesHelmChart(Entity entity) { + return implementsInterface(entity, KubernetesHelmChart.class); + } + public boolean implementsInterface(Entity entity, Class<?> type) { return Iterables.tryFind(Arrays.asList(entity.getClass().getInterfaces()), Predicates.assignableFrom(type)).isPresent(); } @@ -1048,7 +1168,7 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi } } - public static interface ExitCondition extends Callable<Boolean> { - public String getFailureMessage(); + public interface ExitCondition extends Callable<Boolean> { + String getFailureMessage(); } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6ddc595d/locations/container/src/main/java/org/apache/brooklyn/container/supplier/TillerSupplier.java ---------------------------------------------------------------------- diff --git a/locations/container/src/main/java/org/apache/brooklyn/container/supplier/TillerSupplier.java b/locations/container/src/main/java/org/apache/brooklyn/container/supplier/TillerSupplier.java new file mode 100644 index 0000000..ce9f22e --- /dev/null +++ b/locations/container/src/main/java/org/apache/brooklyn/container/supplier/TillerSupplier.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.brooklyn.container.supplier; + +import java.net.MalformedURLException; + +import org.microbean.helm.Tiller; + +import com.google.common.base.Supplier; +import com.google.common.base.Throwables; + +import io.fabric8.kubernetes.client.DefaultKubernetesClient; + +public class TillerSupplier implements Supplier<Tiller> { + private final DefaultKubernetesClient client; + + public TillerSupplier(DefaultKubernetesClient client) { + this.client = client; + } + + @Override + public Tiller get() { + try { + return new Tiller(client); + } catch (MalformedURLException e) { + throw Throwables.propagate(e); + } + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6ddc595d/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationLiveTest.java b/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationLiveTest.java index ed42bd2..4df41b4 100644 --- a/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationLiveTest.java +++ b/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationLiveTest.java @@ -61,6 +61,8 @@ public class KubernetesLocationLiveTest extends BrooklynAppLiveTestSupport { public static final String KUBERNETES_ENDPOINT = System.getProperty("test.brooklyn-container-service.kubernetes.endpoint", ""); public static final String IDENTITY = System.getProperty("test.brooklyn-container-service.kubernetes.identity", ""); public static final String CREDENTIAL = System.getProperty("test.brooklyn-container-service.kubernetes.credential", ""); + public static final String KUBECONFIG = System.getProperty("test.brooklyn-container-service.kubernetes.kubeconfig", ""); + private static final Logger LOG = LoggerFactory.getLogger(KubernetesLocationLiveTest.class); protected KubernetesLocation loc; protected List<KubernetesMachineLocation> machines; http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6ddc595d/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationYamlLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationYamlLiveTest.java b/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationYamlLiveTest.java index a3030b1..76bcd59 100644 --- a/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationYamlLiveTest.java +++ b/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationYamlLiveTest.java @@ -24,6 +24,7 @@ import static com.google.common.base.Predicates.not; import static com.google.common.base.Predicates.notNull; import static org.apache.brooklyn.container.location.kubernetes.KubernetesLocationLiveTest.CREDENTIAL; import static org.apache.brooklyn.container.location.kubernetes.KubernetesLocationLiveTest.IDENTITY; +import static org.apache.brooklyn.container.location.kubernetes.KubernetesLocationLiveTest.KUBECONFIG; import static org.apache.brooklyn.container.location.kubernetes.KubernetesLocationLiveTest.KUBERNETES_ENDPOINT; import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEquals; import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEqualsEventually; @@ -43,6 +44,7 @@ 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.container.entity.docker.DockerContainer; +import org.apache.brooklyn.container.entity.kubernetes.KubernetesHelmChart; import org.apache.brooklyn.container.entity.kubernetes.KubernetesPod; import org.apache.brooklyn.container.entity.kubernetes.KubernetesResource; import org.apache.brooklyn.core.entity.Attributes; @@ -93,6 +95,7 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest { "location:", " kubernetes:", " " + KubernetesLocationConfig.MASTER_URL.getName() + ": \"" + KUBERNETES_ENDPOINT + "\"", + " " + (StringUtils.isBlank(KUBECONFIG) ? "" : "kubeconfig: " + KUBECONFIG), " " + (StringUtils.isBlank(IDENTITY) ? "" : "identity: " + IDENTITY), " " + (StringUtils.isBlank(CREDENTIAL) ? "" : "credential: " + CREDENTIAL)); } @@ -510,6 +513,27 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest { assertReachableEventually(HostAndPort.fromString(httpPublicPort)); } + @Test(groups={"Live"}) + public void testJenkinsHelmChart() throws Exception { + String yaml = Joiner.on("\n").join( + locationYaml, + "services:", + " - type: " + KubernetesHelmChart.class.getName(), + " name: \"jenkins-helm\"", + " chartName: jenkins"); + Entity app = createStartWaitAndLogApplication(yaml); + + Iterable<KubernetesHelmChart> resources = Entities.descendantsAndSelf(app, KubernetesHelmChart.class); + KubernetesHelmChart jenkisHelm = Iterables.find(resources, EntityPredicates.displayNameEqualTo("jenkins-helm")); + + assertEntityHealthy(jenkisHelm); + + Entities.dumpInfo(app); + + Integer httpPort = assertAttributeEventuallyNonNull(jenkisHelm, Sensors.newIntegerSensor("kubernetes.http.port")); + assertEquals(httpPort, Integer.valueOf(8080)); + } + protected void assertReachableEventually(final HostAndPort hostAndPort) { succeedsEventually(new Runnable() { public void run() {