mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
Make RESTClient more generic to API version, simplify version handling
RESTClient is an abstraction on top of arbitrary HTTP endpoints that follow the Kubernetes API conventions. Refactored RESTClientFor so that assumptions that are Kube specific happen outside of that method (so others can reuse the RESTClient). Added more validation to client.New to ensure clients give good input. Exposed APIVersion on RESTClient as a method so that wrapper code (code that adds typed / structured methods over rest endpoints like client.Client) can more easily make decisions about what APIVersion it is running under.
This commit is contained in:
parent
d314862e46
commit
b03fbf90f8
@ -44,7 +44,7 @@ func (c *Client) ReplicationControllers(namespace string) ReplicationControllerI
|
||||
}
|
||||
|
||||
func (c *Client) Nodes() NodeInterface {
|
||||
return newNodes(c, c.preV1Beta3)
|
||||
return newNodes(c)
|
||||
}
|
||||
|
||||
func (c *Client) Events(namespace string) EventInterface {
|
||||
@ -78,9 +78,6 @@ type APIStatus interface {
|
||||
// Client is the implementation of a Kubernetes client.
|
||||
type Client struct {
|
||||
*RESTClient
|
||||
|
||||
// preV1Beta3 is true for v1beta1 and v1beta2
|
||||
preV1Beta3 bool
|
||||
}
|
||||
|
||||
// ServerVersion retrieves and parses the server's version.
|
||||
@ -132,3 +129,8 @@ func IsTimeout(err error) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// preV1Beta3 returns true if the provided API version is an API introduced before v1beta3.
|
||||
func preV1Beta3(version string) bool {
|
||||
return version == "v1beta1" || version == "v1beta2"
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ func (c *testClient) ValidateCommon(t *testing.T, err error) {
|
||||
// buildResourcePath is a convenience function for knowing if a namespace should be in a path param or not
|
||||
func buildResourcePath(namespace, resource string) string {
|
||||
if len(namespace) > 0 {
|
||||
if NamespaceInPathFor(testapi.Version()) {
|
||||
if !(testapi.Version() == "v1beta1" || testapi.Version() == "v1beta2") {
|
||||
return path.Join("ns", namespace, resource)
|
||||
}
|
||||
}
|
||||
@ -183,7 +183,7 @@ func buildQueryValues(namespace string, query url.Values) url.Values {
|
||||
}
|
||||
}
|
||||
if len(namespace) > 0 {
|
||||
if !NamespaceInPathFor(testapi.Version()) {
|
||||
if testapi.Version() == "v1beta1" || testapi.Version() == "v1beta2" {
|
||||
v.Set("namespace", namespace)
|
||||
}
|
||||
}
|
||||
@ -765,7 +765,7 @@ func TestNewMinionPath(t *testing.T) {
|
||||
Response: Response{StatusCode: 200},
|
||||
}
|
||||
cl := c.Setup()
|
||||
cl.preV1Beta3 = false
|
||||
cl.apiVersion = "v1beta3"
|
||||
err := cl.Nodes().Delete("foo")
|
||||
c.Validate(t, nil, err)
|
||||
}
|
||||
|
@ -90,23 +90,28 @@ func (f HTTPClientFunc) Do(req *http.Request) (*http.Response, error) {
|
||||
type FakeRESTClient struct {
|
||||
Client HTTPClient
|
||||
Codec runtime.Codec
|
||||
Legacy bool
|
||||
Req *http.Request
|
||||
Resp *http.Response
|
||||
Err error
|
||||
}
|
||||
|
||||
func (c *FakeRESTClient) Get() *Request {
|
||||
return NewRequest(c, "GET", &url.URL{Host: "localhost"}, c.Codec, true)
|
||||
return NewRequest(c, "GET", &url.URL{Host: "localhost"}, c.Codec, c.Legacy, c.Legacy)
|
||||
}
|
||||
|
||||
func (c *FakeRESTClient) Put() *Request {
|
||||
return NewRequest(c, "PUT", &url.URL{Host: "localhost"}, c.Codec, true)
|
||||
return NewRequest(c, "PUT", &url.URL{Host: "localhost"}, c.Codec, c.Legacy, c.Legacy)
|
||||
}
|
||||
|
||||
func (c *FakeRESTClient) Post() *Request {
|
||||
return NewRequest(c, "POST", &url.URL{Host: "localhost"}, c.Codec, true)
|
||||
return NewRequest(c, "POST", &url.URL{Host: "localhost"}, c.Codec, c.Legacy, c.Legacy)
|
||||
}
|
||||
|
||||
func (c *FakeRESTClient) Delete() *Request {
|
||||
return NewRequest(c, "DELETE", &url.URL{Host: "localhost"}, c.Codec, true)
|
||||
return NewRequest(c, "DELETE", &url.URL{Host: "localhost"}, c.Codec, c.Legacy, c.Legacy)
|
||||
}
|
||||
|
||||
func (c *FakeRESTClient) Do(req *http.Request) (*http.Response, error) {
|
||||
c.Req = req
|
||||
if c.Client != HTTPClient(nil) {
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// Config holds the common attributes that can be passed to a Kubernetes client on
|
||||
@ -34,9 +35,19 @@ type Config struct {
|
||||
// Prefix is the sub path of the server. If not specified, the client will set
|
||||
// a default value. Use "/" to indicate the server root should be used
|
||||
Prefix string
|
||||
// Version is the API version to talk to. If not specified, the client will use
|
||||
// the preferred version.
|
||||
// Version is the API version to talk to. Must be provided when initializing
|
||||
// a RESTClient directly. When initializing a Client, will be set with the default
|
||||
// code version.
|
||||
Version string
|
||||
// LegacyBehavior defines whether the RESTClient should follow conventions that
|
||||
// existed prior to v1beta3 in Kubernetes - namely, namespace (if specified)
|
||||
// not being part of the path, and resource names allowing mixed case. Set to
|
||||
// true when using Kubernetes v1beta1 or v1beta2.
|
||||
LegacyBehavior bool
|
||||
// Codec specifies the encoding and decoding behavior for runtime.Objects passed
|
||||
// to a RESTClient or Client. Required when initializing a RESTClient, optional
|
||||
// when initializing a Client.
|
||||
Codec runtime.Codec
|
||||
|
||||
// Server requires Basic authentication
|
||||
Username string
|
||||
@ -80,16 +91,14 @@ type KubeletConfig struct {
|
||||
// is not valid.
|
||||
func New(c *Config) (*Client, error) {
|
||||
config := *c
|
||||
if config.Prefix == "" {
|
||||
config.Prefix = "/api"
|
||||
if err := SetKubernetesDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := RESTClientFor(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version := defaultVersionFor(&config)
|
||||
isPreV1Beta3 := (version == "v1beta1" || version == "v1beta2")
|
||||
return &Client{client, isPreV1Beta3}, nil
|
||||
return &Client{client}, nil
|
||||
}
|
||||
|
||||
// NewOrDie creates a Kubernetes client and panics if the provided API version is not recognized.
|
||||
@ -101,15 +110,37 @@ func NewOrDie(c *Config) *Client {
|
||||
return client
|
||||
}
|
||||
|
||||
// RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config
|
||||
// object.
|
||||
func RESTClientFor(config *Config) (*RESTClient, error) {
|
||||
version := defaultVersionFor(config)
|
||||
|
||||
// Set version
|
||||
// SetKubernetesDefaults sets default values on the provided client config for accessing the
|
||||
// Kubernetes API or returns an error if any of the defaults are impossible or invalid.
|
||||
func SetKubernetesDefaults(config *Config) error {
|
||||
if config.Prefix == "" {
|
||||
config.Prefix = "/api"
|
||||
}
|
||||
if len(config.Version) == 0 {
|
||||
config.Version = defaultVersionFor(config)
|
||||
}
|
||||
version := config.Version
|
||||
versionInterfaces, err := latest.InterfacesFor(version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("API version '%s' is not recognized (valid values: %s)", version, strings.Join(latest.Versions, ", "))
|
||||
return fmt.Errorf("API version '%s' is not recognized (valid values: %s)", version, strings.Join(latest.Versions, ", "))
|
||||
}
|
||||
if config.Codec == nil {
|
||||
config.Codec = versionInterfaces.Codec
|
||||
}
|
||||
config.LegacyBehavior = (version == "v1beta1" || version == "v1beta2")
|
||||
return nil
|
||||
}
|
||||
|
||||
// RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config
|
||||
// object. Note that a RESTClient may require fields that are optional when initializing a Client.
|
||||
// A RESTClient created by this method is generic - it expects to operate on an API that follows
|
||||
// the Kubernetes conventions, but may not be the Kubernetes API.
|
||||
func RESTClientFor(config *Config) (*RESTClient, error) {
|
||||
if len(config.Version) == 0 {
|
||||
return nil, fmt.Errorf("version is required when initializing a RESTClient")
|
||||
}
|
||||
if config.Codec == nil {
|
||||
return nil, fmt.Errorf("Codec is required when initializing a RESTClient")
|
||||
}
|
||||
|
||||
baseURL, err := defaultServerUrlFor(config)
|
||||
@ -117,7 +148,7 @@ func RESTClientFor(config *Config) (*RESTClient, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := NewRESTClient(baseURL, versionInterfaces.Codec, NamespaceInPathFor(version))
|
||||
client := NewRESTClient(baseURL, config.Version, config.Codec, config.LegacyBehavior)
|
||||
|
||||
transport, err := TransportFor(config)
|
||||
if err != nil {
|
||||
@ -229,9 +260,9 @@ func IsConfigTransportTLS(config *Config) bool {
|
||||
return baseURL.Scheme == "https"
|
||||
}
|
||||
|
||||
// defaultServerUrlFor is shared between IsConfigTransportTLS and RESTClientFor
|
||||
// defaultServerUrlFor is shared between IsConfigTransportTLS and RESTClientFor. It
|
||||
// requires Host and Version to be set prior to being called.
|
||||
func defaultServerUrlFor(config *Config) (*url.URL, error) {
|
||||
version := defaultVersionFor(config)
|
||||
// TODO: move the default to secure when the apiserver supports TLS by default
|
||||
// config.Insecure is taken to mean "I want HTTPS but don't bother checking the certs against a CA."
|
||||
defaultTLS := config.CertFile != "" || config.Insecure
|
||||
@ -239,7 +270,7 @@ func defaultServerUrlFor(config *Config) (*url.URL, error) {
|
||||
if host == "" {
|
||||
host = "localhost"
|
||||
}
|
||||
return DefaultServerURL(host, config.Prefix, version, defaultTLS)
|
||||
return DefaultServerURL(host, config.Prefix, config.Version, defaultTLS)
|
||||
}
|
||||
|
||||
// defaultVersionFor is shared between defaultServerUrlFor and RESTClientFor
|
||||
@ -252,9 +283,3 @@ func defaultVersionFor(config *Config) string {
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
// namespaceInPathFor is used to control what api version should use namespace in url paths
|
||||
func NamespaceInPathFor(version string) bool {
|
||||
// we use query param for v1beta1/v1beta2, v1beta3+ will use path param
|
||||
return (version != "v1beta1" && version != "v1beta2")
|
||||
}
|
||||
|
@ -85,6 +85,10 @@ func TestIsConfigTransportTLS(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
if err := SetKubernetesDefaults(testCase.Config); err != nil {
|
||||
t.Errorf("setting defaults failed for %#v: %v", testCase.Config, err)
|
||||
continue
|
||||
}
|
||||
useTLS := IsConfigTransportTLS(testCase.Config)
|
||||
if testCase.TransportTLS != useTLS {
|
||||
t.Errorf("expected %v for %#v", testCase.TransportTLS, testCase.Config)
|
||||
|
@ -35,18 +35,17 @@ type NodeInterface interface {
|
||||
|
||||
// nodes implements NodesInterface
|
||||
type nodes struct {
|
||||
r *Client
|
||||
preV1Beta3 bool
|
||||
r *Client
|
||||
}
|
||||
|
||||
// newNodes returns a nodes object. Uses "minions" as the
|
||||
// URL resource name for v1beta1 and v1beta2.
|
||||
func newNodes(c *Client, isPreV1Beta3 bool) *nodes {
|
||||
return &nodes{c, isPreV1Beta3}
|
||||
func newNodes(c *Client) *nodes {
|
||||
return &nodes{c}
|
||||
}
|
||||
|
||||
func (c *nodes) resourceName() string {
|
||||
if c.preV1Beta3 {
|
||||
if preV1Beta3(c.r.APIVersion()) {
|
||||
return "minions"
|
||||
}
|
||||
return "nodes"
|
||||
|
@ -89,8 +89,12 @@ type Request struct {
|
||||
// whether to poll.
|
||||
poller PollFunc
|
||||
|
||||
// If true, put ns/<namespace> in path; if false, add "?namespace=<namespace>" as a query parameter
|
||||
namespaceInPath bool
|
||||
// If true, add "?namespace=<namespace>" as a query parameter, if false put ns/<namespace> in path
|
||||
// Query parameter is considered legacy behavior
|
||||
namespaceInQuery bool
|
||||
// If true, lowercase resource prior to inserting into a path, if false, leave it as is. Preserving
|
||||
// case is considered legacy behavior.
|
||||
preserveResourceCase bool
|
||||
|
||||
// generic components accessible via method setters
|
||||
path string
|
||||
@ -110,15 +114,18 @@ type Request struct {
|
||||
body io.Reader
|
||||
}
|
||||
|
||||
// NewRequest creates a new request with the core attributes.
|
||||
func NewRequest(client HTTPClient, verb string, baseURL *url.URL, codec runtime.Codec, namespaceInPath bool) *Request {
|
||||
// NewRequest creates a new request helper object for accessing runtime.Objects on a server.
|
||||
func NewRequest(client HTTPClient, verb string, baseURL *url.URL,
|
||||
codec runtime.Codec, namespaceInQuery bool, preserveResourceCase bool) *Request {
|
||||
return &Request{
|
||||
client: client,
|
||||
verb: verb,
|
||||
baseURL: baseURL,
|
||||
codec: codec,
|
||||
namespaceInPath: namespaceInPath,
|
||||
path: baseURL.Path,
|
||||
client: client,
|
||||
verb: verb,
|
||||
baseURL: baseURL,
|
||||
path: baseURL.Path,
|
||||
|
||||
codec: codec,
|
||||
namespaceInQuery: namespaceInQuery,
|
||||
preserveResourceCase: preserveResourceCase,
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,11 +330,15 @@ func (r *Request) Poller(poller PollFunc) *Request {
|
||||
|
||||
func (r *Request) finalURL() string {
|
||||
p := r.path
|
||||
if r.namespaceInPath {
|
||||
if !r.namespaceInQuery {
|
||||
p = path.Join(p, "ns", r.namespace)
|
||||
}
|
||||
if len(r.resource) != 0 {
|
||||
p = path.Join(p, r.resource)
|
||||
resource := r.resource
|
||||
if !r.preserveResourceCase {
|
||||
resource = strings.ToLower(resource)
|
||||
}
|
||||
p = path.Join(p, resource)
|
||||
}
|
||||
// Join trims trailing slashes, so preserve r.path's trailing slash for backwards compat if nothing was changed
|
||||
if len(r.resourceName) != 0 || len(r.subpath) != 0 {
|
||||
@ -342,7 +353,7 @@ func (r *Request) finalURL() string {
|
||||
query.Add(key, value)
|
||||
}
|
||||
|
||||
if !r.namespaceInPath && len(r.namespace) > 0 {
|
||||
if r.namespaceInQuery && len(r.namespace) > 0 {
|
||||
query.Add("namespace", r.namespace)
|
||||
}
|
||||
|
||||
|
@ -75,21 +75,21 @@ func TestRequestWithErrorWontChange(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRequestPreservesBaseTrailingSlash(t *testing.T) {
|
||||
r := &Request{baseURL: &url.URL{}, path: "/path/"}
|
||||
r := &Request{baseURL: &url.URL{}, path: "/path/", namespaceInQuery: true}
|
||||
if s := r.finalURL(); s != "/path/" {
|
||||
t.Errorf("trailing slash should be preserved: %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestAbsPathPreservesTrailingSlash(t *testing.T) {
|
||||
r := (&Request{baseURL: &url.URL{}}).AbsPath("/foo/")
|
||||
r := (&Request{baseURL: &url.URL{}, namespaceInQuery: true}).AbsPath("/foo/")
|
||||
if s := r.finalURL(); s != "/foo/" {
|
||||
t.Errorf("trailing slash should be preserved: %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestAbsPathJoins(t *testing.T) {
|
||||
r := (&Request{baseURL: &url.URL{}}).AbsPath("foo/bar", "baz")
|
||||
r := (&Request{baseURL: &url.URL{}, namespaceInQuery: true}).AbsPath("foo/bar", "baz")
|
||||
if s := r.finalURL(); s != "foo/bar/baz" {
|
||||
t.Errorf("trailing slash should be preserved: %s", s)
|
||||
}
|
||||
@ -100,6 +100,7 @@ func TestRequestSetsNamespace(t *testing.T) {
|
||||
baseURL: &url.URL{
|
||||
Path: "/",
|
||||
},
|
||||
namespaceInQuery: true,
|
||||
}).Namespace("foo")
|
||||
if r.namespace == "" {
|
||||
t.Errorf("namespace should be set: %#v", r)
|
||||
@ -112,7 +113,6 @@ func TestRequestSetsNamespace(t *testing.T) {
|
||||
baseURL: &url.URL{
|
||||
Path: "/",
|
||||
},
|
||||
namespaceInPath: true,
|
||||
}).Namespace("foo")
|
||||
if s := r.finalURL(); s != "ns/foo" {
|
||||
t.Errorf("namespace should be in path: %s", s)
|
||||
@ -121,9 +121,8 @@ func TestRequestSetsNamespace(t *testing.T) {
|
||||
|
||||
func TestRequestOrdersNamespaceInPath(t *testing.T) {
|
||||
r := (&Request{
|
||||
baseURL: &url.URL{},
|
||||
path: "/test/",
|
||||
namespaceInPath: true,
|
||||
baseURL: &url.URL{},
|
||||
path: "/test/",
|
||||
}).Name("bar").Resource("baz").Namespace("foo")
|
||||
if s := r.finalURL(); s != "/test/ns/foo/baz/bar" {
|
||||
t.Errorf("namespace should be in order in path: %s", s)
|
||||
@ -212,7 +211,7 @@ func TestTransformResponse(t *testing.T) {
|
||||
{Response: &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewReader(invalid))}, Data: invalid},
|
||||
}
|
||||
for i, test := range testCases {
|
||||
r := NewRequest(nil, "", uri, testapi.Codec(), true)
|
||||
r := NewRequest(nil, "", uri, testapi.Codec(), true, true)
|
||||
if test.Response.Body == nil {
|
||||
test.Response.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||
}
|
||||
|
@ -35,10 +35,13 @@ import (
|
||||
// Most consumers should use client.New() to get a Kubernetes API client.
|
||||
type RESTClient struct {
|
||||
baseURL *url.URL
|
||||
// A string identifying the version of the API this client is expected to use.
|
||||
apiVersion string
|
||||
|
||||
// namespaceInPath controls if URLs should encode the namespace as path param instead of query param
|
||||
// needed for backward compatibility
|
||||
namespaceInPath bool
|
||||
// LegacyBehavior controls if URLs should encode the namespace as a query param,
|
||||
// and if resource case is preserved for supporting older API conventions of
|
||||
// Kubernetes. Newer clients should leave this false.
|
||||
LegacyBehavior bool
|
||||
|
||||
// Codec is the encoding and decoding scheme that applies to a particular set of
|
||||
// REST resources.
|
||||
@ -59,10 +62,9 @@ type RESTClient struct {
|
||||
|
||||
// NewRESTClient creates a new RESTClient. This client performs generic REST functions
|
||||
// such as Get, Put, Post, and Delete on specified paths. Codec controls encoding and
|
||||
// decoding of responses from the server. If the namespace should be specified as part
|
||||
// of the path (after the resource), set namespaceInPath to true, otherwise it will be
|
||||
// passed as "namespace" in the query string.
|
||||
func NewRESTClient(baseURL *url.URL, c runtime.Codec, namespaceInPath bool) *RESTClient {
|
||||
// decoding of responses from the server. If this client should use the older, legacy
|
||||
// API conventions from Kubernetes API v1beta1 and v1beta2, set legacyBehavior true.
|
||||
func NewRESTClient(baseURL *url.URL, apiVersion string, c runtime.Codec, legacyBehavior bool) *RESTClient {
|
||||
base := *baseURL
|
||||
if !strings.HasSuffix(base.Path, "/") {
|
||||
base.Path += "/"
|
||||
@ -71,10 +73,12 @@ func NewRESTClient(baseURL *url.URL, c runtime.Codec, namespaceInPath bool) *RES
|
||||
base.Fragment = ""
|
||||
|
||||
return &RESTClient{
|
||||
baseURL: &base,
|
||||
Codec: c,
|
||||
baseURL: &base,
|
||||
apiVersion: apiVersion,
|
||||
|
||||
namespaceInPath: namespaceInPath,
|
||||
Codec: c,
|
||||
|
||||
LegacyBehavior: legacyBehavior,
|
||||
|
||||
// Make asynchronous requests by default
|
||||
Sync: false,
|
||||
@ -106,7 +110,7 @@ func (c *RESTClient) Verb(verb string) *Request {
|
||||
if poller == nil {
|
||||
poller = c.DefaultPoll
|
||||
}
|
||||
return NewRequest(c.Client, verb, c.baseURL, c.Codec, c.namespaceInPath).Poller(poller).Sync(c.Sync).Timeout(c.Timeout)
|
||||
return NewRequest(c.Client, verb, c.baseURL, c.Codec, c.LegacyBehavior, c.LegacyBehavior).Poller(poller).Sync(c.Sync).Timeout(c.Timeout)
|
||||
}
|
||||
|
||||
// Post begins a POST request. Short for c.Verb("POST").
|
||||
@ -134,6 +138,7 @@ func (c *RESTClient) Operation(name string) *Request {
|
||||
return c.Get().Resource("operations").Name(name).Sync(false).NoPoll()
|
||||
}
|
||||
|
||||
// DefaultPoll performs a polling action based on the PollPeriod set on the Client.
|
||||
func (c *RESTClient) DefaultPoll(name string) (*Request, bool) {
|
||||
if c.PollPeriod == 0 {
|
||||
return nil, false
|
||||
@ -143,3 +148,8 @@ func (c *RESTClient) DefaultPoll(name string) (*Request, bool) {
|
||||
// Make a poll request
|
||||
return c.Operation(name).Poller(c.DefaultPoll), true
|
||||
}
|
||||
|
||||
// APIVersion returns the APIVersion this RESTClient is expected to use.
|
||||
func (c *RESTClient) APIVersion() string {
|
||||
return c.apiVersion
|
||||
}
|
||||
|
@ -31,19 +31,19 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
func TestChecksCodec(t *testing.T) {
|
||||
func TestSetsCodec(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
Err bool
|
||||
Prefix string
|
||||
Codec runtime.Codec
|
||||
}{
|
||||
"v1beta1": {false, "/v1beta1/", v1beta1.Codec},
|
||||
"": {false, "/v1beta1/", v1beta1.Codec},
|
||||
"v1beta2": {false, "/v1beta2/", v1beta2.Codec},
|
||||
"v1beta1": {false, "/api/v1beta1/", v1beta1.Codec},
|
||||
"": {false, "/api/v1beta1/", v1beta1.Codec},
|
||||
"v1beta2": {false, "/api/v1beta2/", v1beta2.Codec},
|
||||
"v1beta3": {true, "", nil},
|
||||
}
|
||||
for version, expected := range testCases {
|
||||
client, err := RESTClientFor(&Config{Host: "127.0.0.1", Version: version})
|
||||
client, err := New(&Config{Host: "127.0.0.1", Version: version})
|
||||
switch {
|
||||
case err == nil && expected.Err:
|
||||
t.Errorf("expected error but was nil")
|
||||
@ -54,15 +54,70 @@ func TestChecksCodec(t *testing.T) {
|
||||
case err != nil:
|
||||
continue
|
||||
}
|
||||
if e, a := expected.Prefix, client.baseURL.Path; e != a {
|
||||
if e, a := expected.Prefix, client.RESTClient.baseURL.Path; e != a {
|
||||
t.Errorf("expected %#v, got %#v", e, a)
|
||||
}
|
||||
if e, a := expected.Codec, client.Codec; e != a {
|
||||
if e, a := expected.Codec, client.RESTClient.Codec; e != a {
|
||||
t.Errorf("expected %#v, got %#v", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaults(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Config Config
|
||||
After Config
|
||||
Err bool
|
||||
}{
|
||||
{
|
||||
Config{},
|
||||
Config{
|
||||
Prefix: "/api",
|
||||
Version: latest.Version,
|
||||
Codec: latest.Codec,
|
||||
LegacyBehavior: (latest.Version == "v1beta1" || latest.Version == "v1beta2"),
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
Config{
|
||||
Version: "not_an_api",
|
||||
},
|
||||
Config{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
val := &testCase.Config
|
||||
err := SetKubernetesDefaults(val)
|
||||
switch {
|
||||
case err == nil && testCase.Err:
|
||||
t.Errorf("expected error but was nil")
|
||||
continue
|
||||
case err != nil && !testCase.Err:
|
||||
t.Errorf("unexpected error %v", err)
|
||||
continue
|
||||
case err != nil:
|
||||
continue
|
||||
}
|
||||
if *val != testCase.After {
|
||||
t.Errorf("unexpected result object: %#v", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRESTClientRequires(t *testing.T) {
|
||||
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", Version: "", Codec: testapi.Codec()}); err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", Version: "v1beta1"}); err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", Version: testapi.Version(), Codec: testapi.Codec()}); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatesHostParameter(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Host string
|
||||
@ -81,7 +136,7 @@ func TestValidatesHostParameter(t *testing.T) {
|
||||
{"host/server", "", "", true},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
c, err := RESTClientFor(&Config{Host: testCase.Host, Prefix: testCase.Prefix, Version: "v1beta1"})
|
||||
c, err := RESTClientFor(&Config{Host: testCase.Host, Prefix: testCase.Prefix, Version: "v1beta1", Codec: testapi.Codec()})
|
||||
switch {
|
||||
case err == nil && testCase.Err:
|
||||
t.Errorf("expected error but was nil")
|
||||
@ -110,7 +165,14 @@ func TestDoRequestBearer(t *testing.T) {
|
||||
testServer := httptest.NewServer(&fakeHandler)
|
||||
defer testServer.Close()
|
||||
request, _ := http.NewRequest("GET", testServer.URL, nil)
|
||||
c, err := RESTClientFor(&Config{Host: testServer.URL, BearerToken: "test"})
|
||||
c, err := RESTClientFor(&Config{
|
||||
Host: testServer.URL,
|
||||
Version: testapi.Version(),
|
||||
Codec: testapi.Codec(),
|
||||
LegacyBehavior: true,
|
||||
|
||||
BearerToken: "test",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@ -133,7 +195,14 @@ func TestDoRequestAccepted(t *testing.T) {
|
||||
}
|
||||
testServer := httptest.NewServer(&fakeHandler)
|
||||
defer testServer.Close()
|
||||
c, err := RESTClientFor(&Config{Host: testServer.URL, Username: "test", Version: testapi.Version()})
|
||||
c, err := RESTClientFor(&Config{
|
||||
Host: testServer.URL,
|
||||
Version: testapi.Version(),
|
||||
Codec: testapi.Codec(),
|
||||
LegacyBehavior: true,
|
||||
|
||||
Username: "test",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@ -167,7 +236,15 @@ func TestDoRequestAcceptedSuccess(t *testing.T) {
|
||||
}
|
||||
testServer := httptest.NewServer(&fakeHandler)
|
||||
defer testServer.Close()
|
||||
c, err := RESTClientFor(&Config{Host: testServer.URL, Username: "user", Password: "pass", Version: testapi.Version()})
|
||||
c, err := RESTClientFor(&Config{
|
||||
Host: testServer.URL,
|
||||
Version: testapi.Version(),
|
||||
Codec: testapi.Codec(),
|
||||
LegacyBehavior: true,
|
||||
|
||||
Username: "user",
|
||||
Password: "pass",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@ -198,7 +275,12 @@ func TestDoRequestFailed(t *testing.T) {
|
||||
}
|
||||
testServer := httptest.NewServer(&fakeHandler)
|
||||
defer testServer.Close()
|
||||
c, err := RESTClientFor(&Config{Host: testServer.URL})
|
||||
c, err := RESTClientFor(&Config{
|
||||
Host: testServer.URL,
|
||||
Version: testapi.Version(),
|
||||
Codec: testapi.Codec(),
|
||||
LegacyBehavior: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@ -226,7 +308,15 @@ func TestDoRequestCreated(t *testing.T) {
|
||||
}
|
||||
testServer := httptest.NewServer(&fakeHandler)
|
||||
defer testServer.Close()
|
||||
c, err := RESTClientFor(&Config{Host: testServer.URL, Username: "user", Password: "pass", Version: testapi.Version()})
|
||||
c, err := RESTClientFor(&Config{
|
||||
Host: testServer.URL,
|
||||
Version: testapi.Version(),
|
||||
Codec: testapi.Codec(),
|
||||
LegacyBehavior: true,
|
||||
|
||||
Username: "user",
|
||||
Password: "pass",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ func getFakeClient(t *testing.T, validURLs []string) (ClientPosterFunc, *httptes
|
||||
return func(mapping *meta.RESTMapping) (RESTClientPoster, error) {
|
||||
fakeCodec := runtime.CodecFor(api.Scheme, "v1beta1")
|
||||
fakeUri, _ := url.Parse(server.URL + "/api/v1beta1")
|
||||
return client.NewRESTClient(fakeUri, fakeCodec, false), nil
|
||||
return client.NewRESTClient(fakeUri, "v1beta1", fakeCodec, true), nil
|
||||
}, server
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ import (
|
||||
)
|
||||
|
||||
func makeNamespaceURL(namespace, suffix string) string {
|
||||
if client.NamespaceInPathFor(testapi.Version()) {
|
||||
if !(testapi.Version() == "v1beta1" || testapi.Version() == "v1beta2") {
|
||||
return makeURL("/ns/" + namespace + suffix)
|
||||
}
|
||||
return makeURL(suffix + "?namespace=" + namespace)
|
||||
|
@ -88,7 +88,7 @@ func TestPollMinions(t *testing.T) {
|
||||
}
|
||||
|
||||
func makeNamespaceURL(namespace, suffix string, isClient bool) string {
|
||||
if client.NamespaceInPathFor(testapi.Version()) {
|
||||
if !(testapi.Version() == "v1beta1" || testapi.Version() == "v1beta2") {
|
||||
return makeURL("/ns/" + namespace + suffix)
|
||||
}
|
||||
// if this is a url the client should call, encode the url
|
||||
|
Loading…
Reference in New Issue
Block a user