001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2017 MicroBean.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
014 * implied.  See the License for the specific language governing
015 * permissions and limitations under the License.
016 */
017package org.microbean.helm;
018
019import java.io.Closeable;
020import java.io.IOException;
021
022import java.net.InetAddress;
023import java.net.MalformedURLException;
024
025import java.util.Collections;
026import java.util.HashMap;
027import java.util.Map;
028import java.util.Objects;
029
030import hapi.services.tiller.ReleaseServiceGrpc;
031import hapi.services.tiller.ReleaseServiceGrpc.ReleaseServiceBlockingStub;
032import hapi.services.tiller.ReleaseServiceGrpc.ReleaseServiceFutureStub;
033import hapi.services.tiller.ReleaseServiceGrpc.ReleaseServiceStub;
034
035import io.fabric8.kubernetes.client.HttpClientAware;
036import io.fabric8.kubernetes.client.KubernetesClient;
037import io.fabric8.kubernetes.client.LocalPortForward;
038
039import io.grpc.ManagedChannel;
040import io.grpc.ManagedChannelBuilder;
041import io.grpc.Metadata;
042
043import io.grpc.stub.MetadataUtils;
044
045import org.microbean.kubernetes.Pods;
046
047public class Tiller implements Closeable {
048
049  public static final String VERSION = "2.4.2";
050
051  public static final String DEFAULT_NAMESPACE = "kube-system";
052  
053  public static final int DEFAULT_PORT = 44134;
054
055  public static final Map<String, String> DEFAULT_LABELS;
056
057  private static final Metadata metadata = new Metadata();
058
059  static {
060    final Map<String, String> labels = new HashMap<>();
061    labels.put("name", "tiller");
062    labels.put("app", "helm");
063    DEFAULT_LABELS = Collections.unmodifiableMap(labels);
064    metadata.put(Metadata.Key.of("x-helm-api-client", Metadata.ASCII_STRING_MARSHALLER), VERSION);
065  }
066
067
068  /*
069   * Instance fields.
070   */
071  
072
073  private final LocalPortForward portForward;
074  
075  private final ManagedChannel channel;
076
077
078  /*
079   * Constructors.
080   */
081
082  
083  public Tiller(final ManagedChannel channel) {
084    super();
085    Objects.requireNonNull(channel);
086    this.portForward = null;
087    this.channel = channel;
088  }
089
090  public Tiller(final LocalPortForward portForward) {
091    super();
092    Objects.requireNonNull(portForward);
093    this.portForward = null; // yes, null
094    this.channel = this.buildChannel(portForward);
095  }
096
097  public <T extends HttpClientAware & KubernetesClient> Tiller(final T client) throws MalformedURLException {
098    this(client, DEFAULT_NAMESPACE, DEFAULT_PORT, DEFAULT_LABELS);
099  }
100  
101  public <T extends HttpClientAware & KubernetesClient> Tiller(final T client, final String namespaceHousingTiller) throws MalformedURLException {
102    this(client, namespaceHousingTiller, DEFAULT_PORT, DEFAULT_LABELS);
103  }
104  
105  public <T extends HttpClientAware & KubernetesClient> Tiller(final T client,
106                                                               String namespaceHousingTiller,
107                                                               int tillerPort,
108                                                               Map<String, String> tillerLabels) throws MalformedURLException {
109    super();
110    Objects.requireNonNull(client);
111    if (namespaceHousingTiller == null || namespaceHousingTiller.isEmpty()) {
112      namespaceHousingTiller = DEFAULT_NAMESPACE;
113    }
114    if (tillerPort <= 0) {
115      tillerPort = DEFAULT_PORT;
116    }
117    if (tillerLabels == null) {
118      tillerLabels = DEFAULT_LABELS;
119    }
120    this.portForward = Pods.forwardPort(client.getHttpClient(), client.pods().inNamespace(namespaceHousingTiller).withLabels(tillerLabels), tillerPort);
121    this.channel = this.buildChannel(this.portForward);
122  }
123
124
125  /*
126   * Instance methods.
127   */
128  
129
130  protected ManagedChannel buildChannel(final LocalPortForward portForward) {
131    return ManagedChannelBuilder.forAddress(portForward.getLocalAddress().getHostAddress(), portForward.getLocalPort()).usePlaintext(true).build();
132  }
133
134  @Override
135  public void close() throws IOException {
136    if (this.channel != null) {
137      this.channel.shutdownNow();
138    }
139    if (this.portForward != null) {
140      this.portForward.close();
141    }
142  }
143
144  public ReleaseServiceBlockingStub getReleaseServiceBlockingStub() {
145    ReleaseServiceBlockingStub returnValue = null;
146    if (this.channel != null) {
147      returnValue = MetadataUtils.attachHeaders(ReleaseServiceGrpc.newBlockingStub(this.channel), metadata);
148    }
149    return returnValue;
150  }
151
152  public ReleaseServiceFutureStub getReleaseServiceFutureStub() {
153    ReleaseServiceFutureStub returnValue = null;
154    if (this.channel != null) {
155      returnValue = MetadataUtils.attachHeaders(ReleaseServiceGrpc.newFutureStub(this.channel), metadata);
156    }
157    return returnValue;
158  }
159
160  public ReleaseServiceStub getReleaseServiceStub() {
161    ReleaseServiceStub returnValue = null;
162    if (this.channel != null) {
163      returnValue = MetadataUtils.attachHeaders(ReleaseServiceGrpc.newStub(this.channel), metadata);
164    }
165    return returnValue;
166  }
167
168}