func TestVersionedEncoding(t *testing.T) { s, codec := GetTestScheme() out, err := runtime.Encode(codec, &TestType1{}, unversioned.GroupVersion{Version: "v2"}) if err != nil { t.Fatal(err) } if string(out) != `{"myVersionKey":"v2","myKindKey":"TestType1"}`+"\n" { t.Fatal(string(out)) } _, err = runtime.Encode(codec, &TestType1{}, unversioned.GroupVersion{Version: "v3"}) if err == nil { t.Fatal(err) } cf := newCodecFactory(s, testMetaFactory{}) encoder, _ := cf.SerializerForFileExtension("json") // codec that is unversioned uses the target version unversionedCodec := cf.CodecForVersions(encoder, nil, nil) _, err = runtime.Encode(unversionedCodec, &TestType1{}, unversioned.GroupVersion{Version: "v3"}) if err == nil || !runtime.IsNotRegisteredError(err) { t.Fatal(err) } // unversioned encode with no versions is written directly to wire out, err = runtime.Encode(unversionedCodec, &TestType1{}) if err != nil { t.Fatal(err) } if string(out) != `{"myVersionKey":"__internal","myKindKey":"TestType1"}`+"\n" { t.Fatal(string(out)) } }
// TODO: Have policies be created via an API call and stored in REST storage. func NewFromFile(path string) (policyList, error) { // File format is one map per line. This allows easy concatentation of files, // comments in files, and identification of errors by line number. file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() scanner := bufio.NewScanner(file) pl := make(policyList, 0) decoder := api.Codecs.UniversalDecoder() i := 0 unversionedLines := 0 for scanner.Scan() { i++ p := &api.Policy{} b := scanner.Bytes() // skip comment lines and blank lines trimmed := strings.TrimSpace(string(b)) if len(trimmed) == 0 || strings.HasPrefix(trimmed, "#") { continue } decodedObj, _, err := decoder.Decode(b, nil, nil) if err != nil { if !(runtime.IsMissingVersion(err) || runtime.IsMissingKind(err) || runtime.IsNotRegisteredError(err)) { return nil, policyLoadError{path, i, b, err} } unversionedLines++ // Migrate unversioned policy object oldPolicy := &v0.Policy{} if err := runtime.DecodeInto(decoder, b, oldPolicy); err != nil { return nil, policyLoadError{path, i, b, err} } if err := api.Scheme.Convert(oldPolicy, p); err != nil { return nil, policyLoadError{path, i, b, err} } pl = append(pl, p) continue } decodedPolicy, ok := decodedObj.(*api.Policy) if !ok { return nil, policyLoadError{path, i, b, fmt.Errorf("unrecognized object: %#v", decodedObj)} } pl = append(pl, decodedPolicy) } if unversionedLines > 0 { glog.Warningf(`Policy file %s contained unversioned rules. See docs/admin/authorization.md#abac-mode for ABAC file format details.`, path) } if err := scanner.Err(); err != nil { return nil, policyLoadError{path, -1, nil, err} } return pl, nil }
// Decode attempts to convert the provided data into YAML or JSON, extract the stored schema kind, apply the provided default gvk, and then // load that data into an object matching the desired schema kind or the provided into. If into is *runtime.Unknown, the raw data will be // extracted and no decoding will be performed. If into is not registered with the typer, then the object will be straight decoded using // normal JSON/YAML unmarshalling. If into is provided and the original data is not fully qualified with kind/version/group, the type of // the into will be used to alter the returned gvk. On success or most errors, the method will return the calculated schema kind. func (s *Serializer) Decode(originalData []byte, gvk *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) { if versioned, ok := into.(*runtime.VersionedObjects); ok { into = versioned.Last() obj, actual, err := s.Decode(originalData, gvk, into) if err != nil { return nil, actual, err } versioned.Objects = []runtime.Object{obj} return versioned, actual, nil } data := originalData if s.yaml { altered, err := yaml.YAMLToJSON(data) if err != nil { return nil, nil, err } data = altered } actual, err := s.meta.Interpret(data) if err != nil { return nil, nil, err } if gvk != nil { // apply kind and version defaulting from provided default if len(actual.Kind) == 0 { actual.Kind = gvk.Kind } if len(actual.Version) == 0 && len(actual.Group) == 0 { actual.Group = gvk.Group actual.Version = gvk.Version } if len(actual.Version) == 0 && actual.Group == gvk.Group { actual.Version = gvk.Version } } if unk, ok := into.(*runtime.Unknown); ok && unk != nil { unk.RawJSON = originalData // TODO: set content type here unk.GetObjectKind().SetGroupVersionKind(actual) return unk, actual, nil } if into != nil { typed, _, err := s.typer.ObjectKind(into) switch { case runtime.IsNotRegisteredError(err): if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(into); err != nil { return nil, actual, err } return into, actual, nil case err != nil: return nil, actual, err default: if len(actual.Kind) == 0 { actual.Kind = typed.Kind } if len(actual.Version) == 0 && len(actual.Group) == 0 { actual.Group = typed.Group actual.Version = typed.Version } if len(actual.Version) == 0 && actual.Group == typed.Group { actual.Version = typed.Version } } } if len(actual.Kind) == 0 { return nil, actual, runtime.NewMissingKindErr(string(originalData)) } if len(actual.Version) == 0 { return nil, actual, runtime.NewMissingVersionErr(string(originalData)) } // use the target if necessary obj, err := runtime.UseOrCreateObject(s.typer, s.creater, *actual, into) if err != nil { return nil, actual, err } if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(obj); err != nil { return nil, actual, err } return obj, actual, nil }