kubernetes: add minimal kubernetes API client
Change-Id: I4ca7c27084d5f80f116b8461d0e6769b8bd99ceb
Reviewed-on: https://go-review.googlesource.com/9981
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/kubernetes/client.go b/kubernetes/client.go
new file mode 100644
index 0000000..5f8a713
--- /dev/null
+++ b/kubernetes/client.go
@@ -0,0 +1,103 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package kubernetes contains a minimal client for the Kubernetes API.
+package kubernetes
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+
+ api "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3"
+)
+
+const (
+ // APIEndpoint defines the base path for kubernetes API resources.
+ APIEndpoint = "/api/v1beta3"
+ defaultPodNS = "/namespaces/default/pods"
+)
+
+// Client is a client for the Kubernetes master.
+type Client struct {
+ endpointURL string
+ httpClient *http.Client
+}
+
+// NewClient returns a new Kubernetes client.
+// The provided host is an url (scheme://hostname[:port]) of a
+// Kubernetes master without any path.
+// The provided client is an authorized http.Client used to perform requests to the Kubernetes API master.
+func NewClient(baseURL string, client *http.Client) (*Client, error) {
+ validURL, err := url.Parse(baseURL)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse URL %q: %v", baseURL, err)
+ }
+ return &Client{
+ endpointURL: strings.TrimSuffix(validURL.String(), "/") + APIEndpoint,
+ httpClient: client,
+ }, nil
+}
+
+// RunPod create a new pod resource in the default pod namespace with
+// the given pod API specification.
+// It returns the pod status once it is not pending anymore.
+func (c *Client) Run(pod *api.Pod) (*api.PodStatus, error) {
+ var podJSON bytes.Buffer
+ if err := json.NewEncoder(&podJSON).Encode(pod); err != nil {
+ return nil, fmt.Errorf("failed to encode pod in json: %v", err)
+ }
+ postURL := c.endpointURL + defaultPodNS
+ r, err := http.NewRequest("POST", postURL, &podJSON)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create request: POST %q : %v", postURL, err)
+ }
+ res, err := c.httpClient.Do(r)
+ if err != nil {
+ return nil, fmt.Errorf("failed to make request: POST %q: %v", postURL, err)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ res.Body.Close()
+ if err != nil {
+ return nil, fmt.Errorf("failed to read request body for POST %q: %v", postURL, err)
+ }
+ if res.StatusCode != http.StatusCreated {
+ return nil, fmt.Errorf("http error: %d POST %q: %q: %v", res.StatusCode, postURL, string(body), err)
+ }
+ var podResult api.Pod
+ if err := json.Unmarshal(body, &podResult); err != nil {
+ return nil, fmt.Errorf("failed to decode pod resources: %v", err)
+ }
+ for podResult.Status.Phase == "Pending" {
+ getURL := c.endpointURL + defaultPodNS + "/" + pod.Name
+ r, err := http.NewRequest("GET", getURL, nil)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create request: GET %q : %v", getURL, err)
+ }
+ res, err := c.httpClient.Do(r)
+ if err != nil {
+ return nil, fmt.Errorf("failed to make request: GET %q: %v", getURL, err)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ res.Body.Close()
+ if err != nil {
+ return nil, fmt.Errorf("failed to read request body for GET %q: %v", getURL, err)
+ }
+ if res.StatusCode != http.StatusOK {
+ return nil, fmt.Errorf("http error %d GET %q: %q: %v", res.StatusCode, getURL, string(body), err)
+ }
+ if err := json.Unmarshal(body, &podResult); err != nil {
+ return nil, fmt.Errorf("failed to decode pod resources: %v", err)
+ }
+ time.Sleep(1 * time.Second)
+ // TODO(proppy): add a Cancel type to this func later
+ // so this can select on it.
+ }
+ return &podResult.Status, nil
+}
diff --git a/kubernetes/client_test.go b/kubernetes/client_test.go
new file mode 100644
index 0000000..d439122
--- /dev/null
+++ b/kubernetes/client_test.go
@@ -0,0 +1,40 @@
+package kubernetes_test
+
+import (
+ "log"
+ "net/http"
+
+ api "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3"
+ "golang.org/x/build/kubernetes"
+ "golang.org/x/oauth2"
+)
+
+func ExampleRun() {
+ kube, err := kubernetes.NewClient("example.com", &http.Client{
+ Transport: &oauth2.Transport{
+ Source: oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "aCcessWbU3toKen"}),
+ }})
+ if err != nil {
+ log.Fatalf("failed to create client: %v", err)
+ }
+ kube.Run(&api.Pod{
+ TypeMeta: api.TypeMeta{
+ APIVersion: "v1beta3",
+ Kind: "Pod",
+ },
+ ObjectMeta: api.ObjectMeta{
+ Name: "my-nginx-pod",
+ Labels: map[string]string{
+ "tag": "prod",
+ },
+ },
+ Spec: api.PodSpec{
+ Containers: []api.Container{
+ {
+ Name: "my-nginx-container",
+ Image: "nginx:latest",
+ },
+ },
+ },
+ })
+}