mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-15 23:03:40 +00:00
Merge pull request #9826 from brendandburns/redirect
Remove the redirect verb.
This commit is contained in:
commit
5e597c5f0d
@ -3212,34 +3212,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1/redirect/nodes/{name}",
|
||||
"description": "API at /api/v1 version v1",
|
||||
"operations": [
|
||||
{
|
||||
"type": "string",
|
||||
"method": "GET",
|
||||
"summary": "redirect GET request to Node",
|
||||
"nickname": "redirectNode",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the Node",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"*/*"
|
||||
],
|
||||
"consumes": [
|
||||
"*/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1/proxy/nodes/{name}/{path:*}",
|
||||
"description": "API at /api/v1 version v1",
|
||||
@ -5302,42 +5274,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1/redirect/namespaces/{namespaces}/pods/{name}",
|
||||
"description": "API at /api/v1 version v1",
|
||||
"operations": [
|
||||
{
|
||||
"type": "string",
|
||||
"method": "GET",
|
||||
"summary": "redirect GET request to Pod",
|
||||
"nickname": "redirectPod",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "namespaces",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the Pod",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"*/*"
|
||||
],
|
||||
"consumes": [
|
||||
"*/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1/proxy/namespaces/{namespaces}/pods/{name}/{path:*}",
|
||||
"description": "API at /api/v1 version v1",
|
||||
@ -10187,42 +10123,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1/redirect/namespaces/{namespaces}/services/{name}",
|
||||
"description": "API at /api/v1 version v1",
|
||||
"operations": [
|
||||
{
|
||||
"type": "string",
|
||||
"method": "GET",
|
||||
"summary": "redirect GET request to Service",
|
||||
"nickname": "redirectService",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "namespaces",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the Service",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"*/*"
|
||||
],
|
||||
"consumes": [
|
||||
"*/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1/proxy/namespaces/{namespaces}/services/{name}/{path:*}",
|
||||
"description": "API at /api/v1 version v1",
|
||||
|
@ -3212,34 +3212,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1beta3/redirect/nodes/{name}",
|
||||
"description": "API at /api/v1beta3 version v1beta3",
|
||||
"operations": [
|
||||
{
|
||||
"type": "string",
|
||||
"method": "GET",
|
||||
"summary": "redirect GET request to Node",
|
||||
"nickname": "redirectNode",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the Node",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"*/*"
|
||||
],
|
||||
"consumes": [
|
||||
"*/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1beta3/proxy/nodes/{name}/{path:*}",
|
||||
"description": "API at /api/v1beta3 version v1beta3",
|
||||
@ -5302,42 +5274,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1beta3/redirect/namespaces/{namespaces}/pods/{name}",
|
||||
"description": "API at /api/v1beta3 version v1beta3",
|
||||
"operations": [
|
||||
{
|
||||
"type": "string",
|
||||
"method": "GET",
|
||||
"summary": "redirect GET request to Pod",
|
||||
"nickname": "redirectPod",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "namespaces",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the Pod",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"*/*"
|
||||
],
|
||||
"consumes": [
|
||||
"*/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1beta3/proxy/namespaces/{namespaces}/pods/{name}/{path:*}",
|
||||
"description": "API at /api/v1beta3 version v1beta3",
|
||||
@ -10187,42 +10123,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1beta3/redirect/namespaces/{namespaces}/services/{name}",
|
||||
"description": "API at /api/v1beta3 version v1beta3",
|
||||
"operations": [
|
||||
{
|
||||
"type": "string",
|
||||
"method": "GET",
|
||||
"summary": "redirect GET request to Service",
|
||||
"nickname": "redirectService",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "namespaces",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the Service",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"*/*"
|
||||
],
|
||||
"consumes": [
|
||||
"*/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/api/v1beta3/proxy/namespaces/{namespaces}/services/{name}/{path:*}",
|
||||
"description": "API at /api/v1beta3 version v1beta3",
|
||||
|
@ -1,7 +1,6 @@
|
||||
# User Guide to Accessing the Cluster
|
||||
* [Accessing the cluster API](#api)
|
||||
* [Accessing services running on the cluster](#otherservices)
|
||||
* [Requesting redirects](#redirect)
|
||||
* [So many proxies](#somanyproxies)
|
||||
|
||||
## Accessing the cluster API<a name="api"></a>
|
||||
@ -203,100 +202,7 @@ You may be able to put a apiserver proxy url into the address bar of a browser.
|
||||
way that is unaware of the proxy path prefix.
|
||||
|
||||
## <a name="redirect"></a>Requesting redirects
|
||||
Use a `redirect` request so that the server returns an HTTP redirect response and identifies the specific node and service that
|
||||
can handle the request.
|
||||
|
||||
**Note**: Since the hostname or address that is returned is usually only accessible from inside the cluster,
|
||||
sending `redirect` requests is useful only for code running inside the cluster. Also, keep in mind that any subsequent `redirect` requests to the same
|
||||
server might return different results (because another node at that point in time can better serve the request).
|
||||
|
||||
**Tip**: Use a redirect request to reduce calls to the proxy server by first obtaining the address of a node on the
|
||||
cluster and then using that returned address for all subsequent requests.
|
||||
|
||||
##### Example
|
||||
To request a redirect and then verify the address that gets returned, let's run a query on `oban` (Google Compute Engine virtual machine). Note that `oban` is running in the same project and default network (Google Compute Engine) as the Kubernetes cluster.
|
||||
|
||||
To request a redirect for the Elasticsearch service, we can run the following `curl` command:
|
||||
```
|
||||
user@oban:~$ curl -L -k -u admin:4mty0Vl9nNFfwLJz https://104.197.5.247/api/v1/redirect/namespaces/default/services/elasticsearch-logging/
|
||||
{
|
||||
"status" : 200,
|
||||
"name" : "Skin",
|
||||
"cluster_name" : "kubernetes_logging",
|
||||
"version" : {
|
||||
"number" : "1.4.4",
|
||||
"build_hash" : "c88f77ffc81301dfa9dfd81ca2232f09588bd512",
|
||||
"build_timestamp" : "2015-02-19T13:05:36Z",
|
||||
"build_snapshot" : false,
|
||||
"lucene_version" : "4.10.3"
|
||||
},
|
||||
"tagline" : "You Know, for Search"
|
||||
}
|
||||
```
|
||||
**Note**: We use the `-L` flag in the request so that `curl` follows the returned redirect address and retrieves the Elasticsearch service information.
|
||||
|
||||
If we examine the actual redirect header (instead run the same `curl` command with `-v`), we see that the request to `https://104.197.5.247/api/v1/redirect/namespaces/default/services/elasticsearch-logging/` is redirected to `http://10.244.2.7:9200`:
|
||||
```
|
||||
user@oban:~$ curl -v -k -u admin:4mty0Vl9nNFfwLJz https://104.197.5.247/api/v1/redirect/namespaces/default/services/elasticsearch-logging/
|
||||
* About to connect() to 104.197.5.247 port 443 (#0)
|
||||
* Trying 104.197.5.247...
|
||||
* connected
|
||||
* Connected to 104.197.5.247 (104.197.5.247) port 443 (#0)
|
||||
* successfully set certificate verify locations:
|
||||
* CAfile: none
|
||||
CApath: /etc/ssl/certs
|
||||
* SSLv3, TLS handshake, Client hello (1):
|
||||
* SSLv3, TLS handshake, Server hello (2):
|
||||
* SSLv3, TLS handshake, CERT (11):
|
||||
* SSLv3, TLS handshake, Server key exchange (12):
|
||||
* SSLv3, TLS handshake, Server finished (14):
|
||||
* SSLv3, TLS handshake, Client key exchange (16):
|
||||
* SSLv3, TLS change cipher, Client hello (1):
|
||||
* SSLv3, TLS handshake, Finished (20):
|
||||
* SSLv3, TLS change cipher, Client hello (1):
|
||||
* SSLv3, TLS handshake, Finished (20):
|
||||
* SSL connection using ECDHE-RSA-AES256-GCM-SHA384
|
||||
* Server certificate:
|
||||
* subject: CN=kubernetes-master
|
||||
* start date: 2015-03-04 19:40:24 GMT
|
||||
* expire date: 2025-03-01 19:40:24 GMT
|
||||
* issuer: CN=104.197.5.247@1425498024
|
||||
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
|
||||
* Server auth using Basic with user 'admin'
|
||||
> GET /api/v1/redirect/namespaces/default/services/elasticsearch-logging HTTP/1.1
|
||||
> Authorization: Basic YWRtaW46M210eTBWbDluTkZmd0xKeg==
|
||||
> User-Agent: curl/7.26.0
|
||||
> Host: 104.197.5.247
|
||||
> Accept: */*
|
||||
>
|
||||
* additional stuff not fine transfer.c:1037: 0 0
|
||||
* HTTP 1.1 or later with persistent connection, pipelining supported
|
||||
< HTTP/1.1 307 Temporary Redirect
|
||||
< Server: nginx/1.2.1
|
||||
< Date: Thu, 05 Mar 2015 00:14:45 GMT
|
||||
< Content-Type: text/plain; charset=utf-8
|
||||
< Content-Length: 0
|
||||
< Connection: keep-alive
|
||||
< Location: http://10.244.2.7:9200
|
||||
<
|
||||
* Connection #0 to host 104.197.5.247 left intact
|
||||
* Closing connection #0
|
||||
* SSLv3, TLS alert, Client hello (1):
|
||||
```
|
||||
|
||||
We can also run the `kubectl get pods` command to view a list of the pods on the cluster and verify that `http://10.244.2.7` is where the Elasticsearch service is running:
|
||||
```
|
||||
$ kubectl get pods
|
||||
POD IP CONTAINER(S) IMAGE(S) HOST LABELS STATUS CREATED
|
||||
elasticsearch-logging-controller-gziey 10.244.2.7 elasticsearch-logging kubernetes/elasticsearch:1.0 kubernetes-minion-hqhv.c.kubernetes-user2.internal/104.154.33.252 kubernetes.io/cluster-service=true,name=elasticsearch-logging Running 5 hours
|
||||
kibana-logging-controller-ls6k1 10.244.1.9 kibana-logging kubernetes/kibana:1.1 kubernetes-minion-h5kt.c.kubernetes-user2.internal/146.148.80.37 kubernetes.io/cluster-service=true,name=kibana-logging Running 5 hours
|
||||
kube-dns-oh43e 10.244.1.10 etcd quay.io/coreos/etcd:v2.0.3 kubernetes-minion-h5kt.c.kubernetes-user2.internal/146.148.80.37 k8s-app=kube-dns,kubernetes.io/cluster-service=true,name=kube-dns Running 5 hours
|
||||
kube2sky kubernetes/kube2sky:1.0
|
||||
skydns kubernetes/skydns:2014-12-23-001
|
||||
monitoring-heapster-controller-fplln 10.244.0.4 heapster kubernetes/heapster:v0.8 kubernetes-minion-2il2.c.kubernetes-user2.internal/130.211.155.16 kubernetes.io/cluster-service=true,name=heapster,uses=monitoring-influxdb Running 5 hours
|
||||
monitoring-influx-grafana-controller-0133o 10.244.3.4 influxdb kubernetes/heapster_influxdb:v0.3 kubernetes-minion-kmin.c.kubernetes-user2.internal/130.211.173.22 kubernetes.io/cluster-service=true,name=influxGrafana Running 5 hours
|
||||
grafana kubernetes/heapster_grafana:v0.4
|
||||
```
|
||||
The redirect capabilities have been deprecated and removed. Please use a proxy (see below) instead.
|
||||
|
||||
##<a name="somanyproxies"></a>So Many Proxies
|
||||
There are several different proxies you may encounter when using kubernetes:
|
||||
|
@ -62,7 +62,6 @@ func (a *APIInstaller) Install(proxyDialer func(network, addr string) (net.Conn,
|
||||
// Create the WebService.
|
||||
ws = a.newWebService()
|
||||
|
||||
redirectHandler := (&RedirectHandler{a.group.Storage, a.group.Codec, a.group.Context, a.info})
|
||||
proxyHandler := (&ProxyHandler{a.prefix + "/proxy/", a.group.Storage, a.group.Codec, a.group.Context, a.info, proxyDialer})
|
||||
|
||||
// Register the paths in a deterministic (sorted) order to get a deterministic swagger spec.
|
||||
@ -74,7 +73,7 @@ func (a *APIInstaller) Install(proxyDialer func(network, addr string) (net.Conn,
|
||||
}
|
||||
sort.Strings(paths)
|
||||
for _, path := range paths {
|
||||
if err := a.registerResourceHandlers(path, a.group.Storage[path], ws, redirectHandler, proxyHandler); err != nil {
|
||||
if err := a.registerResourceHandlers(path, a.group.Storage[path], ws, proxyHandler); err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
@ -93,7 +92,7 @@ func (a *APIInstaller) newWebService() *restful.WebService {
|
||||
return ws
|
||||
}
|
||||
|
||||
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService, redirectHandler, proxyHandler http.Handler) error {
|
||||
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService, proxyHandler http.Handler) error {
|
||||
admit := a.group.Admit
|
||||
context := a.group.Context
|
||||
|
||||
@ -277,7 +276,6 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer}, isPatcher)
|
||||
actions = appendIf(actions, action{"DELETE", itemPath, nameParams, namer}, isDeleter)
|
||||
actions = appendIf(actions, action{"WATCH", "watch/" + itemPath, nameParams, namer}, isWatcher)
|
||||
actions = appendIf(actions, action{"REDIRECT", "redirect/" + itemPath, nameParams, namer}, isRedirector)
|
||||
actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath + "/{path:*}", proxyParams, namer}, isRedirector)
|
||||
actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath, nameParams, namer}, isRedirector)
|
||||
actions = appendIf(actions, action{"CONNECT", itemPath, nameParams, namer}, isConnecter)
|
||||
@ -316,7 +314,6 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer}, isPatcher)
|
||||
actions = appendIf(actions, action{"DELETE", itemPath, nameParams, namer}, isDeleter)
|
||||
actions = appendIf(actions, action{"WATCH", "watch/" + itemPath, nameParams, namer}, isWatcher)
|
||||
actions = appendIf(actions, action{"REDIRECT", "redirect/" + itemPath, nameParams, namer}, isRedirector)
|
||||
actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath + "/{path:*}", proxyParams, namer}, isRedirector)
|
||||
actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath, nameParams, namer}, isRedirector)
|
||||
actions = appendIf(actions, action{"CONNECT", itemPath, nameParams, namer}, isConnecter)
|
||||
@ -360,7 +357,6 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer}, isPatcher)
|
||||
actions = appendIf(actions, action{"DELETE", itemPath, nameParams, namer}, isDeleter)
|
||||
actions = appendIf(actions, action{"WATCH", "watch/" + itemPath, nameParams, namer}, isWatcher)
|
||||
actions = appendIf(actions, action{"REDIRECT", "redirect/" + itemPath, nameParams, namer}, isRedirector)
|
||||
actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath + "/{path:*}", proxyParams, namer}, isRedirector)
|
||||
actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath, nameParams, namer}, isRedirector)
|
||||
actions = appendIf(actions, action{"CONNECT", itemPath, nameParams, namer}, isConnecter)
|
||||
@ -568,20 +564,6 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
}
|
||||
addParams(route, action.Params)
|
||||
ws.Route(route)
|
||||
case "REDIRECT": // Get the redirect URL for a resource.
|
||||
doc := "redirect GET request to " + kind
|
||||
if hasSubresource {
|
||||
doc = "redirect GET request to " + subresource + " of " + kind
|
||||
}
|
||||
route := ws.GET(action.Path).To(routeFunction(redirectHandler)).
|
||||
Filter(m).
|
||||
Doc(doc).
|
||||
Operation("redirect" + kind + strings.Title(subresource)).
|
||||
Produces("*/*").
|
||||
Consumes("*/*").
|
||||
Writes("string")
|
||||
addParams(route, action.Params)
|
||||
ws.Route(route)
|
||||
case "PROXY": // Proxy requests to a resource.
|
||||
// Accept all methods as per https://github.com/GoogleCloudPlatform/kubernetes/issues/3996
|
||||
addProxyRoute(ws, "GET", a.prefix, action.Path, proxyHandler, kind, resource, subresource, hasSubresource, action.Params)
|
||||
|
@ -1,104 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors 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 apiserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
type RedirectHandler struct {
|
||||
storage map[string]rest.Storage
|
||||
codec runtime.Codec
|
||||
context api.RequestContextMapper
|
||||
apiRequestInfoResolver *APIRequestInfoResolver
|
||||
}
|
||||
|
||||
func (r *RedirectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
var verb string
|
||||
var apiResource string
|
||||
var httpCode int
|
||||
reqStart := time.Now()
|
||||
defer monitor(&verb, &apiResource, util.GetClient(req), &httpCode, reqStart)
|
||||
|
||||
requestInfo, err := r.apiRequestInfoResolver.GetAPIRequestInfo(req)
|
||||
if err != nil {
|
||||
notFound(w, req)
|
||||
httpCode = http.StatusNotFound
|
||||
return
|
||||
}
|
||||
verb = requestInfo.Verb
|
||||
resource, parts := requestInfo.Resource, requestInfo.Parts
|
||||
ctx, ok := r.context.Get(req)
|
||||
if !ok {
|
||||
ctx = api.NewContext()
|
||||
}
|
||||
ctx = api.WithNamespace(ctx, requestInfo.Namespace)
|
||||
|
||||
// redirection requires /resource/resourceName path parts
|
||||
if len(parts) != 2 || req.Method != "GET" {
|
||||
notFound(w, req)
|
||||
httpCode = http.StatusNotFound
|
||||
return
|
||||
}
|
||||
id := parts[1]
|
||||
storage, ok := r.storage[resource]
|
||||
if !ok {
|
||||
httplog.LogOf(req, w).Addf("'%v' has no storage object", resource)
|
||||
notFound(w, req)
|
||||
httpCode = http.StatusNotFound
|
||||
return
|
||||
}
|
||||
apiResource = resource
|
||||
|
||||
redirector, ok := storage.(rest.Redirector)
|
||||
if !ok {
|
||||
httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource)
|
||||
httpCode = errorJSON(errors.NewMethodNotSupported(resource, "redirect"), r.codec, w)
|
||||
return
|
||||
}
|
||||
|
||||
location, _, err := redirector.ResourceLocation(ctx, id)
|
||||
if err != nil {
|
||||
status := errToAPIStatus(err)
|
||||
writeJSON(status.Code, r.codec, status, w, true)
|
||||
httpCode = status.Code
|
||||
return
|
||||
}
|
||||
if location == nil {
|
||||
httplog.LogOf(req, w).Addf("ResourceLocation for %v returned nil", id)
|
||||
notFound(w, req)
|
||||
httpCode = http.StatusNotFound
|
||||
return
|
||||
}
|
||||
|
||||
// Default to http
|
||||
if location.Scheme == "" {
|
||||
location.Scheme = "http"
|
||||
}
|
||||
|
||||
w.Header().Set("Location", location.String())
|
||||
w.WriteHeader(http.StatusTemporaryRedirect)
|
||||
httpCode = http.StatusTemporaryRedirect
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors 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 apiserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
|
||||
)
|
||||
|
||||
func TestRedirect(t *testing.T) {
|
||||
simpleStorage := &SimpleRESTStorage{
|
||||
errors: map[string]error{},
|
||||
expectedResourceNamespace: "default",
|
||||
}
|
||||
handler := handle(map[string]rest.Storage{"foo": simpleStorage})
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
dontFollow := errors.New("don't follow")
|
||||
client := http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return dontFollow
|
||||
},
|
||||
}
|
||||
|
||||
table := []struct {
|
||||
id string
|
||||
err error
|
||||
code int
|
||||
}{
|
||||
{"cozy", nil, http.StatusTemporaryRedirect},
|
||||
{"horse", errors.New("no such id"), http.StatusInternalServerError},
|
||||
}
|
||||
|
||||
for _, item := range table {
|
||||
simpleStorage.errors["resourceLocation"] = item.err
|
||||
simpleStorage.resourceLocation = &url.URL{Host: item.id}
|
||||
resp, err := client.Get(server.URL + "/api/version/redirect/foo/" + item.id)
|
||||
if resp == nil {
|
||||
t.Fatalf("Unexpected nil resp")
|
||||
}
|
||||
resp.Body.Close()
|
||||
if e, a := item.code, resp.StatusCode; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := item.id, simpleStorage.requestedResourceLocationID; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
if item.err != nil {
|
||||
continue
|
||||
}
|
||||
if err == nil || err.(*url.Error).Err != dontFollow {
|
||||
t.Errorf("Unexpected err %#v", err)
|
||||
}
|
||||
if e, a := "http://"+item.id, resp.Header.Get("Location"); e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRedirectWithNamespaces(t *testing.T) {
|
||||
simpleStorage := &SimpleRESTStorage{
|
||||
errors: map[string]error{},
|
||||
expectedResourceNamespace: "other",
|
||||
}
|
||||
handler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
dontFollow := errors.New("don't follow")
|
||||
client := http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return dontFollow
|
||||
},
|
||||
}
|
||||
|
||||
table := []struct {
|
||||
id string
|
||||
err error
|
||||
code int
|
||||
}{
|
||||
{"cozy", nil, http.StatusTemporaryRedirect},
|
||||
{"horse", errors.New("no such id"), http.StatusInternalServerError},
|
||||
}
|
||||
|
||||
for _, item := range table {
|
||||
simpleStorage.errors["resourceLocation"] = item.err
|
||||
simpleStorage.resourceLocation = &url.URL{Host: item.id}
|
||||
resp, err := client.Get(server.URL + "/api/version2/redirect/namespaces/other/foo/" + item.id)
|
||||
if resp == nil {
|
||||
t.Fatalf("Unexpected nil resp")
|
||||
}
|
||||
resp.Body.Close()
|
||||
if e, a := item.code, resp.StatusCode; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := item.id, simpleStorage.requestedResourceLocationID; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
if item.err != nil {
|
||||
continue
|
||||
}
|
||||
if err == nil || err.(*url.Error).Err != dontFollow {
|
||||
t.Errorf("Unexpected err %#v", err)
|
||||
}
|
||||
if e, a := "http://"+item.id, resp.Header.Get("Location"); e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user