mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-25 11:31:44 +00:00
Merge pull request #3269 from smarterclayton/fix_legacy_support
Make RESTClient more generic to API version, simplify version handling
This commit is contained in:
commit
3e0b4cfabc
@ -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