From 53ca742c66bf696f6edc01c8e962ac0ef59a03b1 Mon Sep 17 00:00:00 2001 From: Jake Sanders Date: Tue, 17 Dec 2019 16:09:51 +0000 Subject: [PATCH] Update the util/kubernetes client to retrieve logs --- debug/log/kubernetes/kubernetes.go | 4 +++- debug/log/kubernetes/kubernetes_test.go | 15 +++++++++++---- debug/log/kubernetes/log.go | 15 +++++++++++++++ debug/log/log.go | 8 ++++---- util/kubernetes/client/api/api_test.go | 11 +++++++++++ util/kubernetes/client/api/request.go | 25 +++++++++++++++++++++++++ util/kubernetes/client/client.go | 16 ++++++++++++++++ 7 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 debug/log/kubernetes/log.go diff --git a/debug/log/kubernetes/kubernetes.go b/debug/log/kubernetes/kubernetes.go index 65841ef8..529b5d7c 100644 --- a/debug/log/kubernetes/kubernetes.go +++ b/debug/log/kubernetes/kubernetes.go @@ -9,7 +9,9 @@ type klog struct{} func (k *klog) Read(...log.ReadOption) []log.Record { return nil } -func (k *klog) Write(log.Record) {} +func (k *klog) Write(l log.Record) { + write(l) +} func (k *klog) Stream(stop chan bool) <-chan log.Record { c := make(chan log.Record) diff --git a/debug/log/kubernetes/kubernetes_test.go b/debug/log/kubernetes/kubernetes_test.go index af9ce9b3..c1eca93d 100644 --- a/debug/log/kubernetes/kubernetes_test.go +++ b/debug/log/kubernetes/kubernetes_test.go @@ -2,6 +2,7 @@ package kubernetes import ( "bytes" + "encoding/json" "io" "os" "testing" @@ -13,6 +14,7 @@ import ( func TestKubernetes(t *testing.T) { k := New() + r, w, err := os.Pipe() if err != nil { t.Fatal(err) @@ -20,17 +22,22 @@ func TestKubernetes(t *testing.T) { s := os.Stderr os.Stderr = w meta := make(map[string]string) - meta["foo"] = "bar" - k.Write(log.Record{ + write := log.Record{ Timestamp: time.Unix(0, 0), Value: "Test log entry", Metadata: meta, - }) + } + meta["foo"] = "bar" + k.Write(write) b := &bytes.Buffer{} w.Close() io.Copy(b, r) os.Stderr = s - assert.Equal(t, "Test log entry", b.String(), "Write was not equal") + var read log.Record + if err := json.Unmarshal(b.Bytes(), &read); err != nil { + t.Fatalf("json.Unmarshal failed: %s", err.Error()) + } + assert.Equal(t, write, read, "Write was not equal") assert.Nil(t, k.Read(), "Read should be unimplemented") diff --git a/debug/log/kubernetes/log.go b/debug/log/kubernetes/log.go new file mode 100644 index 00000000..ea352165 --- /dev/null +++ b/debug/log/kubernetes/log.go @@ -0,0 +1,15 @@ +package kubernetes + +import "github.com/micro/go-micro/debug/log" + +import ( + "encoding/json" + "fmt" + "os" +) + +func write(l log.Record) { + if m, err := json.Marshal(l); err == nil { + fmt.Fprintf(os.Stderr, "%s", m) + } +} diff --git a/debug/log/log.go b/debug/log/log.go index 42a8f558..f4ad0d76 100644 --- a/debug/log/log.go +++ b/debug/log/log.go @@ -29,14 +29,14 @@ type Log interface { // Record is log record entry type Record struct { // Timestamp of logged event - Timestamp time.Time + Timestamp time.Time `json:"time"` // Value contains log entry - Value interface{} + Value interface{} `json:"value"` // Metadata to enrich log record - Metadata map[string]string + Metadata map[string]string `json:"metadata"` } -// level is a log level +// Level is a log level type Level int const ( diff --git a/util/kubernetes/client/api/api_test.go b/util/kubernetes/client/api/api_test.go index 474dff55..f1f45fdd 100644 --- a/util/kubernetes/client/api/api_test.go +++ b/util/kubernetes/client/api/api_test.go @@ -111,6 +111,17 @@ var tests = []testcase{ URI: "/apis/apps/v1/namespaces/default/deployments/baz", Header: map[string]string{"foo": "bar"}, }, + testcase{ + ReqFn: func(opts *Options) *Request { + return NewRequest(opts). + Get(). + Resource("pod"). + SubResource("log"). + Name("foolog") + }, + Method: "GET", + URI: "/api/v1/namespaces/default/pods/foolog/log", + }, } var wrappedHandler = func(test *testcase, t *testing.T) http.HandlerFunc { diff --git a/util/kubernetes/client/api/request.go b/util/kubernetes/client/api/request.go index 1b454c00..53d57195 100644 --- a/util/kubernetes/client/api/request.go +++ b/util/kubernetes/client/api/request.go @@ -23,6 +23,7 @@ type Request struct { resource string resourceName *string + subResource *string body io.Reader err error @@ -79,6 +80,13 @@ func (r *Request) Resource(s string) *Request { return r } +// SubResource sets a subresource on a resource, +// e.g. pods/log for pod logs +func (r *Request) SubResource(s string) *Request { + r.subResource = &s + return r +} + // Name is for targeting a specific resource by id func (r *Request) Name(s string) *Request { r.resourceName = &s @@ -154,6 +162,9 @@ func (r *Request) request() (*http.Request, error) { // append resourceName if it is present if r.resourceName != nil { url += *r.resourceName + if r.subResource != nil { + url += "/" + *r.subResource + } } // append any query params @@ -202,6 +213,20 @@ func (r *Request) Do() *Response { return newResponse(res, err) } +// Raw performs a Raw HTTP request to the Kubernetes API +func (r *Request) Raw() (*http.Response, error) { + req, err := r.request() + if err != nil { + return nil, err + } + + res, err := r.client.Do(req) + if err != nil { + return nil, err + } + return res, nil +} + // Options ... type Options struct { Host string diff --git a/util/kubernetes/client/client.go b/util/kubernetes/client/client.go index 3680863f..821ee3da 100644 --- a/util/kubernetes/client/client.go +++ b/util/kubernetes/client/client.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/tls" "errors" + "io" "io/ioutil" "net/http" "os" @@ -116,6 +117,21 @@ func (c *client) Get(r *Resource, labels map[string]string) error { Into(r.Value) } +// Logs returns logs for a pod +func (c *client) Logs(podName string) (io.ReadCloser, error) { + req := api.NewRequest(c.opts). + Get(). + Resource("pod"). + SubResource("log"). + Name(podName) + + resp, err := req.Raw() + if err != nil { + return nil, err + } + return resp.Body, nil +} + // Update updates API object func (c *client) Update(r *Resource) error { req := api.NewRequest(c.opts).