| 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 |