mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 12:18:16 +00:00 
			
		
		
		
	Have the service account controller force retry
Service account controller, when API token not found, now sends 500 with Retry-After: 1s. Also change the apiserver to actually write the error.
This commit is contained in:
		@@ -26,6 +26,7 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	rt "runtime"
 | 
						rt "runtime"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -449,6 +450,11 @@ func writeNegotiated(s runtime.NegotiatedSerializer, gv unversioned.GroupVersion
 | 
				
			|||||||
func errorNegotiated(err error, s runtime.NegotiatedSerializer, gv unversioned.GroupVersion, w http.ResponseWriter, req *http.Request) int {
 | 
					func errorNegotiated(err error, s runtime.NegotiatedSerializer, gv unversioned.GroupVersion, w http.ResponseWriter, req *http.Request) int {
 | 
				
			||||||
	status := errToAPIStatus(err)
 | 
						status := errToAPIStatus(err)
 | 
				
			||||||
	code := int(status.Code)
 | 
						code := int(status.Code)
 | 
				
			||||||
 | 
						// when writing an error, check to see if the status indicates a retry after period
 | 
				
			||||||
 | 
						if status.Details != nil && status.Details.RetryAfterSeconds > 0 {
 | 
				
			||||||
 | 
							delay := strconv.Itoa(int(status.Details.RetryAfterSeconds))
 | 
				
			||||||
 | 
							w.Header().Set("Retry-After", delay)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	writeNegotiated(s, gv, w, req, code, status)
 | 
						writeNegotiated(s, gv, w, req, code, status)
 | 
				
			||||||
	return code
 | 
						return code
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1562,6 +1562,7 @@ func TestGetNamespaceSelfLink(t *testing.T) {
 | 
				
			|||||||
		t.Errorf("Never set self link")
 | 
							t.Errorf("Never set self link")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestGetMissing(t *testing.T) {
 | 
					func TestGetMissing(t *testing.T) {
 | 
				
			||||||
	storage := map[string]rest.Storage{}
 | 
						storage := map[string]rest.Storage{}
 | 
				
			||||||
	simpleStorage := SimpleRESTStorage{
 | 
						simpleStorage := SimpleRESTStorage{
 | 
				
			||||||
@@ -1572,7 +1573,7 @@ func TestGetMissing(t *testing.T) {
 | 
				
			|||||||
	server := httptest.NewServer(handler)
 | 
						server := httptest.NewServer(handler)
 | 
				
			||||||
	defer server.Close()
 | 
						defer server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple/id")
 | 
						resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("unexpected error: %v", err)
 | 
							t.Errorf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1582,6 +1583,28 @@ func TestGetMissing(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetRetryAfter(t *testing.T) {
 | 
				
			||||||
 | 
						storage := map[string]rest.Storage{}
 | 
				
			||||||
 | 
						simpleStorage := SimpleRESTStorage{
 | 
				
			||||||
 | 
							errors: map[string]error{"get": apierrs.NewServerTimeout(api.Resource("simples"), "id", 2)},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						storage["simple"] = &simpleStorage
 | 
				
			||||||
 | 
						handler := handle(storage)
 | 
				
			||||||
 | 
						server := httptest.NewServer(handler)
 | 
				
			||||||
 | 
						defer server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resp.StatusCode != http.StatusInternalServerError {
 | 
				
			||||||
 | 
							t.Errorf("Unexpected response %#v", resp)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resp.Header.Get("Retry-After") != "2" {
 | 
				
			||||||
 | 
							t.Errorf("Unexpected Retry-After header: %v", resp.Header)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestConnect(t *testing.T) {
 | 
					func TestConnect(t *testing.T) {
 | 
				
			||||||
	responseText := "Hello World"
 | 
						responseText := "Hello World"
 | 
				
			||||||
	itemID := "theID"
 | 
						itemID := "theID"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/admission"
 | 
						"k8s.io/kubernetes/pkg/admission"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/errors"
 | 
						"k8s.io/kubernetes/pkg/api/errors"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/client/cache"
 | 
						"k8s.io/kubernetes/pkg/client/cache"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/fields"
 | 
						"k8s.io/kubernetes/pkg/fields"
 | 
				
			||||||
	kubelet "k8s.io/kubernetes/pkg/kubelet/types"
 | 
						kubelet "k8s.io/kubernetes/pkg/kubelet/types"
 | 
				
			||||||
@@ -199,6 +200,9 @@ func (s *serviceAccount) Admit(a admission.Attributes) (err error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if s.MountServiceAccountToken {
 | 
						if s.MountServiceAccountToken {
 | 
				
			||||||
		if err := s.mountServiceAccountToken(serviceAccount, pod); err != nil {
 | 
							if err := s.mountServiceAccountToken(serviceAccount, pod); err != nil {
 | 
				
			||||||
 | 
								if _, ok := err.(errors.APIStatus); ok {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			return admission.NewForbidden(a, err)
 | 
								return admission.NewForbidden(a, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -357,8 +361,9 @@ func (s *serviceAccount) mountServiceAccountToken(serviceAccount *api.ServiceAcc
 | 
				
			|||||||
		// We don't have an API token to mount, so return
 | 
							// We don't have an API token to mount, so return
 | 
				
			||||||
		if s.RequireAPIToken {
 | 
							if s.RequireAPIToken {
 | 
				
			||||||
			// If a token is required, this is considered an error
 | 
								// If a token is required, this is considered an error
 | 
				
			||||||
			// TODO: convert to a ServerTimeout error (or other error that sends a Retry-After header)
 | 
								err := errors.NewServerTimeout(unversioned.GroupResource{Resource: "serviceaccounts"}, "create pod", 1)
 | 
				
			||||||
			return fmt.Errorf("no API token found for service account %s/%s, retry after the token is automatically created and added to the service account", serviceAccount.Namespace, serviceAccount.Name)
 | 
								err.ErrStatus.Message = fmt.Sprintf("No API token found for service account %q, retry after the token is automatically created and added to the service account", serviceAccount.Name)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/admission"
 | 
						"k8s.io/kubernetes/pkg/admission"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/errors"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
 | 
						"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
 | 
				
			||||||
	kubelet "k8s.io/kubernetes/pkg/kubelet/types"
 | 
						kubelet "k8s.io/kubernetes/pkg/kubelet/types"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/types"
 | 
						"k8s.io/kubernetes/pkg/types"
 | 
				
			||||||
@@ -168,8 +169,8 @@ func TestAssignsDefaultServiceAccountAndRejectsMissingAPIToken(t *testing.T) {
 | 
				
			|||||||
	pod := &api.Pod{}
 | 
						pod := &api.Pod{}
 | 
				
			||||||
	attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil)
 | 
						attrs := admission.NewAttributesRecord(pod, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil)
 | 
				
			||||||
	err := admit.Admit(attrs)
 | 
						err := admit.Admit(attrs)
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil || !errors.IsServerTimeout(err) {
 | 
				
			||||||
		t.Errorf("Expected admission error for missing API token")
 | 
							t.Errorf("Expected server timeout error for missing API token: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user