kuberentes: roll back recent Kubernetes client changes

https://golang.org/cl/21765 was not tested and not adequately reviewed
and doesn't work at all. Roll it back for now, since the blind fix in
https://golang.org/cl/21765 helped a bit but wasn't enough.

Change-Id: I781fac04d019fa5e6120ec398e13671b89d802c5
Reviewed-on: https://go-review.googlesource.com/25043
Reviewed-by: Johan Euphrosine <proppy@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/kubernetes/client.go b/kubernetes/client.go
index 94445e4..9e77b8c 100644
--- a/kubernetes/client.go
+++ b/kubernetes/client.go
@@ -9,9 +9,7 @@
 	"bufio"
 	"bytes"
 	"encoding/json"
-	"errors"
 	"fmt"
-	"io"
 	"io/ioutil"
 	"log"
 	"net/http"
@@ -26,27 +24,12 @@
 
 const (
 	// APIEndpoint defines the base path for kubernetes API resources.
-	APIEndpoint       = "/api/v1"
-	defaultPodNS      = "/namespaces/default/pods"
-	defaultSecretNS   = "/namespaces/default/secrets"
-	defaultWatchPodNS = "/watch/namespaces/default/pods"
-	nodes             = "/nodes"
+	APIEndpoint     = "/api/v1"
+	defaultPod      = "/namespaces/default/pods"
+	defaultWatchPod = "/watch/namespaces/default/pods"
+	nodes           = "/nodes"
 )
 
-// ErrSecretNotFound is returned by GetSecret when a secret is not found.
-var ErrSecretNotFound = errors.New("kubernetes: secret not found")
-
-// APIError is returned by Client methods when an API call failed.
-type APIError struct {
-	StatusCode int
-	Body       string
-	Header     http.Header
-}
-
-func (e *APIError) Error() string {
-	return fmt.Sprintf("API error %d: %q", e.StatusCode, e.Body)
-}
-
 // Client is a client for the Kubernetes master.
 type Client struct {
 	endpointURL string
@@ -77,9 +60,30 @@
 // An error is returned if the pod can not be created, or if ctx.Done
 // is closed.
 func (c *Client) RunLongLivedPod(ctx context.Context, 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 + defaultPod
+	req, err := http.NewRequest("POST", postURL, &podJSON)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: POST %q : %v", postURL, err)
+	}
+	res, err := ctxhttp.Do(ctx, c.httpClient, req)
+	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 := c.do(ctx, &podResult, "POST", wantCreated, defaultPodNS, pod); err != nil {
-		return nil, err
+	if err := json.Unmarshal(body, &podResult); err != nil {
+		return nil, fmt.Errorf("failed to decode pod resources: %v", err)
 	}
 
 	for {
@@ -112,16 +116,54 @@
 
 // GetPods returns all pods in the cluster, regardless of status.
 func (c *Client) GetPods(ctx context.Context) ([]api.Pod, error) {
-	var res api.PodList
-	if err := c.do(ctx, &res, "GET", wantOK, c.endpointURL+defaultPodNS, nil); err != nil {
-		return nil, err
+	getURL := c.endpointURL + defaultPod
+
+	// Make request to Kubernetes API
+	req, err := http.NewRequest("GET", getURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: GET %q : %v", getURL, err)
 	}
-	return res.Items, nil
+	res, err := ctxhttp.Do(ctx, c.httpClient, req)
+	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)
+	}
+
+	var podList api.PodList
+	if err := json.Unmarshal(body, &podList); err != nil {
+		return nil, fmt.Errorf("failed to decode list of pod resources: %v", err)
+	}
+	return podList.Items, nil
 }
 
 // PodDelete deletes the specified Kubernetes pod.
 func (c *Client) DeletePod(ctx context.Context, podName string) error {
-	return c.do(ctx, nil, "DELETE", wantOK, defaultPodNS+"/"+podName, nil)
+	url := c.endpointURL + defaultPod + "/" + podName
+	req, err := http.NewRequest("DELETE", url, nil)
+	if err != nil {
+		return fmt.Errorf("failed to create request: DELETE %q : %v", url, err)
+	}
+	res, err := ctxhttp.Do(ctx, c.httpClient, req)
+	if err != nil {
+		return fmt.Errorf("failed to make request: DELETE %q: %v", url, err)
+	}
+	body, err := ioutil.ReadAll(res.Body)
+	res.Body.Close()
+	if err != nil {
+		return fmt.Errorf("failed to read response body: DELETE %q: %v, url, err")
+	}
+	if res.StatusCode != http.StatusOK {
+		return fmt.Errorf("http error: %d DELETE %q: %q: %v", res.StatusCode, url, string(body), err)
+	}
+	return nil
 }
 
 // TODO(bradfitz): WatchPod is unreliable, so this is disabled.
@@ -204,7 +246,7 @@
 		defer cancel()
 
 		// Make request to Kubernetes API
-		getURL := c.endpointURL + defaultWatchPodNS + "/" + podName
+		getURL := c.endpointURL + defaultWatchPod + "/" + podName
 		req, err := http.NewRequest("GET", getURL, nil)
 		req.URL.Query().Add("resourceVersion", podResourceVersion)
 		if err != nil {
@@ -267,9 +309,30 @@
 // Retrieve the status of a pod synchronously from the Kube
 // API server.
 func (c *Client) PodStatus(ctx context.Context, podName string) (*api.PodStatus, error) {
-	var pod api.Pod
-	if err := c.do(ctx, &pod, "GET", wantOK, defaultPodNS+"/"+podName, nil); err != nil {
-		return nil, err
+	getURL := c.endpointURL + defaultPod + "/" + podName
+
+	// Make request to Kubernetes API
+	req, err := http.NewRequest("GET", getURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: GET %q : %v", getURL, err)
+	}
+	res, err := ctxhttp.Do(ctx, c.httpClient, req)
+	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)
+	}
+
+	var pod *api.Pod
+	if err := json.Unmarshal(body, &pod); err != nil {
+		return nil, fmt.Errorf("failed to decode pod resources: %v", err)
 	}
 	return &pod.Status, nil
 }
@@ -278,88 +341,48 @@
 // in the pod.
 func (c *Client) PodLog(ctx context.Context, podName string) (string, error) {
 	// TODO(evanbrown): support multiple containers
-	var logs string
-	if err := c.do(ctx, &logs, "GET", wantOK, defaultPodNS+"/"+podName+"/log", nil); err != nil {
-		return "", err
+	url := c.endpointURL + defaultPod + "/" + podName + "/log"
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return "", fmt.Errorf("failed to create request: GET %q : %v", url, err)
 	}
-	return logs, nil
+	res, err := ctxhttp.Do(ctx, c.httpClient, req)
+	if err != nil {
+		return "", fmt.Errorf("failed to make request: GET %q: %v", url, err)
+	}
+	body, err := ioutil.ReadAll(res.Body)
+	res.Body.Close()
+	if err != nil {
+		return "", fmt.Errorf("failed to read response body: GET %q: %v", url, err)
+	}
+	if res.StatusCode != http.StatusOK {
+		return "", fmt.Errorf("http error %d GET %q: %q: %v", res.StatusCode, url, string(body), err)
+	}
+	return string(body), nil
 }
 
 // PodNodes returns the list of nodes that comprise the Kubernetes cluster
 func (c *Client) GetNodes(ctx context.Context) ([]api.Node, error) {
-	var res api.NodeList
-	if err := c.do(ctx, &res, "GET", wantOK, nodes, nil); err != nil {
-		return nil, err
-	}
-	return res.Items, nil
-}
-
-// CreateSecret creates a new secret resource in the default secret namespace with
-// the given secret.
-// It returns a new secret instance corresponding to the server side representation.
-func (c *Client) CreateSecret(ctx context.Context, secret *api.Secret) (*api.Secret, error) {
-	var res api.Secret
-	if err := c.do(ctx, &res, "POST", wantCreated, defaultSecretNS, secret); err != nil {
-		return nil, err
-	}
-	return &res, nil
-}
-
-// GetSecret returns the specified secret from the default secret namespace.
-// If the secret is not found, the err will be ErrSecretNotFound.
-func (c *Client) GetSecret(ctx context.Context, name string) (*api.Secret, error) {
-	var res api.Secret
-	if err := c.do(ctx, &res, "GET", wantOK, defaultSecretNS+"/"+name, nil); err != nil {
-		return nil, err
-	}
-	return &res, nil
-}
-
-func wantOK(code int) bool      { return code == http.StatusOK }
-func wantCreated(code int) bool { return code == http.StatusCreated }
-
-func (c *Client) do(ctx context.Context, dst interface{}, method string, checkResStatus func(int) bool, path string, payload interface{}) error {
-	var body io.Reader
-	if payload != nil {
-		buf := new(bytes.Buffer)
-		if err := json.NewEncoder(buf).Encode(payload); err != nil {
-			return fmt.Errorf("failed encode json payload: %v", err)
-		}
-		body = buf
-	}
-	req, err := http.NewRequest(method, c.endpointURL+path, body)
+	url := c.endpointURL + nodes
+	req, err := http.NewRequest("GET", url, nil)
 	if err != nil {
-		return fmt.Errorf("failed to create request: %s %q : %v", method, path, err)
+		return nil, fmt.Errorf("failed to create request: GET %q : %v", url, err)
 	}
-	resp, err := ctxhttp.Do(ctx, c.httpClient, req)
+	res, err := ctxhttp.Do(ctx, c.httpClient, req)
 	if err != nil {
-		return fmt.Errorf("failed to perform request: %s %q: %v", method, path, err)
+		return nil, fmt.Errorf("failed to make request: GET %q: %v", url, err)
 	}
-	defer resp.Body.Close()
-	if !checkResStatus(resp.StatusCode) {
-		body, _ := ioutil.ReadAll(resp.Body)
-		return &APIError{
-			StatusCode: resp.StatusCode,
-			Body:       string(body),
-			Header:     resp.Header,
-		}
+	body, err := ioutil.ReadAll(res.Body)
+	res.Body.Close()
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response body: GET %q: %v, url, err")
 	}
-
-	switch dst := dst.(type) {
-	case nil:
-		return nil
-	case *string:
-		// string dest
-		bs, err := ioutil.ReadAll(resp.Body)
-		if err != nil {
-			return fmt.Errorf("failed to read raw body: %v", err)
-		}
-		*dst = string(bs)
-	default:
-		// json dest
-		if err := json.NewDecoder(resp.Body).Decode(dst); err != nil {
-			return fmt.Errorf("failed to decode API response: %v", err)
-		}
+	if res.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("http error %d GET %q: %q: %v", res.StatusCode, url, string(body), err)
 	}
-	return nil
+	var nodeList *api.NodeList
+	if err := json.Unmarshal(body, &nodeList); err != nil {
+		return nil, fmt.Errorf("failed to decode node list: %v", err)
+	}
+	return nodeList.Items, nil
 }