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}