From 23279220e73e1dbc3d1c2a5d1d99c4932b3ec308 Mon Sep 17 00:00:00 2001 From: Lewis Headden Date: Fri, 1 Feb 2019 14:01:08 -0500 Subject: [PATCH 1/2] Add ManagedChannelFactory/ManagedChannelConfigurer --- .../helm/DefaultManagedChannelFactory.java | 61 ++++++++ .../helm/ManagedChannelConfigurer.java | 11 ++ .../microbean/helm/ManagedChannelFactory.java | 32 +++++ src/main/java/org/microbean/helm/Tiller.java | 133 +++++++++--------- .../org/microbean/helm/TillerInstaller.java | 2 +- 5 files changed, 173 insertions(+), 66 deletions(-) create mode 100644 src/main/java/org/microbean/helm/DefaultManagedChannelFactory.java create mode 100644 src/main/java/org/microbean/helm/ManagedChannelConfigurer.java create mode 100644 src/main/java/org/microbean/helm/ManagedChannelFactory.java diff --git a/src/main/java/org/microbean/helm/DefaultManagedChannelFactory.java b/src/main/java/org/microbean/helm/DefaultManagedChannelFactory.java new file mode 100644 index 00000000..c97d3b3e --- /dev/null +++ b/src/main/java/org/microbean/helm/DefaultManagedChannelFactory.java @@ -0,0 +1,61 @@ +package org.microbean.helm; + +import io.fabric8.kubernetes.client.LocalPortForward; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import java.net.InetAddress; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import org.microbean.development.annotation.Issue; + +/** + * A default implementation of the {@link ManagedChannelFactory} that creates a {@link ManagedChannel} + * from a {@link LocalPortForward}. + * + * Allows additional customization of the {@link ManagedChannelBuilder} by supplying a + * {@link ManagedChannelConfigurer} to the constructor. + */ +public class DefaultManagedChannelFactory implements ManagedChannelFactory { + + private static final ManagedChannelConfigurer NOOP_CONFIGURER = (builder) -> {}; + private final ManagedChannelConfigurer configurer; + + public DefaultManagedChannelFactory() { + this.configurer = NOOP_CONFIGURER; + } + + /** + * @param configurer a {@link ManagedChannelConfigurer} to allow overriding of the default + * configuration of the {@link ManagedChannel} + * + * @throws NullPointerException if the configurer is null + */ + public DefaultManagedChannelFactory(final ManagedChannelConfigurer configurer) { + Objects.requireNonNull(configurer); + this.configurer = configurer; + } + + @Issue(id = "42", uri = "https://github.com/microbean/microbean-helm/issues/42") + @Override public ManagedChannel create(final LocalPortForward portForward) { + Objects.requireNonNull(portForward); + @Issue(id = "43", uri = "https://github.com/microbean/microbean-helm/issues/43") + final InetAddress localAddress = portForward.getLocalAddress(); + if (localAddress == null) { + throw new IllegalArgumentException("portForward", + new IllegalStateException("portForward.getLocalAddress() == null")); + } + final String hostAddress = localAddress.getHostAddress(); + if (hostAddress == null) { + throw new IllegalArgumentException("portForward", + new IllegalStateException("portForward.getLocalAddress().getHostAddress() == null")); + } + final ManagedChannelBuilder builder = + ManagedChannelBuilder.forAddress(hostAddress, portForward.getLocalPort()) + .idleTimeout(5L, TimeUnit.SECONDS) + .keepAliveTime(30L, TimeUnit.SECONDS) + .maxInboundMessageSize(Tiller.MAX_MESSAGE_SIZE) + .usePlaintext(true); + configurer.configure(builder); + return builder.build(); + } +} diff --git a/src/main/java/org/microbean/helm/ManagedChannelConfigurer.java b/src/main/java/org/microbean/helm/ManagedChannelConfigurer.java new file mode 100644 index 00000000..51c86d80 --- /dev/null +++ b/src/main/java/org/microbean/helm/ManagedChannelConfigurer.java @@ -0,0 +1,11 @@ +package org.microbean.helm; + +import io.grpc.ManagedChannelBuilder; + +/** + * An interface whose implementations configure options on the supplied + * {@link ManagedChannelBuilder} to override defaults. + */ +public interface ManagedChannelConfigurer { + void configure(final ManagedChannelBuilder managedChannelBuilder); +} diff --git a/src/main/java/org/microbean/helm/ManagedChannelFactory.java b/src/main/java/org/microbean/helm/ManagedChannelFactory.java new file mode 100644 index 00000000..07c3dc21 --- /dev/null +++ b/src/main/java/org/microbean/helm/ManagedChannelFactory.java @@ -0,0 +1,32 @@ +package org.microbean.helm; + +import io.fabric8.kubernetes.client.LocalPortForward; +import io.grpc.ManagedChannel; + +/** + * An interface whose implementations create a {@link ManagedChannel} from a + * {@link LocalPortForward} to be used to communicate with Tiller. + */ +public interface ManagedChannelFactory { + + /** + * Creates a {@link ManagedChannel} for communication with Tiller + * from the information contained in the supplied {@link + * LocalPortForward}. + * + *

This method never returns {@code null}.

+ * + *

Overrides of this method must not return {@code null}.

+ * + * @param portForward a {@link LocalPortForward}; must not be {@code + * null} + * @return a non-{@code null} {@link ManagedChannel} + * @throws NullPointerException if {@code portForward} is {@code + * null} + * @throws IllegalArgumentException if {@code portForward}'s + * {@link LocalPortForward#getLocalAddress()} method returns {@code + * null} + */ + ManagedChannel create(final LocalPortForward portForward); + +} diff --git a/src/main/java/org/microbean/helm/Tiller.java b/src/main/java/org/microbean/helm/Tiller.java index dd5ecca4..8b50ea5e 100644 --- a/src/main/java/org/microbean/helm/Tiller.java +++ b/src/main/java/org/microbean/helm/Tiller.java @@ -19,7 +19,6 @@ import java.io.Closeable; import java.io.IOException; -import java.net.InetAddress; import java.net.MalformedURLException; import java.util.Collections; @@ -27,8 +26,6 @@ import java.util.Map; import java.util.Objects; -import java.util.concurrent.TimeUnit; - import hapi.services.tiller.ReleaseServiceGrpc; import hapi.services.tiller.ReleaseServiceGrpc.ReleaseServiceBlockingStub; import hapi.services.tiller.ReleaseServiceGrpc.ReleaseServiceFutureStub; @@ -46,7 +43,6 @@ import io.fabric8.kubernetes.client.LocalPortForward; import io.grpc.ManagedChannel; -import io.grpc.ManagedChannelBuilder; import io.grpc.Metadata; import io.grpc.health.v1.HealthGrpc; @@ -58,8 +54,6 @@ import okhttp3.OkHttpClient; -import org.microbean.development.annotation.Issue; - import org.microbean.kubernetes.Pods; /** @@ -113,7 +107,7 @@ public class Tiller implements ConfigAware, Closeable { * be. */ public static final int MAX_MESSAGE_SIZE = 20 * 1024 * 1024; - + /** * A {@link Metadata} that ensures that certain Tiller-related * headers are passed with every gRPC call. @@ -122,11 +116,10 @@ public class Tiller implements ConfigAware, Closeable { */ private static final Metadata metadata = new Metadata(); - /* * Static initializer. */ - + /** * Static initializer; initializes the {@link #DEFAULT_LABELS} @@ -173,7 +166,6 @@ public class Tiller implements ConfigAware, Closeable { */ private final ManagedChannel channel; - /* * Constructors. */ @@ -209,11 +201,30 @@ public Tiller(final ManagedChannel channel) { * null} */ public Tiller(final LocalPortForward portForward) { + this(portForward, new DefaultManagedChannelFactory()); + } + + + /** + * Creates a new {@link Tiller} that will use information from the + * supplied {@link LocalPortForward} to establish a communications + * channel with the Tiller server. + * + * @param portForward the {@link LocalPortForward} to use; must not + * be {@code null} + * + * @param managedChannelFactory the {@link ManagedChannelFactory} that will be used to create a + * {@link ManagedChannel} to communicate with Tiller + * + * @exception NullPointerException if {@code portForward} is {@code + * null} + */ + public Tiller(final LocalPortForward portForward, final ManagedChannelFactory managedChannelFactory) { super(); Objects.requireNonNull(portForward); this.config = null; this.portForward = null; // yes, null - this.channel = this.buildChannel(portForward); + this.channel = managedChannelFactory.create(portForward); } /** @@ -244,7 +255,41 @@ public Tiller(final LocalPortForward portForward) { * @exception NullPointerException if {@code client} is {@code null} */ public Tiller(final T client) throws MalformedURLException { - this(client, DEFAULT_NAMESPACE, DEFAULT_PORT, DEFAULT_LABELS); + this(client, DEFAULT_NAMESPACE, DEFAULT_PORT, DEFAULT_LABELS, new DefaultManagedChannelFactory()); + } + + /** + * Creates a new {@link Tiller} that will forward a local port to + * port {@code 44134} on a Pod housing Tiller in the {@code + * kube-system} namespace running in the Kubernetes cluster with + * which the supplied {@link KubernetesClient} is capable of + * communicating. + * + *

The {@linkplain Pods#getFirstReadyPod(Listable) first ready + * Pod} with a {@code name} label whose value is {@code tiller} and + * with an {@code app} label whose value is {@code helm} is deemed + * to be the pod housing the Tiller instance to connect to. (This + * duplicates the default logic of the {@code helm} command line + * executable.)

+ * + * @param a {@link KubernetesClient} implementation that is also + * an {@link HttpClientAware} implementation, such as {@link + * DefaultKubernetesClient} + * + * @param client the {@link KubernetesClient}-and-{@link + * HttpClientAware} implementation that can communicate with a + * Kubernetes cluster; must not be {@code null} + * + * @param managedChannelFactory the {@link ManagedChannelFactory} that will be used to create a + * {@link ManagedChannel} to communicate with Tiller + * + * @exception MalformedURLException if there was a problem + * identifying a Pod within the cluster that houses a Tiller instance + * + * @exception NullPointerException if {@code client} is {@code null} + */ + public Tiller(final T client, final ManagedChannelFactory managedChannelFactory) throws MalformedURLException { + this(client, DEFAULT_NAMESPACE, DEFAULT_PORT, DEFAULT_LABELS, managedChannelFactory); } /** @@ -285,7 +330,7 @@ public Tiller(final T client) thr * found and consequently a connection could not be established */ public Tiller(final T client, final String namespaceHousingTiller) throws MalformedURLException { - this(client, namespaceHousingTiller, DEFAULT_PORT, DEFAULT_LABELS); + this(client, namespaceHousingTiller, DEFAULT_PORT, DEFAULT_LABELS, new DefaultManagedChannelFactory()); } /** @@ -320,6 +365,9 @@ public Tiller(final T client, fin * instance; if {@code null} then the value of {@link * #DEFAULT_LABELS} will be used instead * + * @param managedChannelFactory the {@link ManagedChannelFactory} that will be used to create a + * {@link ManagedChannel} to communicate with Tiller + * * @exception MalformedURLException if there was a problem * identifying a Pod within the cluster that houses a Tiller instance * @@ -334,7 +382,8 @@ public Tiller(final T client, fin public Tiller(final T client, String namespaceHousingTiller, int tillerPort, - Map tillerLabels) throws MalformedURLException { + Map tillerLabels, + ManagedChannelFactory managedChannelFactory) throws MalformedURLException { super(); Objects.requireNonNull(client); this.config = client.getConfiguration(); @@ -352,12 +401,12 @@ public Tiller(final T client, throw new IllegalArgumentException("client", new IllegalStateException("client.getHttpClient() == null")); } LocalPortForward portForward = null; - + this.portForward = Pods.forwardPort(httpClient, client.pods().inNamespace(namespaceHousingTiller).withLabels(tillerLabels), tillerPort); if (this.portForward == null) { throw new TillerException("Could not forward port to a Ready Tiller pod's port " + tillerPort + " in namespace " + namespaceHousingTiller + " with labels " + tillerLabels); } - this.channel = this.buildChannel(this.portForward); + this.channel = managedChannelFactory.create(this.portForward); } @@ -365,7 +414,6 @@ public Tiller(final T client, * Instance methods. */ - /** * Returns any {@link Config} available at construction time. * @@ -377,51 +425,6 @@ public Tiller(final T client, public Config getConfiguration() { return this.config; } - - - /** - * Creates a {@link ManagedChannel} for communication with Tiller - * from the information contained in the supplied {@link - * LocalPortForward}. - * - *

Note: This method is (deliberately) called - * from constructors so must have stateless semantics.

- * - *

This method never returns {@code null}.

- * - *

Overrides of this method must not return {@code null}.

- * - * @param portForward a {@link LocalPortForward}; must not be {@code - * null} - * - * @return a non-{@code null} {@link ManagedChannel} - * - * @exception NullPointerException if {@code portForward} is {@code - * null} - * - * @exception IllegalArgumentException if {@code portForward}'s - * {@link LocalPortForward#getLocalAddress()} method returns {@code - * null} - */ - @Issue(id = "42", uri = "https://github.com/microbean/microbean-helm/issues/42") - protected ManagedChannel buildChannel(final LocalPortForward portForward) { - Objects.requireNonNull(portForward); - @Issue(id = "43", uri = "https://github.com/microbean/microbean-helm/issues/43") - final InetAddress localAddress = portForward.getLocalAddress(); - if (localAddress == null) { - throw new IllegalArgumentException("portForward", new IllegalStateException("portForward.getLocalAddress() == null")); - } - final String hostAddress = localAddress.getHostAddress(); - if (hostAddress == null) { - throw new IllegalArgumentException("portForward", new IllegalStateException("portForward.getLocalAddress().getHostAddress() == null")); - } - return ManagedChannelBuilder.forAddress(hostAddress, portForward.getLocalPort()) - .idleTimeout(5L, TimeUnit.SECONDS) - .keepAliveTime(30L, TimeUnit.SECONDS) - .maxInboundMessageSize(MAX_MESSAGE_SIZE) - .usePlaintext(true) - .build(); - } /** * Closes this {@link Tiller} after use; any {@link @@ -498,7 +501,7 @@ public ReleaseServiceFutureStub getReleaseServiceFutureStub() { * @return a non-{@code null} {@link ReleaseServiceStub} * * @see ReleaseServiceStub - */ + */ public ReleaseServiceStub getReleaseServiceStub() { ReleaseServiceStub returnValue = null; if (this.channel != null) { @@ -522,7 +525,7 @@ public HealthFutureStub getHealthFutureStub() { } return returnValue; } - + public HealthStub getHealthStub() { HealthStub returnValue = null; if (this.channel != null) { @@ -538,5 +541,5 @@ public VersionOrBuilder getVersion() throws IOException { assert response != null; return response.getVersion(); } - + } diff --git a/src/main/java/org/microbean/helm/TillerInstaller.java b/src/main/java/org/microbean/helm/TillerInstaller.java index 767d3318..385a9686 100644 --- a/src/main/java/org/microbean/helm/TillerInstaller.java +++ b/src/main/java/org/microbean/helm/TillerInstaller.java @@ -999,7 +999,7 @@ protected final void ping(String throw new TillerPollingDeadlineExceededException(String.valueOf(timeoutInMilliseconds)); } @SuppressWarnings("unchecked") - final Tiller tiller = new Tiller((T)this.kubernetesClient, namespace, -1 /* use default */, labels); + final Tiller tiller = new Tiller((T)this.kubernetesClient, namespace, -1 /* use default */, labels, new DefaultManagedChannelFactory()); final HealthBlockingStub health = tiller.getHealthBlockingStub(); assert health != null; final HealthCheckRequest.Builder builder = HealthCheckRequest.newBuilder(); From e9aec75c01811c2c9cba094efab50e2b9373f753 Mon Sep 17 00:00:00 2001 From: Lewis Headden Date: Fri, 1 Feb 2019 14:27:58 -0500 Subject: [PATCH 2/2] Ignore IntelliJ files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f4739a6f..39d4567d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ nbactions.xml src/site/markdown/*.html target/ +.idea +*.iml \ No newline at end of file