mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-15 14:14:39 +00:00
Pod log subresource
Adds a Log subresource to Pod storage. The Log subresource implements rest.GetterWithOptions and produces a ResourceStreamer resource that will stream the log output from the pod's host node.
This commit is contained in:
19
pkg/registry/generic/rest/doc.go
Normal file
19
pkg/registry/generic/rest/doc.go
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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 rest has generic implementations of resources used for
|
||||
// REST responses
|
||||
package rest
|
69
pkg/registry/generic/rest/streamer.go
Normal file
69
pkg/registry/generic/rest/streamer.go
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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 rest
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
|
||||
)
|
||||
|
||||
// LocationStreamer is a resource that streams the contents of a particular
|
||||
// location URL
|
||||
type LocationStreamer struct {
|
||||
Location *url.URL
|
||||
Transport http.RoundTripper
|
||||
ContentType string
|
||||
Flush bool
|
||||
}
|
||||
|
||||
// a LocationStreamer must implement a rest.ResourceStreamer
|
||||
var _ rest.ResourceStreamer = &LocationStreamer{}
|
||||
|
||||
// IsAnAPIObject marks this object as a runtime.Object
|
||||
func (*LocationStreamer) IsAnAPIObject() {}
|
||||
|
||||
// InputStream returns a stream with the contents of the URL location. If no location is provided,
|
||||
// a null stream is returned.
|
||||
func (s *LocationStreamer) InputStream(apiVersion, acceptHeader string) (stream io.ReadCloser, flush bool, contentType string, err error) {
|
||||
if s.Location == nil {
|
||||
// If no location was provided, return a null stream
|
||||
return nil, false, "", nil
|
||||
}
|
||||
transport := s.Transport
|
||||
if transport == nil {
|
||||
transport = http.DefaultTransport
|
||||
}
|
||||
client := &http.Client{Transport: transport}
|
||||
resp, err := client.Get(s.Location.String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
contentType = s.ContentType
|
||||
if len(contentType) == 0 {
|
||||
contentType = resp.Header.Get("Content-Type")
|
||||
if len(contentType) > 0 {
|
||||
contentType = strings.TrimSpace(strings.SplitN(contentType, ";", 2)[0])
|
||||
}
|
||||
}
|
||||
flush = s.Flush
|
||||
stream = resp.Body
|
||||
return
|
||||
}
|
118
pkg/registry/generic/rest/streamer_test.go
Normal file
118
pkg/registry/generic/rest/streamer_test.go
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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 rest
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInputStreamReader(t *testing.T) {
|
||||
resultString := "Test output"
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write([]byte(resultString))
|
||||
}))
|
||||
defer s.Close()
|
||||
u, err := url.Parse(s.URL)
|
||||
if err != nil {
|
||||
t.Errorf("Error parsing server URL: %v", err)
|
||||
return
|
||||
}
|
||||
streamer := &LocationStreamer{
|
||||
Location: u,
|
||||
}
|
||||
readCloser, _, _, err := streamer.InputStream("v1beta1", "text/plain")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when getting stream: %v", err)
|
||||
return
|
||||
}
|
||||
defer readCloser.Close()
|
||||
result, err := ioutil.ReadAll(readCloser)
|
||||
if string(result) != resultString {
|
||||
t.Errorf("Stream content does not match. Got: %s. Expected: %s.", string(result), resultString)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInputStreamNullLocation(t *testing.T) {
|
||||
streamer := &LocationStreamer{
|
||||
Location: nil,
|
||||
}
|
||||
readCloser, _, _, err := streamer.InputStream("v1beta1", "text/plain")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when getting stream with null location: %v", err)
|
||||
}
|
||||
if readCloser != nil {
|
||||
t.Errorf("Expected stream to be nil. Got: %#v", readCloser)
|
||||
}
|
||||
}
|
||||
|
||||
type testTransport struct {
|
||||
body string
|
||||
err error
|
||||
}
|
||||
|
||||
func (tt *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
r := bufio.NewReader(bytes.NewBufferString(tt.body))
|
||||
return http.ReadResponse(r, req)
|
||||
}
|
||||
|
||||
func fakeTransport(mime, message string) http.RoundTripper {
|
||||
content := fmt.Sprintf("HTTP/1.1 200 OK\nContent-Type: %s\n\n%s", mime, message)
|
||||
return &testTransport{body: content}
|
||||
}
|
||||
|
||||
func TestInputStreamContentType(t *testing.T) {
|
||||
location, _ := url.Parse("http://www.example.com")
|
||||
streamer := &LocationStreamer{
|
||||
Location: location,
|
||||
Transport: fakeTransport("application/json", "hello world"),
|
||||
}
|
||||
readCloser, _, contentType, err := streamer.InputStream("v1beta1", "text/plain")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when getting stream: %v", err)
|
||||
return
|
||||
}
|
||||
defer readCloser.Close()
|
||||
if contentType != "application/json" {
|
||||
t.Errorf("Unexpected content type. Got: %s. Expected: application/json", contentType)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInputStreamTransport(t *testing.T) {
|
||||
message := "hello world"
|
||||
location, _ := url.Parse("http://www.example.com")
|
||||
streamer := &LocationStreamer{
|
||||
Location: location,
|
||||
Transport: fakeTransport("text/plain", message),
|
||||
}
|
||||
readCloser, _, _, err := streamer.InputStream("v1beta1", "text/plain")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when getting stream: %v", err)
|
||||
return
|
||||
}
|
||||
defer readCloser.Close()
|
||||
result, err := ioutil.ReadAll(readCloser)
|
||||
if string(result) != message {
|
||||
t.Errorf("Stream content does not match. Got: %s. Expected: %s.", string(result), message)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user