func (t *Tester) testListMatchLabels(obj runtime.Object, assignFn AssignFunc) { ctx := t.TestContext() testLabels := map[string]string{"key": "value"} foo3 := copyOrDie(obj) t.setObjectMeta(foo3, "foo3") foo4 := copyOrDie(obj) foo4Meta := t.getObjectMetaOrFail(foo4) foo4Meta.Name = "foo4" foo4Meta.Namespace = genericapirequest.NamespaceValue(ctx) foo4Meta.Labels = testLabels objs := ([]runtime.Object{foo3, foo4}) assignFn(objs) filtered := []runtime.Object{objs[1]} selector := labels.SelectorFromSet(labels.Set(testLabels)) options := &api.ListOptions{LabelSelector: selector} listObj, err := t.storage.(rest.Lister).List(ctx, options) if err != nil { t.Errorf("unexpected error: %v", err) } items, err := listToItems(listObj) if err != nil { t.Errorf("unexpected error: %v", err) } if len(items) != len(filtered) { t.Errorf("unexpected number of items: %v", len(items)) } if !api.Semantic.DeepEqual(filtered, items) { t.Errorf("expected: %#v, got: %#v", filtered, items) } }
// Bind just does a POST binding RPC. func (b *binder) Bind(binding *v1.Binding) error { glog.V(3).Infof("Attempting to bind %v to %v", binding.Name, binding.Target.Name) ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), binding.Namespace) return b.Client.Core().RESTClient().Post().Namespace(genericapirequest.NamespaceValue(ctx)).Resource("bindings").Body(binding).Do().Error() // TODO: use Pods interface for binding once clusters are upgraded // return b.Pods(binding.Namespace).Bind(binding) }
// TestValidNamespace validates that namespace rules are enforced on a resource prior to create or update func TestValidNamespace(t *testing.T) { ctx := genericapirequest.NewDefaultContext() namespace, _ := genericapirequest.NamespaceFrom(ctx) resource := api.ReplicationController{} if !ValidNamespace(ctx, &resource.ObjectMeta) { t.Fatalf("expected success") } if namespace != resource.Namespace { t.Fatalf("expected resource to have the default namespace assigned during validation") } resource = api.ReplicationController{ObjectMeta: metav1.ObjectMeta{Namespace: "other"}} if ValidNamespace(ctx, &resource.ObjectMeta) { t.Fatalf("Expected error that resource and context errors do not match because resource has different namespace") } ctx = genericapirequest.NewContext() if ValidNamespace(ctx, &resource.ObjectMeta) { t.Fatalf("Expected error that resource and context errors do not match since context has no namespace") } ctx = genericapirequest.NewContext() ns := genericapirequest.NamespaceValue(ctx) if ns != "" { t.Fatalf("Expected the empty string") } }
func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object) (runtime.Object, error) { localSubjectAccessReview, ok := obj.(*authorizationapi.LocalSubjectAccessReview) if !ok { return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a LocaLocalSubjectAccessReview: %#v", obj)) } if errs := authorizationvalidation.ValidateLocalSubjectAccessReview(localSubjectAccessReview); len(errs) > 0 { return nil, kapierrors.NewInvalid(authorizationapi.Kind(localSubjectAccessReview.Kind), "", errs) } namespace := genericapirequest.NamespaceValue(ctx) if len(namespace) == 0 { return nil, kapierrors.NewBadRequest(fmt.Sprintf("namespace is required on this type: %v", namespace)) } if namespace != localSubjectAccessReview.Namespace { return nil, kapierrors.NewBadRequest(fmt.Sprintf("spec.resourceAttributes.namespace must match namespace: %v", namespace)) } authorizationAttributes := authorizationutil.AuthorizationAttributesFrom(localSubjectAccessReview.Spec) allowed, reason, evaluationErr := r.authorizer.Authorize(authorizationAttributes) localSubjectAccessReview.Status = authorizationapi.SubjectAccessReviewStatus{ Allowed: allowed, Reason: reason, } if evaluationErr != nil { localSubjectAccessReview.Status.EvaluationError = evaluationErr.Error() } return localSubjectAccessReview, nil }
func (t *Tester) setObjectMeta(obj runtime.Object, name string) { meta := t.getObjectMetaOrFail(obj) meta.Name = name if t.clusterScope { meta.Namespace = api.NamespaceNone } else { meta.Namespace = genericapirequest.NamespaceValue(t.TestContext()) } meta.GenerateName = "" meta.Generation = 1 }
func (t *Tester) testGetMimatchedNamespace(obj runtime.Object) { ctx1 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar1") ctx2 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar2") objMeta := t.getObjectMetaOrFail(obj) objMeta.Name = t.namer(4) objMeta.Namespace = genericapirequest.NamespaceValue(ctx1) _, err := t.storage.(rest.Creater).Create(ctx1, obj) if err != nil { t.Errorf("unexpected error: %v", err) } _, err = t.storage.(rest.Getter).Get(ctx2, t.namer(4), &metav1.GetOptions{}) if t.clusterScope { if err != nil { t.Errorf("unexpected error: %v", err) } } else { if !errors.IsNotFound(err) { t.Errorf("unexpected error returned: %#v", err) } } }
// testGetDifferentNamespace ensures same-name objects in different namespaces do not clash func (t *Tester) testGetDifferentNamespace(obj runtime.Object) { if t.clusterScope { t.Fatalf("the test does not work in in cluster-scope") } objMeta := t.getObjectMetaOrFail(obj) objMeta.Name = t.namer(5) ctx1 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar3") objMeta.Namespace = genericapirequest.NamespaceValue(ctx1) _, err := t.storage.(rest.Creater).Create(ctx1, obj) if err != nil { t.Errorf("unexpected error: %v", err) } ctx2 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar4") objMeta.Namespace = genericapirequest.NamespaceValue(ctx2) _, err = t.storage.(rest.Creater).Create(ctx2, obj) if err != nil { t.Errorf("unexpected error: %v", err) } got1, err := t.storage.(rest.Getter).Get(ctx1, objMeta.Name, &metav1.GetOptions{}) if err != nil { t.Errorf("unexpected error: %v", err) } got1Meta := t.getObjectMetaOrFail(got1) if got1Meta.Name != objMeta.Name { t.Errorf("unexpected name of object: %#v, expected: %s", got1, objMeta.Name) } if got1Meta.Namespace != genericapirequest.NamespaceValue(ctx1) { t.Errorf("unexpected namespace of object: %#v, expected: %s", got1, genericapirequest.NamespaceValue(ctx1)) } got2, err := t.storage.(rest.Getter).Get(ctx2, objMeta.Name, &metav1.GetOptions{}) if err != nil { t.Errorf("unexpected error: %v", err) } got2Meta := t.getObjectMetaOrFail(got2) if got2Meta.Name != objMeta.Name { t.Errorf("unexpected name of object: %#v, expected: %s", got2, objMeta.Name) } if got2Meta.Namespace != genericapirequest.NamespaceValue(ctx2) { t.Errorf("unexpected namespace of object: %#v, expected: %s", got2, genericapirequest.NamespaceValue(ctx2)) } }
func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object) (runtime.Object, error) { tokenReview, ok := obj.(*authentication.TokenReview) if !ok { return nil, apierrors.NewBadRequest(fmt.Sprintf("not a TokenReview: %#v", obj)) } namespace := genericapirequest.NamespaceValue(ctx) if len(namespace) != 0 { return nil, apierrors.NewBadRequest(fmt.Sprintf("namespace is not allowed on this type: %v", namespace)) } if r.tokenAuthenticator == nil { return tokenReview, nil } // create a header that contains nothing but the token fakeReq := &http.Request{Header: http.Header{}} fakeReq.Header.Add("Authorization", "Bearer "+tokenReview.Spec.Token) tokenUser, ok, err := r.tokenAuthenticator.AuthenticateRequest(fakeReq) tokenReview.Status.Authenticated = ok if err != nil { tokenReview.Status.Error = err.Error() } if tokenUser != nil { tokenReview.Status.User = authentication.UserInfo{ Username: tokenUser.GetName(), UID: tokenUser.GetUID(), Groups: tokenUser.GetGroups(), Extra: map[string]authentication.ExtraValue{}, } for k, v := range tokenUser.GetExtra() { tokenReview.Status.User.Extra[k] = authentication.ExtraValue(v) } } return tokenReview, nil }
// patchResource divides PatchResource for easier unit testing func patchResource( ctx request.Context, admit updateAdmissionFunc, timeout time.Duration, versionedObj runtime.Object, patcher rest.Patcher, name string, patchType types.PatchType, patchJS []byte, namer ScopeNamer, copier runtime.ObjectCopier, resource schema.GroupVersionResource, codec runtime.Codec, ) (runtime.Object, error) { namespace := request.NamespaceValue(ctx) var ( originalObjJS []byte originalPatchedObjJS []byte originalObjMap map[string]interface{} originalPatchMap map[string]interface{} lastConflictErr error ) // applyPatch is called every time GuaranteedUpdate asks for the updated object, // and is given the currently persisted object as input. applyPatch := func(_ request.Context, _, currentObject runtime.Object) (runtime.Object, error) { // Make sure we actually have a persisted currentObject if hasUID, err := hasUID(currentObject); err != nil { return nil, err } else if !hasUID { return nil, errors.NewNotFound(resource.GroupResource(), name) } switch { case originalObjJS == nil && originalObjMap == nil: // first time through, // 1. apply the patch // 2. save the original and patched to detect whether there were conflicting changes on retries objToUpdate := patcher.New() // For performance reasons, in case of strategicpatch, we avoid json // marshaling and unmarshaling and operate just on map[string]interface{}. // In case of other patch types, we still have to operate on JSON // representations. switch patchType { case types.JSONPatchType, types.MergePatchType: originalJS, patchedJS, err := patchObjectJSON(patchType, codec, currentObject, patchJS, objToUpdate, versionedObj) if err != nil { return nil, err } originalObjJS, originalPatchedObjJS = originalJS, patchedJS case types.StrategicMergePatchType: originalMap, patchMap, err := strategicPatchObject(codec, currentObject, patchJS, objToUpdate, versionedObj) if err != nil { return nil, err } originalObjMap, originalPatchMap = originalMap, patchMap } if err := checkName(objToUpdate, name, namespace, namer); err != nil { return nil, err } return objToUpdate, nil default: // on a conflict, // 1. build a strategic merge patch from originalJS and the patchedJS. Different patch types can // be specified, but a strategic merge patch should be expressive enough handle them. Build the // patch with this type to handle those cases. // 2. build a strategic merge patch from originalJS and the currentJS // 3. ensure no conflicts between the two patches // 4. apply the #1 patch to the currentJS object // TODO: This should be one-step conversion that doesn't require // json marshaling and unmarshaling once #39017 is fixed. data, err := runtime.Encode(codec, currentObject) if err != nil { return nil, err } currentObjMap := make(map[string]interface{}) if err := json.Unmarshal(data, ¤tObjMap); err != nil { return nil, err } var currentPatchMap map[string]interface{} if originalObjMap != nil { var err error currentPatchMap, err = strategicpatch.CreateTwoWayMergeMapPatch(originalObjMap, currentObjMap, versionedObj) if err != nil { return nil, err } } else { if originalPatchMap == nil { // Compute original patch, if we already didn't do this in previous retries. originalPatch, err := strategicpatch.CreateTwoWayMergePatch(originalObjJS, originalPatchedObjJS, versionedObj) if err != nil { return nil, err } originalPatchMap = make(map[string]interface{}) if err := json.Unmarshal(originalPatch, &originalPatchMap); err != nil { return nil, err } } // Compute current patch. currentObjJS, err := runtime.Encode(codec, currentObject) if err != nil { return nil, err } currentPatch, err := strategicpatch.CreateTwoWayMergePatch(originalObjJS, currentObjJS, versionedObj) if err != nil { return nil, err } currentPatchMap = make(map[string]interface{}) if err := json.Unmarshal(currentPatch, ¤tPatchMap); err != nil { return nil, err } } hasConflicts, err := strategicpatch.HasConflicts(originalPatchMap, currentPatchMap) if err != nil { return nil, err } if hasConflicts { if glog.V(4) { diff1, _ := json.Marshal(currentPatchMap) diff2, _ := json.Marshal(originalPatchMap) glog.Infof("patchResource failed for resource %s, because there is a meaningful conflict.\n diff1=%v\n, diff2=%v\n", name, diff1, diff2) } // Return the last conflict error we got if we have one if lastConflictErr != nil { return nil, lastConflictErr } // Otherwise manufacture one of our own return nil, errors.NewConflict(resource.GroupResource(), name, nil) } objToUpdate := patcher.New() if err := applyPatchToObject(codec, currentObjMap, originalPatchMap, objToUpdate, versionedObj); err != nil { return nil, err } return objToUpdate, nil } } // applyAdmission is called every time GuaranteedUpdate asks for the updated object, // and is given the currently persisted object and the patched object as input. applyAdmission := func(ctx request.Context, patchedObject runtime.Object, currentObject runtime.Object) (runtime.Object, error) { return patchedObject, admit(patchedObject, currentObject) } updatedObjectInfo := rest.DefaultUpdatedObjectInfo(nil, copier, applyPatch, applyAdmission) return finishRequest(timeout, func() (runtime.Object, error) { updateObject, _, updateErr := patcher.Update(ctx, name, updatedObjectInfo) for i := 0; i < maxRetryWhenPatchConflicts && (errors.IsConflict(updateErr)); i++ { lastConflictErr = updateErr updateObject, _, updateErr = patcher.Update(ctx, name, updatedObjectInfo) } return updateObject, updateErr }) }