OLD | NEW |
1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
4 | 4 |
5 // Package textproto implements a textproto config service Resolver. | 5 // Package textproto implements a textproto config service Resolver. |
6 // | 6 // |
7 // It uses the "luci-go" text protobuf multi-line extension. For more | 7 // It uses the "luci-go" text protobuf multi-line extension. For more |
8 // information, see: github.com/luci/luci-go/common/proto | 8 // information, see: github.com/luci/luci-go/common/proto |
9 // | 9 // |
10 // The textproto protobuf Resolver internally formats its content as a binary | 10 // The textproto protobuf Resolver internally formats its content as a binary |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 // var out []*MyProtoMessages | 74 // var out []*MyProtoMessages |
75 // TextProtoSlice(&out) | 75 // TextProtoSlice(&out) |
76 func Slice(out interface{}) interface { | 76 func Slice(out interface{}) interface { |
77 cfgclient.MultiResolver | 77 cfgclient.MultiResolver |
78 cfgclient.FormattingResolver | 78 cfgclient.FormattingResolver |
79 } { | 79 } { |
80 r := resolver{} | 80 r := resolver{} |
81 | 81 |
82 v := reflect.ValueOf(out) | 82 v := reflect.ValueOf(out) |
83 if !r.loadMulti(v) { | 83 if !r.loadMulti(v) { |
84 » » panic(errors.Reason("%(type)s is not a pointer to a slice of pro
tobuf message types").D("type", v.Type()).Err()) | 84 » » panic(errors.Reason("%s is not a pointer to a slice of protobuf
message types", v.Type()).Err()) |
85 } | 85 } |
86 return &r | 86 return &r |
87 } | 87 } |
88 | 88 |
89 type resolver struct { | 89 type resolver struct { |
90 messageName string | 90 messageName string |
91 | 91 |
92 single proto.Message | 92 single proto.Message |
93 singleType reflect.Type | 93 singleType reflect.Type |
94 | 94 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 } | 137 } |
138 r.multiDest.Index(i).Set(msgV) | 138 r.multiDest.Index(i).Set(msgV) |
139 return nil | 139 return nil |
140 } | 140 } |
141 | 141 |
142 func (r *resolver) resolveItem(out proto.Message, content string, format string)
error { | 142 func (r *resolver) resolveItem(out proto.Message, content string, format string)
error { |
143 switch format { | 143 switch format { |
144 case "": | 144 case "": |
145 // Not formatted (text protobuf). | 145 // Not formatted (text protobuf). |
146 if err := luciProto.UnmarshalTextML(content, out); err != nil { | 146 if err := luciProto.UnmarshalTextML(content, out); err != nil { |
147 » » » return errors.Annotate(err).Reason("failed to unmarshal
text protobuf").Err() | 147 » » » return errors.Annotate(err, "failed to unmarshal text pr
otobuf").Err() |
148 } | 148 } |
149 return nil | 149 return nil |
150 | 150 |
151 case BinaryFormat: | 151 case BinaryFormat: |
152 if err := parseBinaryContent(content, out); err != nil { | 152 if err := parseBinaryContent(content, out); err != nil { |
153 » » » return errors.Annotate(err).Reason("failed to unmarshal
binary protobuf").Err() | 153 » » » return errors.Annotate(err, "failed to unmarshal binary
protobuf").Err() |
154 } | 154 } |
155 return nil | 155 return nil |
156 | 156 |
157 default: | 157 default: |
158 » » return errors.Reason("unsupported content format: %(format)q").D
("format", format).Err() | 158 » » return errors.Reason("unsupported content format: %q", format).E
rr() |
159 } | 159 } |
160 } | 160 } |
161 | 161 |
162 func (r *resolver) Format() backend.FormatSpec { | 162 func (r *resolver) Format() backend.FormatSpec { |
163 return backend.FormatSpec{BinaryFormat, r.messageName} | 163 return backend.FormatSpec{BinaryFormat, r.messageName} |
164 } | 164 } |
165 | 165 |
166 // Formatter is a cfgclient.Formatter implementation bound to a specific | 166 // Formatter is a cfgclient.Formatter implementation bound to a specific |
167 // protobuf message. | 167 // protobuf message. |
168 // | 168 // |
169 // It takes a text protobuf representation of that message as input and returns | 169 // It takes a text protobuf representation of that message as input and returns |
170 // a binary protobuf representation as output. | 170 // a binary protobuf representation as output. |
171 type Formatter struct{} | 171 type Formatter struct{} |
172 | 172 |
173 // FormatItem implements cfgclient.Formatter. | 173 // FormatItem implements cfgclient.Formatter. |
174 func (f *Formatter) FormatItem(c, fd string) (string, error) { | 174 func (f *Formatter) FormatItem(c, fd string) (string, error) { |
175 archetype := proto.MessageType(fd) | 175 archetype := proto.MessageType(fd) |
176 if archetype == nil { | 176 if archetype == nil { |
177 » » return "", errors.Reason("unknown proto.Message type %(type)q in
formatter data"). | 177 » » return "", errors.Reason("unknown proto.Message type %q in forma
tter data", fd).Err() |
178 » » » D("type", fd).Err() | |
179 } | 178 } |
180 msg := archetypeMessage(archetype) | 179 msg := archetypeMessage(archetype) |
181 | 180 |
182 // Convert from config to protobuf. | 181 // Convert from config to protobuf. |
183 if err := luciProto.UnmarshalTextML(c, msg); err != nil { | 182 if err := luciProto.UnmarshalTextML(c, msg); err != nil { |
184 » » return "", errors.Annotate(err).Reason("failed to unmarshal text
protobuf content").Err() | 183 » » return "", errors.Annotate(err, "failed to unmarshal text protob
uf content").Err() |
185 } | 184 } |
186 | 185 |
187 // Binary format. | 186 // Binary format. |
188 bc, err := makeBinaryContent(msg) | 187 bc, err := makeBinaryContent(msg) |
189 if err != nil { | 188 if err != nil { |
190 » » return "", errors.Annotate(err).Err() | 189 » » return "", errors.Annotate(err, "").Err() |
191 } | 190 } |
192 return bc, nil | 191 return bc, nil |
193 } | 192 } |
194 | 193 |
195 // t is a pointer to a proto.Message instance. | 194 // t is a pointer to a proto.Message instance. |
196 func archetypeInstance(t reflect.Type) reflect.Value { | 195 func archetypeInstance(t reflect.Type) reflect.Value { |
197 return reflect.New(t.Elem()) | 196 return reflect.New(t.Elem()) |
198 } | 197 } |
199 | 198 |
200 func archetypeMessage(t reflect.Type) proto.Message { | 199 func archetypeMessage(t reflect.Type) proto.Message { |
201 return archetypeInstance(t).Interface().(proto.Message) | 200 return archetypeInstance(t).Interface().(proto.Message) |
202 } | 201 } |
203 | 202 |
204 // makeBinaryContent constructs a binary content string from text protobuf | 203 // makeBinaryContent constructs a binary content string from text protobuf |
205 // content. | 204 // content. |
206 // | 205 // |
207 // The binary content is formatted by concatenating two "cmpbin" binary values | 206 // The binary content is formatted by concatenating two "cmpbin" binary values |
208 // together: | 207 // together: |
209 // [Message Name] | [Marshaled Message Data] | 208 // [Message Name] | [Marshaled Message Data] |
210 func makeBinaryContent(msg proto.Message) (string, error) { | 209 func makeBinaryContent(msg proto.Message) (string, error) { |
211 d, err := proto.Marshal(msg) | 210 d, err := proto.Marshal(msg) |
212 if err != nil { | 211 if err != nil { |
213 » » return "", errors.Annotate(err).Reason("failed to marshal messag
e").Err() | 212 » » return "", errors.Annotate(err, "failed to marshal message").Err
() |
214 } | 213 } |
215 | 214 |
216 var buf bytes.Buffer | 215 var buf bytes.Buffer |
217 if _, err := cmpbin.WriteString(&buf, proto.MessageName(msg)); err != ni
l { | 216 if _, err := cmpbin.WriteString(&buf, proto.MessageName(msg)); err != ni
l { |
218 » » return "", errors.Annotate(err).Reason("failed to write message
name").Err() | 217 » » return "", errors.Annotate(err, "failed to write message name").
Err() |
219 } | 218 } |
220 if _, err := cmpbin.WriteBytes(&buf, d); err != nil { | 219 if _, err := cmpbin.WriteBytes(&buf, d); err != nil { |
221 » » return "", errors.Annotate(err).Reason("failed to write binary m
essage content").Err() | 220 » » return "", errors.Annotate(err, "failed to write binary message
content").Err() |
222 } | 221 } |
223 return buf.String(), nil | 222 return buf.String(), nil |
224 } | 223 } |
225 | 224 |
226 // parseBinaryContent parses a binary content string, pulling out the message | 225 // parseBinaryContent parses a binary content string, pulling out the message |
227 // type and marshalled message data. It then unmarshals the specified type into | 226 // type and marshalled message data. It then unmarshals the specified type into |
228 // a new message based on the archetype. | 227 // a new message based on the archetype. |
229 // | 228 // |
230 // If the binary content's declared type doesn't match the archetype, or if the | 229 // If the binary content's declared type doesn't match the archetype, or if the |
231 // binary content is invalid, an error will be returned. | 230 // binary content is invalid, an error will be returned. |
232 func parseBinaryContent(v string, msg proto.Message) error { | 231 func parseBinaryContent(v string, msg proto.Message) error { |
233 r := strings.NewReader(v) | 232 r := strings.NewReader(v) |
234 encName, _, err := cmpbin.ReadString(r) | 233 encName, _, err := cmpbin.ReadString(r) |
235 if err != nil { | 234 if err != nil { |
236 » » return errors.Annotate(err).Reason("failed to read message name"
).Err() | 235 » » return errors.Annotate(err, "failed to read message name").Err() |
237 } | 236 } |
238 | 237 |
239 // Construct a message for this. | 238 // Construct a message for this. |
240 if name := proto.MessageName(msg); name != encName { | 239 if name := proto.MessageName(msg); name != encName { |
241 » » return errors.Reason("message name %(name)q doesn't match encode
d name %(enc)q"). | 240 » » return errors.Reason("message name %q doesn't match encoded name
%q", name, encName).Err() |
242 » » » D("name", name).D("enc", encName).Err() | |
243 } | 241 } |
244 | 242 |
245 // We have the right message, unmarshal. | 243 // We have the right message, unmarshal. |
246 d, _, err := cmpbin.ReadBytes(r) | 244 d, _, err := cmpbin.ReadBytes(r) |
247 if err != nil { | 245 if err != nil { |
248 » » return errors.Annotate(err).Reason("failed to read binary messag
e content").Err() | 246 » » return errors.Annotate(err, "failed to read binary message conte
nt").Err() |
249 } | 247 } |
250 if err := proto.Unmarshal(d, msg); err != nil { | 248 if err := proto.Unmarshal(d, msg); err != nil { |
251 » » return errors.Annotate(err).Reason("failed to unmarshal message"
).Err() | 249 » » return errors.Annotate(err, "failed to unmarshal message").Err() |
252 } | 250 } |
253 return nil | 251 return nil |
254 } | 252 } |
OLD | NEW |