func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) { printer := spew.ConfigState{DisableMethods: true} name := reflect.TypeOf(item).Elem().Name() data, err := codec.Encode(item) if err != nil { t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", item)) return } obj2, err := codec.Decode(data) if err != nil { t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), printer.Sprintf("%#v", item)) return } if !api.Semantic.DeepEqual(item, obj2) { t.Errorf("1: %v: diff: %v\nCodec: %v\nData: %s\nSource: %#v\nFinal: %#v", name, util.ObjectGoPrintDiff(item, obj2), codec, string(data), printer.Sprintf("%#v", item), printer.Sprintf("%#v", obj2)) return } obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) err = codec.DecodeInto(data, obj3) if err != nil { t.Errorf("2: %v: %v", name, err) return } if !api.Semantic.DeepEqual(item, obj3) { t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(item, obj3), codec) return } }
// CreateResource returns a function that will handle a resource creation. func CreateResource(r RESTCreater, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, typer runtime.ObjectTyper, resource string, admit admission.Interface) restful.RouteFunction { return func(req *restful.Request, res *restful.Response) { w := res.ResponseWriter // TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer) timeout := parseTimeout(req.Request.URL.Query().Get("timeout")) namespace, err := namer.Namespace(req) if err != nil { errorJSON(err, codec, w) return } ctx := ctxFn(req) ctx = api.WithNamespace(ctx, namespace) body, err := readBody(req.Request) if err != nil { errorJSON(err, codec, w) return } obj := r.New() if err := codec.DecodeInto(body, obj); err != nil { err = transformDecodeError(typer, err, obj, body) errorJSON(err, codec, w) return } err = admit.Admit(admission.NewAttributesRecord(obj, namespace, resource, "CREATE")) if err != nil { errorJSON(err, codec, w) return } result, err := finishRequest(timeout, func() (runtime.Object, error) { out, err := r.Create(ctx, obj) if status, ok := out.(*api.Status); ok && err == nil && status.Code == 0 { status.Code = http.StatusCreated } return out, err }) if err != nil { errorJSON(err, codec, w) return } if err := setSelfLink(result, req, namer); err != nil { errorJSON(err, codec, w) return } writeJSON(http.StatusCreated, codec, result, w) } }
// ToWireFormat takes input 'data' as either json or yaml, checks that it parses as the // appropriate object type, and returns json for sending to the API or an error. func (p *Parser) ToWireFormat(data []byte, storage string, c runtime.Codec) ([]byte, error) { prototypeType, found := p.storageToType[storage] if !found { return nil, fmt.Errorf("unknown storage type: %v", storage) } obj := reflect.New(prototypeType).Interface().(runtime.Object) err := c.DecodeInto(data, obj) if err != nil { return nil, err } return c.Encode(obj) }
func roundTrip(t *testing.T, codec runtime.Codec, originalItem runtime.Object) { // Make a copy of the originalItem to give to conversion functions // This lets us know if conversion messed with the input object deepCopy, err := api.Scheme.DeepCopy(originalItem) if err != nil { t.Errorf("Could not copy object: %v", err) return } item := deepCopy.(runtime.Object) name := reflect.TypeOf(item).Elem().Name() data, err := codec.Encode(item) if err != nil { if conversion.IsNotRegisteredError(err) { t.Logf("%v is not registered", name) return } t.Errorf("%v: %v (%#v)", name, err, item) return } obj2, err := codec.Decode(data) if err != nil { t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), originalItem) return } if reflect.TypeOf(item) != reflect.TypeOf(obj2) { obj2conv := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) if err := api.Scheme.Convert(obj2, obj2conv); err != nil { t.Errorf("0X: no conversion from %v to %v: %v", reflect.TypeOf(item), reflect.TypeOf(obj2), err) return } obj2 = obj2conv } if !api.Semantic.DeepEqual(originalItem, obj2) { t.Errorf("1: %v: diff: %v\nCodec: %v\nData: %s\nSource: %s", name, util.ObjectDiff(originalItem, obj2), codec, string(data), util.ObjectGoPrintSideBySide(originalItem, obj2)) return } obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) err = codec.DecodeInto(data, obj3) if err != nil { t.Errorf("2: %v: %v", name, err) return } if !api.Semantic.DeepEqual(originalItem, obj3) { t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(originalItem, obj3), codec) return } }
func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) { name := reflect.TypeOf(source).Elem().Name() apiObjectFuzzer.Fuzz(source) j, err := runtime.FindJSONBase(source) if err != nil { t.Fatalf("Unexpected error %v for %#v", err, source) } j.SetKind("") j.SetAPIVersion("") data, err := codec.Encode(source) if err != nil { t.Errorf("%v: %v (%#v)", name, err, source) return } obj2, err := codec.Decode(data) if err != nil { t.Errorf("%v: %v", name, err) return } else { if !reflect.DeepEqual(source, obj2) { t.Errorf("1: %v: diff: %v", name, runtime.ObjectDiff(source, obj2)) return } } obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface().(runtime.Object) err = codec.DecodeInto(data, obj3) if err != nil { t.Errorf("2: %v: %v", name, err) return } else { if !reflect.DeepEqual(source, obj3) { t.Errorf("3: %v: diff: %v", name, runtime.ObjectDiff(source, obj3)) return } } }
// PatchResource returns a function that will handle a resource patch // TODO: Eventually PatchResource should just use AtomicUpdate and this routine should be a bit cleaner func PatchResource(r RESTPatcher, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, typer runtime.ObjectTyper, resource string, admit admission.Interface) restful.RouteFunction { return func(req *restful.Request, res *restful.Response) { w := res.ResponseWriter // TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer) timeout := parseTimeout(req.Request.URL.Query().Get("timeout")) namespace, name, err := namer.Name(req) if err != nil { errorJSON(err, codec, w) return } obj := r.New() // PATCH requires same permission as UPDATE err = admit.Admit(admission.NewAttributesRecord(obj, namespace, resource, "UPDATE")) if err != nil { errorJSON(err, codec, w) return } ctx := ctxFn(req) ctx = api.WithNamespace(ctx, namespace) original, err := r.Get(ctx, name) if err != nil { errorJSON(err, codec, w) return } originalObjJs, err := codec.Encode(original) if err != nil { errorJSON(err, codec, w) return } patchJs, err := readBody(req.Request) if err != nil { errorJSON(err, codec, w) return } patchedObjJs, err := jsonpatch.MergePatch(originalObjJs, patchJs) if err != nil { errorJSON(err, codec, w) return } if err := codec.DecodeInto(patchedObjJs, obj); err != nil { errorJSON(err, codec, w) return } if err := checkName(obj, name, namespace, namer); err != nil { errorJSON(err, codec, w) return } result, err := finishRequest(timeout, func() (runtime.Object, error) { // update should never create as previous get would fail obj, _, err := r.Update(ctx, obj) return obj, err }) if err != nil { errorJSON(err, codec, w) return } if err := setSelfLink(result, req, namer); err != nil { errorJSON(err, codec, w) return } writeJSON(http.StatusOK, codec, result, w) } }