// NewREST returns a RESTStorage object that will work against secrets. func NewREST(opts generic.RESTOptions) *REST { prefix := "/secrets" newListFunc := func() runtime.Object { return &api.SecretList{} } storageInterface := opts.Decorator( opts.Storage, cachesize.GetWatchCacheSizeByResource(cachesize.Secrets), &api.Secret{}, prefix, secret.Strategy, newListFunc) store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.Secret{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { return registry.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, id string) (string, error) { return registry.NamespaceKeyFunc(ctx, prefix, id) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Secret).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return secret.Matcher(label, field) }, QualifiedResource: api.Resource("secrets"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: secret.Strategy, UpdateStrategy: secret.Strategy, DeleteStrategy: secret.Strategy, Storage: storageInterface, } return &REST{store} }
func TestCheckGeneratedNameError(t *testing.T) { expect := errors.NewNotFound(api.Resource("foos"), "bar") if err := rest.CheckGeneratedNameError(Strategy, expect, &api.Pod{}); err != expect { t.Errorf("NotFoundError should be ignored: %v", err) } expect = errors.NewAlreadyExists(api.Resource("foos"), "bar") if err := rest.CheckGeneratedNameError(Strategy, expect, &api.Pod{}); err != expect { t.Errorf("AlreadyExists should be returned when no GenerateName field: %v", err) } expect = errors.NewAlreadyExists(api.Resource("foos"), "bar") if err := rest.CheckGeneratedNameError(Strategy, expect, &api.Pod{ObjectMeta: api.ObjectMeta{GenerateName: "foo"}}); err == nil || !errors.IsServerTimeout(err) { t.Errorf("expected try again later error: %v", err) } }
// existingController verifies if the controller already exists func (r *RollingUpdater) existingController(controller *api.ReplicationController) (*api.ReplicationController, error) { // without rc name but generate name, there's no existing rc if len(controller.Name) == 0 && len(controller.GenerateName) > 0 { return nil, errors.NewNotFound(api.Resource("replicationcontrollers"), controller.Name) } // controller name is required to get rc back return r.c.ReplicationControllers(controller.Namespace).Get(controller.Name) }
func TestErrorNew(t *testing.T) { err := NewAlreadyExists(api.Resource("tests"), "1") if !IsAlreadyExists(err) { t.Errorf("expected to be %s", unversioned.StatusReasonAlreadyExists) } if IsConflict(err) { t.Errorf("expected to not be %s", unversioned.StatusReasonConflict) } if IsNotFound(err) { t.Errorf(fmt.Sprintf("expected to not be %s", unversioned.StatusReasonNotFound)) } if IsInvalid(err) { t.Errorf("expected to not be %s", unversioned.StatusReasonInvalid) } if IsBadRequest(err) { t.Errorf("expected to not be %s", unversioned.StatusReasonBadRequest) } if IsForbidden(err) { t.Errorf("expected to not be %s", unversioned.StatusReasonForbidden) } if IsServerTimeout(err) { t.Errorf("expected to not be %s", unversioned.StatusReasonServerTimeout) } if IsMethodNotSupported(err) { t.Errorf("expected to not be %s", unversioned.StatusReasonMethodNotAllowed) } if !IsConflict(NewConflict(api.Resource("tests"), "2", errors.New("message"))) { t.Errorf("expected to be conflict") } if !IsNotFound(NewNotFound(api.Resource("tests"), "3")) { t.Errorf("expected to be %s", unversioned.StatusReasonNotFound) } if !IsInvalid(NewInvalid(api.Kind("Test"), "2", nil)) { t.Errorf("expected to be %s", unversioned.StatusReasonInvalid) } if !IsBadRequest(NewBadRequest("reason")) { t.Errorf("expected to be %s", unversioned.StatusReasonBadRequest) } if !IsForbidden(NewForbidden(api.Resource("tests"), "2", errors.New("reason"))) { t.Errorf("expected to be %s", unversioned.StatusReasonForbidden) } if !IsUnauthorized(NewUnauthorized("reason")) { t.Errorf("expected to be %s", unversioned.StatusReasonUnauthorized) } if !IsServerTimeout(NewServerTimeout(api.Resource("tests"), "reason", 0)) { t.Errorf("expected to be %s", unversioned.StatusReasonServerTimeout) } if time, ok := SuggestsClientDelay(NewServerTimeout(api.Resource("tests"), "doing something", 10)); time != 10 || !ok { t.Errorf("expected to be %s", unversioned.StatusReasonServerTimeout) } if time, ok := SuggestsClientDelay(NewTimeoutError("test reason", 10)); time != 10 || !ok { t.Errorf("expected to be %s", unversioned.StatusReasonTimeout) } if !IsMethodNotSupported(NewMethodNotSupported(api.Resource("foos"), "delete")) { t.Errorf("expected to be %s", unversioned.StatusReasonMethodNotAllowed) } }
func TestSyncBatchIgnoresNotFound(t *testing.T) { client := fake.Clientset{} syncer := newTestManager(&client) client.AddReactor("get", "pods", func(action core.Action) (bool, runtime.Object, error) { return true, nil, errors.NewNotFound(api.Resource("pods"), "test-pod") }) syncer.SetPodStatus(getTestPod(), getRandomPodStatus()) syncer.testSyncBatch() verifyActions(t, syncer.kubeClient, []core.Action{ core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: unversioned.GroupVersionResource{Resource: "pods"}}}, }) }
func (r *NodeRegistry) GetNode(ctx api.Context, nodeID string) (*api.Node, error) { r.Lock() defer r.Unlock() if r.Err != nil { return nil, r.Err } for _, node := range r.Nodes.Items { if node.Name == nodeID { return &node, nil } } return nil, errors.NewNotFound(api.Resource("nodes"), nodeID) }
func (tw *baseTimeoutWriter) timeout(msg string) { tw.mu.Lock() defer tw.mu.Unlock() if !tw.wroteHeader && !tw.hijacked { tw.w.WriteHeader(http.StatusGatewayTimeout) if msg != "" { tw.w.Write([]byte(msg)) } else { enc := json.NewEncoder(tw.w) enc.Encode(errors.NewServerTimeout(api.Resource(""), "", 0)) } } tw.timedOut = true }
func TestGenericHttpResponseCheckerLimitReader(t *testing.T) { responseChecker := NewGenericHttpResponseChecker(api.Resource("pods"), "foo") excessedString := strings.Repeat("a", (maxReadLength + 10000)) resp := &http.Response{ Body: ioutil.NopCloser(bytes.NewBufferString(excessedString)), StatusCode: http.StatusBadRequest, } err := responseChecker.Check(resp) if err == nil { t.Error("unexpected non-error") } if len(err.Error()) != maxReadLength { t.Errorf("expected lenth of error message: %d, saw: %d", maxReadLength, len(err.Error())) } }
func TestGenericHttpResponseChecker(t *testing.T) { responseChecker := NewGenericHttpResponseChecker(api.Resource("pods"), "foo") tests := []struct { resp *http.Response expectError bool expected error name string }{ { resp: &http.Response{ Body: ioutil.NopCloser(bytes.NewBufferString("Success")), StatusCode: http.StatusOK, }, expectError: false, name: "ok", }, { resp: &http.Response{ Body: ioutil.NopCloser(bytes.NewBufferString("Invalid request.")), StatusCode: http.StatusBadRequest, }, expectError: true, expected: errors.NewBadRequest("Invalid request."), name: "bad request", }, { resp: &http.Response{ Body: ioutil.NopCloser(bytes.NewBufferString("Pod does not exist.")), StatusCode: http.StatusInternalServerError, }, expectError: true, expected: errors.NewInternalError(fmt.Errorf("%s", "Pod does not exist.")), name: "internal server error", }, } for _, test := range tests { err := responseChecker.Check(test.resp) if test.expectError && err == nil { t.Error("unexpected non-error") } if !test.expectError && err != nil { t.Errorf("unexpected error: %v", err) } if test.expectError && !reflect.DeepEqual(err, test.expected) { t.Errorf("expected: %s, saw: %s", test.expected, err) } } }
// NewREST returns a RESTStorage object that will work with ConfigMap objects. func NewREST(opts generic.RESTOptions) *REST { prefix := "/configmaps" newListFunc := func() runtime.Object { return &api.ConfigMapList{} } storageInterface := opts.Decorator( opts.Storage, 100, &api.ConfigMap{}, prefix, configmap.Strategy, newListFunc) store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.ConfigMap{} }, // NewListFunc returns an object to store results of an etcd list. NewListFunc: newListFunc, // Produces a path that etcd understands, to the root of the resource // by combining the namespace in the context with the given prefix. KeyRootFunc: func(ctx api.Context) string { return registry.NamespaceKeyRootFunc(ctx, prefix) }, // Produces a path that etcd understands, to the resource by combining // the namespace in the context with the given prefix KeyFunc: func(ctx api.Context, name string) (string, error) { return registry.NamespaceKeyFunc(ctx, prefix, name) }, // Retrieves the name field of a ConfigMap object. ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.ConfigMap).Name, nil }, // Matches objects based on labels/fields for list and watch PredicateFunc: configmap.MatchConfigMap, QualifiedResource: api.Resource("configmaps"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: configmap.Strategy, UpdateStrategy: configmap.Strategy, DeleteStrategy: configmap.Strategy, Storage: storageInterface, } return &REST{store} }
func (p *testPatcher) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { currentPod := p.startingPod if p.numUpdates > 0 { currentPod = p.updatePod } p.numUpdates++ obj, err := objInfo.UpdatedObject(ctx, currentPod) if err != nil { return nil, false, err } inPod := obj.(*api.Pod) if inPod.ResourceVersion != p.updatePod.ResourceVersion { return nil, false, apierrors.NewConflict(api.Resource("pods"), inPod.Name, fmt.Errorf("existing %v, new %v", p.updatePod.ResourceVersion, inPod.ResourceVersion)) } return inPod, false, nil }
func (m *FakeNodeHandler) Create(node *api.Node) (*api.Node, error) { m.createLock.Lock() defer func() { m.RequestCount++ m.createLock.Unlock() }() for _, n := range m.Existing { if n.Name == node.Name { return nil, apierrors.NewAlreadyExists(api.Resource("nodes"), node.Name) } } if m.CreateHook == nil || m.CreateHook(m, node) { nodeCopy := *node m.CreatedNodes = append(m.CreatedNodes, &nodeCopy) return node, nil } else { return nil, errors.New("Create error.") } }
// NewREST returns a RESTStorage object that will work against nodes. func NewStorage(opts generic.RESTOptions, connection client.ConnectionInfoGetter, proxyTransport http.RoundTripper) NodeStorage { prefix := "/minions" newListFunc := func() runtime.Object { return &api.NodeList{} } storageInterface := opts.Decorator( opts.Storage, cachesize.GetWatchCacheSizeByResource(cachesize.Nodes), &api.Node{}, prefix, node.Strategy, newListFunc) store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.Node{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { return prefix }, KeyFunc: func(ctx api.Context, name string) (string, error) { return registry.NoNamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Node).Name, nil }, PredicateFunc: node.MatchNode, QualifiedResource: api.Resource("nodes"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: node.Strategy, UpdateStrategy: node.Strategy, DeleteStrategy: node.Strategy, ExportStrategy: node.Strategy, Storage: storageInterface, } statusStore := *store statusStore.UpdateStrategy = node.StatusStrategy nodeREST := &REST{store, connection, proxyTransport} return NodeStorage{ Node: nodeREST, Status: &StatusREST{store: &statusStore}, Proxy: &noderest.ProxyREST{Store: store, Connection: client.ConnectionInfoGetter(nodeREST), ProxyTransport: proxyTransport}, } }
// NewREST returns a RESTStorage object that will work against namespaces. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST, *FinalizeREST) { prefix := "/namespaces" newListFunc := func() runtime.Object { return &api.NamespaceList{} } storageInterface := opts.Decorator( opts.Storage, cachesize.GetWatchCacheSizeByResource(cachesize.Namespaces), &api.Namespace{}, prefix, namespace.Strategy, newListFunc) store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.Namespace{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { return prefix }, KeyFunc: func(ctx api.Context, name string) (string, error) { return registry.NoNamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Namespace).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return namespace.MatchNamespace(label, field) }, QualifiedResource: api.Resource("namespaces"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: namespace.Strategy, UpdateStrategy: namespace.Strategy, DeleteStrategy: namespace.Strategy, ReturnDeletedObject: true, Storage: storageInterface, } statusStore := *store statusStore.UpdateStrategy = namespace.StatusStrategy finalizeStore := *store finalizeStore.UpdateStrategy = namespace.FinalizeStrategy return &REST{Store: store, status: &statusStore}, &StatusREST{store: &statusStore}, &FinalizeREST{store: &finalizeStore} }
func TestDeleteAllNotFound(t *testing.T) { _, svc, _ := testData() f, tf, codec := NewAPIFactory() // Add an item to the list which will result in a 404 on delete svc.Items = append(svc.Items, api.Service{ObjectMeta: api.ObjectMeta{Name: "foo"}}) notFoundError := &errors.NewNotFound(api.Resource("services"), "foo").ErrStatus tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case p == "/namespaces/test/services" && m == "GET": return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, svc)}, nil case p == "/namespaces/test/services/foo" && m == "DELETE": return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: objBody(codec, notFoundError)}, nil case p == "/namespaces/test/services/baz" && m == "DELETE": return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &svc.Items[0])}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdDelete(f, buf) cmd.Flags().Set("all", "true") cmd.Flags().Set("cascade", "false") // Make sure we can explicitly choose to fail on NotFound errors, even with --all cmd.Flags().Set("ignore-not-found", "false") cmd.Flags().Set("output", "name") err := RunDelete(f, buf, cmd, []string{"services"}, &DeleteOptions{}) if err == nil || !errors.IsNotFound(err) { t.Errorf("unexpected error: expected NotFound, got %v", err) } }
func NewTestGenericStoreRegistry(t *testing.T) (*etcdtesting.EtcdTestServer, *Store) { podPrefix := "/pods" server := etcdtesting.NewEtcdTestClientServer(t) s := etcdstorage.NewEtcdStorage(server.Client, testapi.Default.StorageCodec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize) strategy := &testRESTStrategy{api.Scheme, api.SimpleNameGenerator, true, false, true} return server, &Store{ NewFunc: func() runtime.Object { return &api.Pod{} }, NewListFunc: func() runtime.Object { return &api.PodList{} }, QualifiedResource: api.Resource("pods"), CreateStrategy: strategy, UpdateStrategy: strategy, DeleteStrategy: strategy, KeyRootFunc: func(ctx api.Context) string { return podPrefix }, KeyFunc: func(ctx api.Context, id string) (string, error) { if _, ok := api.NamespaceFrom(ctx); !ok { return "", fmt.Errorf("namespace is required") } return path.Join(podPrefix, id), nil }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Pod).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return &generic.SelectionPredicate{ Label: label, Field: field, GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { pod, ok := obj.(*api.Pod) if !ok { return nil, nil, fmt.Errorf("not a pod") } return labels.Set(pod.ObjectMeta.Labels), generic.ObjectMetaFieldsSet(pod.ObjectMeta, true), nil }, } }, Storage: s, } }
// NewREST returns a RESTStorage object that will work against events. func NewREST(opts generic.RESTOptions, ttl uint64) *REST { prefix := "/events" // We explicitly do NOT do any decoration here - switching on Cacher // for events will lead to too high memory consumption. storageInterface := opts.Storage store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.Event{} }, NewListFunc: func() runtime.Object { return &api.EventList{} }, KeyRootFunc: func(ctx api.Context) string { return registry.NamespaceKeyRootFunc(ctx, prefix) }, KeyFunc: func(ctx api.Context, id string) (string, error) { return registry.NamespaceKeyFunc(ctx, prefix, id) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Event).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return event.MatchEvent(label, field) }, TTLFunc: func(runtime.Object, uint64, bool) (uint64, error) { return ttl, nil }, QualifiedResource: api.Resource("events"), DeleteCollectionWorkers: opts.DeleteCollectionWorkers, CreateStrategy: event.Strategy, UpdateStrategy: event.Strategy, DeleteStrategy: event.Strategy, Storage: storageInterface, } return &REST{store} }
//TODO: Unify with RecoverPanics? func logStackOnRecover(s runtime.NegotiatedSerializer, panicReason interface{}, w http.ResponseWriter) { var buffer bytes.Buffer buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason)) for i := 2; ; i += 1 { _, file, line, ok := rt.Caller(i) if !ok { break } buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line)) } glog.Errorln(buffer.String()) headers := http.Header{} if ct := w.Header().Get("Content-Type"); len(ct) > 0 { headers.Set("Accept", ct) } errorNegotiated(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", api.Resource(""), "", "", 0, false), s, unversioned.GroupVersion{}, w, &http.Request{Header: headers}) }
func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { proxyHandlerTraceID := rand.Int63() var verb string var apiResource string var httpCode int reqStart := time.Now() defer metrics.Monitor(&verb, &apiResource, net.GetHTTPClient(req), w.Header().Get("Content-Type"), httpCode, reqStart) requestInfo, err := r.requestInfoResolver.GetRequestInfo(req) if err != nil || !requestInfo.IsResourceRequest { notFound(w, req) httpCode = http.StatusNotFound return } verb = requestInfo.Verb namespace, resource, parts := requestInfo.Namespace, requestInfo.Resource, requestInfo.Parts ctx, ok := r.context.Get(req) if !ok { ctx = api.NewContext() } ctx = api.WithNamespace(ctx, namespace) if len(parts) < 2 { notFound(w, req) httpCode = http.StatusNotFound return } id := parts[1] remainder := "" if len(parts) > 2 { proxyParts := parts[2:] remainder = strings.Join(proxyParts, "/") if strings.HasSuffix(req.URL.Path, "/") { // The original path had a trailing slash, which has been stripped // by KindAndNamespace(). We should add it back because some // servers (like etcd) require it. remainder = remainder + "/" } } 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 gv := unversioned.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion} redirector, ok := storage.(rest.Redirector) if !ok { httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource) httpCode = errorNegotiated(errors.NewMethodNotSupported(api.Resource(resource), "proxy"), r.serializer, gv, w, req) return } location, roundTripper, err := redirector.ResourceLocation(ctx, id) if err != nil { httplog.LogOf(req, w).Addf("Error getting ResourceLocation: %v", err) httpCode = errorNegotiated(err, r.serializer, gv, w, req) return } if location == nil { httplog.LogOf(req, w).Addf("ResourceLocation for %v returned nil", id) notFound(w, req) httpCode = http.StatusNotFound return } if roundTripper != nil { glog.V(5).Infof("[%x: %v] using transport %T...", proxyHandlerTraceID, req.URL, roundTripper) } // Default to http if location.Scheme == "" { location.Scheme = "http" } // Add the subpath if len(remainder) > 0 { location.Path = singleJoiningSlash(location.Path, remainder) } // Start with anything returned from the storage, and add the original request's parameters values := location.Query() for k, vs := range req.URL.Query() { for _, v := range vs { values.Add(k, v) } } location.RawQuery = values.Encode() newReq, err := http.NewRequest(req.Method, location.String(), req.Body) if err != nil { httpCode = errorNegotiated(err, r.serializer, gv, w, req) return } httpCode = http.StatusOK newReq.Header = req.Header newReq.ContentLength = req.ContentLength // Copy the TransferEncoding is for future-proofing. Currently Go only supports "chunked" and // it can determine the TransferEncoding based on ContentLength and the Body. newReq.TransferEncoding = req.TransferEncoding // TODO convert this entire proxy to an UpgradeAwareProxy similar to // https://github.com/openshift/origin/blob/master/pkg/util/httpproxy/upgradeawareproxy.go. // That proxy needs to be modified to support multiple backends, not just 1. if r.tryUpgrade(w, req, newReq, location, roundTripper, gv) { return } // Redirect requests of the form "/{resource}/{name}" to "/{resource}/{name}/" // This is essentially a hack for http://issue.k8s.io/4958. // Note: Keep this code after tryUpgrade to not break that flow. if len(parts) == 2 && !strings.HasSuffix(req.URL.Path, "/") { var queryPart string if len(req.URL.RawQuery) > 0 { queryPart = "?" + req.URL.RawQuery } w.Header().Set("Location", req.URL.Path+"/"+queryPart) w.WriteHeader(http.StatusMovedPermanently) return } start := time.Now() glog.V(4).Infof("[%x] Beginning proxy %s...", proxyHandlerTraceID, req.URL) defer func() { glog.V(4).Infof("[%x] Proxy %v finished %v.", proxyHandlerTraceID, req.URL, time.Now().Sub(start)) }() proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: location.Scheme, Host: location.Host}) alreadyRewriting := false if roundTripper != nil { _, alreadyRewriting = roundTripper.(*proxyutil.Transport) glog.V(5).Infof("[%x] Not making a reriting transport for proxy %s...", proxyHandlerTraceID, req.URL) } if !alreadyRewriting { glog.V(5).Infof("[%x] making a transport for proxy %s...", proxyHandlerTraceID, req.URL) prepend := path.Join(r.prefix, resource, id) if len(namespace) > 0 { prepend = path.Join(r.prefix, "namespaces", namespace, resource, id) } pTransport := &proxyutil.Transport{ Scheme: req.URL.Scheme, Host: req.URL.Host, PathPrepend: prepend, RoundTripper: roundTripper, } roundTripper = pTransport } proxy.Transport = roundTripper proxy.FlushInterval = 200 * time.Millisecond proxy.ServeHTTP(w, newReq) }
func TestSyncBatchNoDeadlock(t *testing.T) { client := &fake.Clientset{} m := newTestManager(client) pod := getTestPod() // Setup fake client. var ret api.Pod var err error client.AddReactor("*", "pods", func(action core.Action) (bool, runtime.Object, error) { switch action := action.(type) { case core.GetAction: assert.Equal(t, pod.Name, action.GetName(), "Unexpeted GetAction: %+v", action) case core.UpdateAction: assert.Equal(t, pod.Name, action.GetObject().(*api.Pod).Name, "Unexpeted UpdateAction: %+v", action) default: assert.Fail(t, "Unexpected Action: %+v", action) } return true, &ret, err }) pod.Status.ContainerStatuses = []api.ContainerStatus{{State: api.ContainerState{Running: &api.ContainerStateRunning{}}}} getAction := core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: unversioned.GroupVersionResource{Resource: "pods"}}} updateAction := core.UpdateActionImpl{ActionImpl: core.ActionImpl{Verb: "update", Resource: unversioned.GroupVersionResource{Resource: "pods"}, Subresource: "status"}} // Pod not found. ret = *pod err = errors.NewNotFound(api.Resource("pods"), pod.Name) m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []core.Action{getAction}) client.ClearActions() // Pod was recreated. ret.UID = "other_pod" err = nil m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []core.Action{getAction}) client.ClearActions() // Pod not deleted (success case). ret = *pod m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []core.Action{getAction, updateAction}) client.ClearActions() // Pod is terminated, but still running. pod.DeletionTimestamp = new(unversioned.Time) m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []core.Action{getAction, updateAction}) client.ClearActions() // Pod is terminated successfully. pod.Status.ContainerStatuses[0].State.Running = nil pod.Status.ContainerStatuses[0].State.Terminated = &api.ContainerStateTerminated{} m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []core.Action{getAction, updateAction}) client.ClearActions() // Error case. err = fmt.Errorf("intentional test error") m.SetPodStatus(pod, getRandomPodStatus()) m.testSyncBatch() verifyActions(t, client, []core.Action{getAction}) client.ClearActions() }
func (rs *REST) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { oldService, err := rs.registry.GetService(ctx, name) if err != nil { return nil, false, err } obj, err := objInfo.UpdatedObject(ctx, oldService) if err != nil { return nil, false, err } service := obj.(*api.Service) if !api.ValidNamespace(ctx, &service.ObjectMeta) { return nil, false, errors.NewConflict(api.Resource("services"), service.Namespace, fmt.Errorf("Service.Namespace does not match the provided context")) } // Copy over non-user fields // TODO: make this a merge function if errs := validation.ValidateServiceUpdate(service, oldService); len(errs) > 0 { return nil, false, errors.NewInvalid(api.Kind("Service"), service.Name, errs) } nodePortOp := portallocator.StartOperation(rs.serviceNodePorts) defer nodePortOp.Finish() assignNodePorts := shouldAssignNodePorts(service) oldNodePorts := CollectServiceNodePorts(oldService) newNodePorts := []int{} if assignNodePorts { for i := range service.Spec.Ports { servicePort := &service.Spec.Ports[i] nodePort := int(servicePort.NodePort) if nodePort != 0 { if !contains(oldNodePorts, nodePort) { err := nodePortOp.Allocate(nodePort) if err != nil { el := field.ErrorList{field.Invalid(field.NewPath("spec", "ports").Index(i).Child("nodePort"), nodePort, err.Error())} return nil, false, errors.NewInvalid(api.Kind("Service"), service.Name, el) } } } else { nodePort, err = nodePortOp.AllocateNext() if err != nil { // TODO: what error should be returned here? It's not a // field-level validation failure (the field is valid), and it's // not really an internal error. return nil, false, errors.NewInternalError(fmt.Errorf("failed to allocate a nodePort: %v", err)) } servicePort.NodePort = int32(nodePort) } // Detect duplicate node ports; this should have been caught by validation, so we panic if contains(newNodePorts, nodePort) { panic("duplicate node port") } newNodePorts = append(newNodePorts, nodePort) } } else { // Validate should have validated that nodePort == 0 } // The comparison loops are O(N^2), but we don't expect N to be huge // (there's a hard-limit at 2^16, because they're ports; and even 4 ports would be a lot) for _, oldNodePort := range oldNodePorts { if !contains(newNodePorts, oldNodePort) { continue } nodePortOp.ReleaseDeferred(oldNodePort) } // Remove any LoadBalancerStatus now if Type != LoadBalancer; // although loadbalancer delete is actually asynchronous, we don't need to expose the user to that complexity. if service.Spec.Type != api.ServiceTypeLoadBalancer { service.Status.LoadBalancer = api.LoadBalancerStatus{} } out, err := rs.registry.UpdateService(ctx, service) if err == nil { el := nodePortOp.Commit() if el != nil { // problems should be fixed by an eventual reconciliation / restart glog.Errorf("error(s) committing NodePorts changes: %v", el) } } return out, false, err }
func serviceErrorHandler(s runtime.NegotiatedSerializer, requestResolver *RequestInfoResolver, apiVersions []string, serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) { errorNegotiated(apierrors.NewGenericServerResponse(serviceErr.Code, "", api.Resource(""), "", "", 0, false), s, unversioned.GroupVersion{}, response.ResponseWriter, request.Request) }
// Delete enforces life-cycle rules for namespace termination func (r *REST) Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error) { nsObj, err := r.Get(ctx, name) if err != nil { return nil, err } namespace := nsObj.(*api.Namespace) // Ensure we have a UID precondition if options == nil { options = api.NewDeleteOptions(0) } if options.Preconditions == nil { options.Preconditions = &api.Preconditions{} } if options.Preconditions.UID == nil { options.Preconditions.UID = &namespace.UID } else if *options.Preconditions.UID != namespace.UID { err = apierrors.NewConflict( api.Resource("namespaces"), name, fmt.Errorf("Precondition failed: UID in precondition: %v, UID in object meta: %v", *options.Preconditions.UID, namespace.UID), ) return nil, err } // upon first request to delete, we switch the phase to start namespace termination // TODO: enhance graceful deletion's calls to DeleteStrategy to allow phase change and finalizer patterns if namespace.DeletionTimestamp.IsZero() { key, err := r.Store.KeyFunc(ctx, name) if err != nil { return nil, err } preconditions := storage.Preconditions{UID: options.Preconditions.UID} out := r.Store.NewFunc() err = r.Store.Storage.GuaranteedUpdate( ctx, key, out, false, &preconditions, storage.SimpleUpdate(func(existing runtime.Object) (runtime.Object, error) { existingNamespace, ok := existing.(*api.Namespace) if !ok { // wrong type return nil, fmt.Errorf("expected *api.Namespace, got %v", existing) } // Set the deletion timestamp if needed if existingNamespace.DeletionTimestamp.IsZero() { now := unversioned.Now() existingNamespace.DeletionTimestamp = &now } // Set the namespace phase to terminating, if needed if existingNamespace.Status.Phase != api.NamespaceTerminating { existingNamespace.Status.Phase = api.NamespaceTerminating } return existingNamespace, nil }), ) if err != nil { err = storageerr.InterpretGetError(err, api.Resource("namespaces"), name) err = storageerr.InterpretUpdateError(err, api.Resource("namespaces"), name) if _, ok := err.(*apierrors.StatusError); !ok { err = apierrors.NewInternalError(err) } return nil, err } return out, nil } // prior to final deletion, we must ensure that finalizers is empty if len(namespace.Spec.Finalizers) != 0 { err = apierrors.NewConflict(api.Resource("namespaces"), namespace.Name, fmt.Errorf("The system is ensuring all content is removed from this namespace. Upon completion, this namespace will automatically be purged by the system.")) return nil, err } return r.Store.Delete(ctx, name, options) }