This repository has been archived by the owner on Aug 29, 2023. It is now read-only.
/
encode.go
152 lines (130 loc) · 4.89 KB
/
encode.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package serializer
import (
"github.com/sirupsen/logrus"
"github.com/weaveworks/libgitops/pkg/util"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type EncodingOptions struct {
// Use pretty printing when writing to the output. (Default: true)
// TODO: Fix that sometimes omitempty fields aren't respected
Pretty *bool
// Whether to preserve YAML comments internally. This only works for objects embedding metav1.ObjectMeta.
// Only applicable to ContentTypeYAML framers.
// Using any other framer will be silently ignored. Usage of this option also requires setting
// the PreserveComments in DecodingOptions, too. (Default: false)
// TODO: Make this a BestEffort & Strict mode
PreserveComments *bool
// TODO: Maybe consider an option to always convert to the preferred version (not just internal)
}
type EncodingOptionsFunc func(*EncodingOptions)
func WithPrettyEncode(pretty bool) EncodingOptionsFunc {
return func(opts *EncodingOptions) {
opts.Pretty = &pretty
}
}
func WithCommentsEncode(comments bool) EncodingOptionsFunc {
return func(opts *EncodingOptions) {
opts.PreserveComments = &comments
}
}
func WithEncodingOptions(newOpts EncodingOptions) EncodingOptionsFunc {
return func(opts *EncodingOptions) {
// TODO: Null-check all of these before using them
*opts = newOpts
}
}
func defaultEncodeOpts() *EncodingOptions {
return &EncodingOptions{
Pretty: util.BoolPtr(true),
PreserveComments: util.BoolPtr(false),
}
}
func newEncodeOpts(fns ...EncodingOptionsFunc) *EncodingOptions {
opts := defaultEncodeOpts()
for _, fn := range fns {
fn(opts)
}
return opts
}
type encoder struct {
*schemeAndCodec
opts EncodingOptions
}
func newEncoder(schemeAndCodec *schemeAndCodec, opts EncodingOptions) Encoder {
return &encoder{
schemeAndCodec,
opts,
}
}
// Encode encodes the given objects and writes them to the specified FrameWriter.
// The FrameWriter specifies the ContentType. This encoder will automatically convert any
// internal object given to the preferred external groupversion. No conversion will happen
// if the given object is of an external version.
// TODO: This should automatically convert to the preferred version
func (e *encoder) Encode(fw FrameWriter, objs ...runtime.Object) error {
for _, obj := range objs {
// Get the kind for the given object
gvk, err := GVKForObject(e.scheme, obj)
if err != nil {
return err
}
// If the object is internal, convert it to the preferred external one
if gvk.Version == runtime.APIVersionInternal {
gv, err := prioritizedVersionForGroup(e.scheme, gvk.Group)
if err != nil {
return err
}
gvk.Version = gv.Version
}
// Encode it
if err := e.EncodeForGroupVersion(fw, obj, gvk.GroupVersion()); err != nil {
return err
}
}
return nil
}
// EncodeForGroupVersion encodes the given object for the specific groupversion. If the object
// is not of that version currently it will try to convert. The output bytes are written to the
// FrameWriter. The FrameWriter specifies the ContentType.
func (e *encoder) EncodeForGroupVersion(fw FrameWriter, obj runtime.Object, gv schema.GroupVersion) error {
// Get the serializer for the media type
serializerInfo, ok := runtime.SerializerInfoForMediaType(e.codecs.SupportedMediaTypes(), string(fw.ContentType()))
if !ok {
return ErrUnsupportedContentType
}
// Choose the pretty or non-pretty one
encoder := serializerInfo.Serializer
// Use the pretty serializer if it was asked for and is defined for the content type
if *e.opts.Pretty {
// Apparently not all SerializerInfos have this field defined (e.g. YAML)
// TODO: This could be considered a bug in upstream, create an issue
if serializerInfo.PrettySerializer != nil {
encoder = serializerInfo.PrettySerializer
} else {
logrus.Debugf("PrettySerializer for ContentType %s is nil, falling back to Serializer.", fw.ContentType())
}
}
// Get a version-specific encoder for the specified groupversion
versionEncoder := encoderForVersion(e.scheme, encoder, gv)
// Cast the object to a metav1.Object to get access to annotations
metaobj, ok := toMetaObject(obj)
// For objects without ObjectMeta, the cast will fail. Allow that failure and do "normal" encoding
if !ok {
return versionEncoder.Encode(obj, fw)
}
// Specialize the encoder for a specific gv and encode the object
return e.encodeWithCommentSupport(versionEncoder, fw, obj, metaobj)
}
// encoderForVersion is used instead of CodecFactory.EncoderForVersion, as we want to use our own converter
func encoderForVersion(scheme *runtime.Scheme, encoder runtime.Encoder, gv schema.GroupVersion) runtime.Encoder {
return newConversionCodecForScheme(
scheme,
encoder, // what content-type encoder to use
nil, // no decoder
gv, // specify what the target encode groupversion is
nil, // no target decode groupversion
false, // no defaulting
true, // convert if needed before encode
)
}