From dd3a485c78ad9f23ef60f74c9789d046bc1dca46 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Wed, 6 Sep 2017 23:06:30 -0700 Subject: [PATCH] initial protobuf client. --- examples/pom.xml | 5 + .../client/examples/ProtoExample.java | 47 ++++++++ pom.xml | 1 + proto/pom.xml | 2 +- util/pom.xml | 10 ++ .../io/kubernetes/client/ProtoClient.java | 112 ++++++++++++++++++ 6 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 examples/src/main/java/io/kubernetes/client/examples/ProtoExample.java create mode 100644 util/src/main/java/io/kubernetes/client/ProtoClient.java diff --git a/examples/pom.xml b/examples/pom.xml index 03bdf908de..24df486232 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -24,6 +24,11 @@ client-java-util 0.2-SNAPSHOT + + io.kubernetes + client-java-proto + 0.2-SNAPSHOT + com.google.guava guava diff --git a/examples/src/main/java/io/kubernetes/client/examples/ProtoExample.java b/examples/src/main/java/io/kubernetes/client/examples/ProtoExample.java new file mode 100644 index 0000000000..b78dd5d14b --- /dev/null +++ b/examples/src/main/java/io/kubernetes/client/examples/ProtoExample.java @@ -0,0 +1,47 @@ +/* +Copyright 2017 The Kubernetes Authors. +Licensed 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 io.kubernetes.client.examples; + +import io.kubernetes.client.ApiClient; +import io.kubernetes.client.ApiException; +import io.kubernetes.client.Configuration; +import io.kubernetes.client.ProtoClient; +import io.kubernetes.client.proto.V1.Pod; +import io.kubernetes.client.proto.V1.PodList; +import io.kubernetes.client.util.Config; + +import java.io.IOException; + + +/** + * A simple example of how to use the Java API + * + * Easiest way to run this: + * mvn exec:java -Dexec.mainClass="io.kubernetes.client.examples.ProtoExample" + * + * From inside $REPO_DIR/examples + */ +public class ProtoExample { + public static void main(String[] args) throws IOException, ApiException, InterruptedException { + ApiClient client = Config.defaultClient(); + Configuration.setDefaultApiClient(client); + + ProtoClient pc = new ProtoClient(client); + PodList list = pc.list(PodList.newBuilder(), "/api/v1/namespaces/default/pods"); + + if (list.getItemsCount() > 0) { + Pod p = list.getItems(0); + System.out.println(p.toString()); + } + } +} diff --git a/pom.xml b/pom.xml index 6e15168886..4ee7122476 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,7 @@ util examples kubernetes + proto diff --git a/proto/pom.xml b/proto/pom.xml index 812f546e40..7a53978423 100644 --- a/proto/pom.xml +++ b/proto/pom.xml @@ -2,7 +2,7 @@ 4.0.0 io.kubernetes client-java-proto - 0.1-SNAPSHOT + 0.2-SNAPSHOT jar client-java-proto https://github.com/kubernetes-client/java diff --git a/util/pom.xml b/util/pom.xml index 837eaf324b..92f968360c 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -17,6 +17,11 @@ client-java-api 0.2-SNAPSHOT + + io.kubernetes + client-java-proto + 0.2-SNAPSHOT + org.yaml snakeyaml @@ -59,6 +64,11 @@ system-rules 1.16.1 + + com.google.protobuf + protobuf-java + 3.4.0 + diff --git a/util/src/main/java/io/kubernetes/client/ProtoClient.java b/util/src/main/java/io/kubernetes/client/ProtoClient.java new file mode 100644 index 0000000000..5aefc0b17e --- /dev/null +++ b/util/src/main/java/io/kubernetes/client/ProtoClient.java @@ -0,0 +1,112 @@ +package io.kubernetes.client; + +import io.kubernetes.client.ApiException; +import io.kubernetes.client.Configuration; +import io.kubernetes.client.proto.Runtime.Unknown; + +import com.google.common.io.ByteStreams; +import com.google.protobuf.Message; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; +import com.squareup.okhttp.ResponseBody; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; + +public class ProtoClient { + private ApiClient apiClient; + // Magic number for the beginning of proto encoded. + // https://github.com/kubernetes/apimachinery/blob/master/pkg/runtime/serializer/protobuf/protobuf.go#L42 + private static final byte[] MAGIC = new byte[] { 0x6b, 0x38, 0x73, 0x00 }; + + /** + * Simple Protocol Budder API client constructor, uses default configuration + */ + public ProtoClient() { + this(Configuration.getDefaultApiClient()); + } + + /** + * ProtocolBuffer Client Constructor + * @param apiClient The api client to use. + */ + public ProtoClient(ApiClient apiClient) { + this.apiClient = apiClient; + } + + /** + * Get the API client for these ProtocolBuffer operations. + * @return The API client that will be used. + */ + public ApiClient getApiClient() { + return apiClient; + } + + /** + * Set the API client for subsequent ProtocolBuffer operations. + * @param apiClient The new API client to use. + */ + public void setApiClient(ApiClient apiClient) { + this.apiClient = apiClient; + } + + /** + * Get a Kubernetes API object using protocol buffer encoding. + * @param builder The appropriate Builder for the object receveived from the request. + * @param path The URL path to call (e.g. /api/v1/namespaces/default/pods/pod-name) + * @return A Message of type T + */ + public T get(T.Builder builder, String path) throws ApiException, IOException { + return (T) request(builder, path, "GET"); + } + + /** + * List is fluent, semantic sugar method on top of get, which is intended + * to convey that the object is a List of objects rather than a single object + * @param builder The appropriate Builder for the object receveived from the request. + * @param path The URL path to call (e.g. /api/v1/namespaces/default/pods/pod-name) + * @return A Message of type T + */ + public T list(T.Builder listObj, String path) throws ApiException, IOException { + return get(listObj, path); + } + + /** + * Generic protocol buffer based HTTP request. + * Not intended for general consumption, but public for advance use cases. + * @param builder The appropriate Builder for the object receveived from the request. + * @param method The HTTP method (e.g. GET) for this request. + * @param path The URL path to call (e.g. /api/v1/namespaces/default/pods/pod-name) + * @return A Message of type T + */ + public T request(T.Builder builder, String path, String method) throws ApiException, IOException { + HashMap headers = new HashMap(); + headers.put("Content-type", "application/vnd.kubernetes.protobuf"); + headers.put("Accept", "application/vnd.kubernetes.protobuf"); + Request request = apiClient.buildRequest(path, method, new ArrayList(), new ArrayList(), null, + headers, new HashMap(), new String[0], null); + Response resp = apiClient.getHttpClient().newCall(request).execute(); + Unknown u = parse(resp.body().byteStream()); + return (T) builder.mergeFrom(u.getRaw()).build(); + } + + private Unknown parse(InputStream stream) throws ApiException, IOException { + // This isn't really documented anywhere except the code, but + // the proto-buf format is: + // * 4 byte magic number + // * Protocol Buffer encoded object of type runtime.Unknown + // * the 'raw' field in that object contains a Protocol Buffer + // encoding of the actual object. + // TODO: Document this somewhere proper. + byte[] magic = new byte[4]; + stream.read(magic); + for (int i = 0; i < MAGIC.length; i++) { + if (magic[i] != MAGIC[i]) { + throw new ApiException("Unexpected magic number: " + magic); + } + } + return Unknown.parseFrom(stream); + } +} \ No newline at end of file