api, metadata: use map type for Container.Extensions

To allow for updating extensions without collisions, we have moved to
using a map type that can be explicitly selected via the field path for
updates. This ensures that multiple parties can operate on their
extensions without stepping on each other's toes or incurring an
inordinate number of round trips.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
Stephen J Day 2017-09-08 14:51:30 -07:00
parent 3552ce5688
commit f7d31e2be4
No known key found for this signature in database
GPG Key ID: 67B3DED84EDC823F
9 changed files with 349 additions and 123 deletions

View File

@ -190,7 +190,7 @@ file {
number: 10 number: 10
label: LABEL_REPEATED label: LABEL_REPEATED
type: TYPE_MESSAGE type: TYPE_MESSAGE
type_name: ".google.protobuf.Any" type_name: ".containerd.services.containers.v1.Container.ExtensionsEntry"
options { options {
65001: 0 65001: 0
} }
@ -234,6 +234,27 @@ file {
json_name: "options" json_name: "options"
} }
} }
nested_type {
name: "ExtensionsEntry"
field {
name: "key"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "key"
}
field {
name: "value"
number: 2
label: LABEL_OPTIONAL
type: TYPE_MESSAGE
type_name: ".google.protobuf.Any"
json_name: "value"
}
options {
map_entry: true
}
}
} }
message_type { message_type {
name: "GetContainerRequest" name: "GetContainerRequest"

View File

@ -98,7 +98,12 @@ type Container struct {
// associated with the container. One may provide protobuf, json, or other // associated with the container. One may provide protobuf, json, or other
// encoding formats. The primary use of this is to further decorate the // encoding formats. The primary use of this is to further decorate the
// container object with fields that may be specific to a client integration. // container object with fields that may be specific to a client integration.
Extensions []google_protobuf1.Any `protobuf:"bytes,10,rep,name=extensions" json:"extensions"` //
// The key portion of this map should identify a "name" for the extension
// that should be unique against other extensions. When updating extension
// data, one should only update the specified extension using field paths
// to select a specific map key.
Extensions map[string]google_protobuf1.Any `protobuf:"bytes,10,rep,name=extensions" json:"extensions" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
} }
func (m *Container) Reset() { *m = Container{} } func (m *Container) Reset() { *m = Container{} }
@ -524,15 +529,29 @@ func (m *Container) MarshalTo(dAtA []byte) (int, error) {
} }
i += n4 i += n4
if len(m.Extensions) > 0 { if len(m.Extensions) > 0 {
for _, msg := range m.Extensions { for k, _ := range m.Extensions {
dAtA[i] = 0x52 dAtA[i] = 0x52
i++ i++
i = encodeVarintContainers(dAtA, i, uint64(msg.Size())) v := m.Extensions[k]
n, err := msg.MarshalTo(dAtA[i:]) msgSize := 0
if (&v) != nil {
msgSize = (&v).Size()
msgSize += 1 + sovContainers(uint64(msgSize))
}
mapSize := 1 + len(k) + sovContainers(uint64(len(k))) + msgSize
i = encodeVarintContainers(dAtA, i, uint64(mapSize))
dAtA[i] = 0xa
i++
i = encodeVarintContainers(dAtA, i, uint64(len(k)))
i += copy(dAtA[i:], k)
dAtA[i] = 0x12
i++
i = encodeVarintContainers(dAtA, i, uint64((&v).Size()))
n5, err := (&v).MarshalTo(dAtA[i:])
if err != nil { if err != nil {
return 0, err return 0, err
} }
i += n i += n5
} }
} }
return i, nil return i, nil
@ -563,11 +582,11 @@ func (m *Container_Runtime) MarshalTo(dAtA []byte) (int, error) {
dAtA[i] = 0x12 dAtA[i] = 0x12
i++ i++
i = encodeVarintContainers(dAtA, i, uint64(m.Options.Size())) i = encodeVarintContainers(dAtA, i, uint64(m.Options.Size()))
n5, err := m.Options.MarshalTo(dAtA[i:]) n6, err := m.Options.MarshalTo(dAtA[i:])
if err != nil { if err != nil {
return 0, err return 0, err
} }
i += n5 i += n6
} }
return i, nil return i, nil
} }
@ -614,11 +633,11 @@ func (m *GetContainerResponse) MarshalTo(dAtA []byte) (int, error) {
dAtA[i] = 0xa dAtA[i] = 0xa
i++ i++
i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size())) i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size()))
n6, err := m.Container.MarshalTo(dAtA[i:]) n7, err := m.Container.MarshalTo(dAtA[i:])
if err != nil { if err != nil {
return 0, err return 0, err
} }
i += n6 i += n7
return i, nil return i, nil
} }
@ -703,11 +722,11 @@ func (m *CreateContainerRequest) MarshalTo(dAtA []byte) (int, error) {
dAtA[i] = 0xa dAtA[i] = 0xa
i++ i++
i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size())) i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size()))
n7, err := m.Container.MarshalTo(dAtA[i:]) n8, err := m.Container.MarshalTo(dAtA[i:])
if err != nil { if err != nil {
return 0, err return 0, err
} }
i += n7 i += n8
return i, nil return i, nil
} }
@ -729,11 +748,11 @@ func (m *CreateContainerResponse) MarshalTo(dAtA []byte) (int, error) {
dAtA[i] = 0xa dAtA[i] = 0xa
i++ i++
i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size())) i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size()))
n8, err := m.Container.MarshalTo(dAtA[i:]) n9, err := m.Container.MarshalTo(dAtA[i:])
if err != nil { if err != nil {
return 0, err return 0, err
} }
i += n8 i += n9
return i, nil return i, nil
} }
@ -755,20 +774,20 @@ func (m *UpdateContainerRequest) MarshalTo(dAtA []byte) (int, error) {
dAtA[i] = 0xa dAtA[i] = 0xa
i++ i++
i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size())) i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size()))
n9, err := m.Container.MarshalTo(dAtA[i:]) n10, err := m.Container.MarshalTo(dAtA[i:])
if err != nil { if err != nil {
return 0, err return 0, err
} }
i += n9 i += n10
if m.UpdateMask != nil { if m.UpdateMask != nil {
dAtA[i] = 0x12 dAtA[i] = 0x12
i++ i++
i = encodeVarintContainers(dAtA, i, uint64(m.UpdateMask.Size())) i = encodeVarintContainers(dAtA, i, uint64(m.UpdateMask.Size()))
n10, err := m.UpdateMask.MarshalTo(dAtA[i:]) n11, err := m.UpdateMask.MarshalTo(dAtA[i:])
if err != nil { if err != nil {
return 0, err return 0, err
} }
i += n10 i += n11
} }
return i, nil return i, nil
} }
@ -791,11 +810,11 @@ func (m *UpdateContainerResponse) MarshalTo(dAtA []byte) (int, error) {
dAtA[i] = 0xa dAtA[i] = 0xa
i++ i++
i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size())) i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size()))
n11, err := m.Container.MarshalTo(dAtA[i:]) n12, err := m.Container.MarshalTo(dAtA[i:])
if err != nil { if err != nil {
return 0, err return 0, err
} }
i += n11 i += n12
return i, nil return i, nil
} }
@ -890,9 +909,12 @@ func (m *Container) Size() (n int) {
l = github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt) l = github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt)
n += 1 + l + sovContainers(uint64(l)) n += 1 + l + sovContainers(uint64(l))
if len(m.Extensions) > 0 { if len(m.Extensions) > 0 {
for _, e := range m.Extensions { for k, v := range m.Extensions {
l = e.Size() _ = k
n += 1 + l + sovContainers(uint64(l)) _ = v
l = v.Size()
mapEntrySize := 1 + len(k) + sovContainers(uint64(len(k))) + 1 + l + sovContainers(uint64(l))
n += mapEntrySize + 1 + sovContainers(uint64(mapEntrySize))
} }
} }
return n return n
@ -1027,6 +1049,16 @@ func (this *Container) String() string {
mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k])
} }
mapStringForLabels += "}" mapStringForLabels += "}"
keysForExtensions := make([]string, 0, len(this.Extensions))
for k, _ := range this.Extensions {
keysForExtensions = append(keysForExtensions, k)
}
github_com_gogo_protobuf_sortkeys.Strings(keysForExtensions)
mapStringForExtensions := "map[string]google_protobuf1.Any{"
for _, k := range keysForExtensions {
mapStringForExtensions += fmt.Sprintf("%v: %v,", k, this.Extensions[k])
}
mapStringForExtensions += "}"
s := strings.Join([]string{`&Container{`, s := strings.Join([]string{`&Container{`,
`ID:` + fmt.Sprintf("%v", this.ID) + `,`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`,
`Labels:` + mapStringForLabels + `,`, `Labels:` + mapStringForLabels + `,`,
@ -1037,7 +1069,7 @@ func (this *Container) String() string {
`SnapshotKey:` + fmt.Sprintf("%v", this.SnapshotKey) + `,`, `SnapshotKey:` + fmt.Sprintf("%v", this.SnapshotKey) + `,`,
`CreatedAt:` + strings.Replace(strings.Replace(this.CreatedAt.String(), "Timestamp", "google_protobuf4.Timestamp", 1), `&`, ``, 1) + `,`, `CreatedAt:` + strings.Replace(strings.Replace(this.CreatedAt.String(), "Timestamp", "google_protobuf4.Timestamp", 1), `&`, ``, 1) + `,`,
`UpdatedAt:` + strings.Replace(strings.Replace(this.UpdatedAt.String(), "Timestamp", "google_protobuf4.Timestamp", 1), `&`, ``, 1) + `,`, `UpdatedAt:` + strings.Replace(strings.Replace(this.UpdatedAt.String(), "Timestamp", "google_protobuf4.Timestamp", 1), `&`, ``, 1) + `,`,
`Extensions:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Extensions), "Any", "google_protobuf1.Any", 1), `&`, ``, 1) + `,`, `Extensions:` + mapStringForExtensions + `,`,
`}`, `}`,
}, "") }, "")
return s return s
@ -1565,9 +1597,99 @@ func (m *Container) Unmarshal(dAtA []byte) error {
if postIndex > l { if postIndex > l {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
m.Extensions = append(m.Extensions, google_protobuf1.Any{}) var keykey uint64
if err := m.Extensions[len(m.Extensions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { for shift := uint(0); ; shift += 7 {
return err if shift >= 64 {
return ErrIntOverflowContainers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
keykey |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
var stringLenmapkey uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowContainers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLenmapkey |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLenmapkey := int(stringLenmapkey)
if intStringLenmapkey < 0 {
return ErrInvalidLengthContainers
}
postStringIndexmapkey := iNdEx + intStringLenmapkey
if postStringIndexmapkey > l {
return io.ErrUnexpectedEOF
}
mapkey := string(dAtA[iNdEx:postStringIndexmapkey])
iNdEx = postStringIndexmapkey
if m.Extensions == nil {
m.Extensions = make(map[string]google_protobuf1.Any)
}
if iNdEx < postIndex {
var valuekey uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowContainers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
valuekey |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
var mapmsglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowContainers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
mapmsglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if mapmsglen < 0 {
return ErrInvalidLengthContainers
}
postmsgIndex := iNdEx + mapmsglen
if mapmsglen < 0 {
return ErrInvalidLengthContainers
}
if postmsgIndex > l {
return io.ErrUnexpectedEOF
}
mapvalue := &google_protobuf1.Any{}
if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil {
return err
}
iNdEx = postmsgIndex
m.Extensions[mapkey] = *mapvalue
} else {
var mapvalue google_protobuf1.Any
m.Extensions[mapkey] = mapvalue
} }
iNdEx = postIndex iNdEx = postIndex
default: default:
@ -2564,52 +2686,54 @@ func init() {
} }
var fileDescriptorContainers = []byte{ var fileDescriptorContainers = []byte{
// 752 bytes of a gzipped FileDescriptorProto // 776 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4d, 0x73, 0xd2, 0x5c, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcd, 0x72, 0x12, 0x5b,
0x14, 0x6e, 0x02, 0x85, 0x72, 0x78, 0x17, 0xef, 0x5c, 0x11, 0x63, 0x9c, 0x01, 0xca, 0x8a, 0x85, 0x10, 0xce, 0x00, 0x81, 0xd0, 0xdc, 0xaa, 0x7b, 0xeb, 0x5c, 0x2e, 0x77, 0x1c, 0xab, 0x80, 0xb0,
0x06, 0x8b, 0x8e, 0xf6, 0x63, 0x55, 0xfa, 0x35, 0x8e, 0xad, 0xd3, 0xc9, 0xe8, 0x46, 0x17, 0x35, 0xa2, 0x2c, 0x1d, 0x0c, 0x5a, 0x9a, 0x1f, 0x37, 0x21, 0x7f, 0x65, 0x99, 0x58, 0xa9, 0x51, 0x37,
0xc0, 0x29, 0x8d, 0xe4, 0xcb, 0xdc, 0x0b, 0x23, 0xe3, 0x42, 0x7f, 0x82, 0x6b, 0xff, 0x80, 0x7f, 0xba, 0x88, 0x03, 0x74, 0xc8, 0xc8, 0xfc, 0x39, 0xe7, 0x40, 0x49, 0xb9, 0xd0, 0x47, 0x70, 0xe7,
0xa5, 0x4b, 0x97, 0xae, 0x6a, 0xcb, 0x2f, 0x71, 0x72, 0x93, 0x34, 0x29, 0xa4, 0x0a, 0x55, 0x76, 0x23, 0xf8, 0x2a, 0x59, 0xba, 0x74, 0x15, 0x13, 0x9e, 0xc4, 0x9a, 0x33, 0x33, 0xcc, 0x04, 0x06,
0xe7, 0xe4, 0x9e, 0xe7, 0x9c, 0xe7, 0x9e, 0xf3, 0x9c, 0x24, 0xb0, 0xdf, 0xd5, 0xd9, 0x49, 0xbf, 0x85, 0x68, 0x76, 0xa7, 0x39, 0xfd, 0x7d, 0xfd, 0xf1, 0x75, 0xf7, 0x01, 0xd8, 0xef, 0x68, 0xec,
0xa5, 0xb4, 0x6d, 0xb3, 0xde, 0xb6, 0x2d, 0xa6, 0xe9, 0x16, 0xba, 0x9d, 0xb8, 0xa9, 0x39, 0x7a, 0xa4, 0xd7, 0x94, 0x5b, 0x96, 0x51, 0x6b, 0x59, 0x26, 0x53, 0x35, 0x13, 0x9d, 0x76, 0xf4, 0xa8,
0x9d, 0xa2, 0x3b, 0xd0, 0xdb, 0x48, 0xa3, 0xe7, 0xb4, 0x3e, 0x58, 0x89, 0x79, 0x8a, 0xe3, 0xda, 0xda, 0x5a, 0x8d, 0xa2, 0xd3, 0xd7, 0x5a, 0x48, 0xc3, 0xcf, 0x69, 0xad, 0xbf, 0x12, 0x89, 0x64,
0xcc, 0x26, 0xcb, 0x11, 0x4e, 0x09, 0x31, 0x4a, 0x2c, 0x6a, 0xb0, 0x22, 0x17, 0xba, 0x76, 0xd7, 0xdb, 0xb1, 0x98, 0x45, 0x96, 0x43, 0x9c, 0x1c, 0x60, 0xe4, 0x48, 0x56, 0x7f, 0x45, 0xca, 0x77,
0xe6, 0xd1, 0x75, 0xcf, 0xf2, 0x81, 0xf2, 0xdd, 0xae, 0x6d, 0x77, 0x0d, 0xac, 0x73, 0xaf, 0xd5, 0xac, 0x8e, 0xc5, 0xb3, 0x6b, 0xee, 0xc9, 0x03, 0x4a, 0x37, 0x3a, 0x96, 0xd5, 0xd1, 0xb1, 0xc6,
0x3f, 0xae, 0x6b, 0xd6, 0x30, 0x38, 0xba, 0x37, 0x7e, 0x84, 0xa6, 0xc3, 0xc2, 0xc3, 0xca, 0xf8, 0xa3, 0x66, 0xef, 0xb8, 0xa6, 0x9a, 0x03, 0xff, 0xea, 0xe6, 0xf8, 0x15, 0x1a, 0x36, 0x0b, 0x2e,
0xe1, 0xb1, 0x8e, 0x46, 0xe7, 0xc8, 0xd4, 0x68, 0x2f, 0x88, 0x28, 0x8f, 0x47, 0x30, 0xdd, 0x44, 0xcb, 0xe3, 0x97, 0xc7, 0x1a, 0xea, 0xed, 0x23, 0x43, 0xa5, 0x5d, 0x3f, 0xa3, 0x34, 0x9e, 0xc1,
0xca, 0x34, 0xd3, 0xf1, 0x03, 0xaa, 0x5f, 0x17, 0x21, 0xb7, 0x15, 0x52, 0x24, 0x45, 0x10, 0xf5, 0x34, 0x03, 0x29, 0x53, 0x0d, 0xdb, 0x4b, 0xa8, 0x7c, 0x4e, 0x43, 0x76, 0x2b, 0x90, 0x48, 0x0a,
0x8e, 0x24, 0x54, 0x84, 0x5a, 0xae, 0x99, 0x19, 0x9d, 0x95, 0xc5, 0x67, 0xdb, 0xaa, 0xa8, 0x77, 0x90, 0xd0, 0xda, 0xa2, 0x50, 0x16, 0xaa, 0xd9, 0x46, 0x7a, 0x78, 0x56, 0x4a, 0x3c, 0xde, 0x56,
0xc8, 0x21, 0x64, 0x0c, 0xad, 0x85, 0x06, 0x95, 0xc4, 0x4a, 0xaa, 0x96, 0x6f, 0xac, 0x2a, 0x7f, 0x12, 0x5a, 0x9b, 0x1c, 0x42, 0x5a, 0x57, 0x9b, 0xa8, 0x53, 0x31, 0x51, 0x4e, 0x56, 0x73, 0xf5,
0xbc, 0xaa, 0x72, 0x99, 0x55, 0xd9, 0xe7, 0xd0, 0x1d, 0x8b, 0xb9, 0x43, 0x35, 0xc8, 0x43, 0x0a, 0x55, 0xf9, 0x97, 0x5f, 0x55, 0x1e, 0xb1, 0xca, 0xfb, 0x1c, 0xba, 0x63, 0x32, 0x67, 0xa0, 0xf8,
0xb0, 0xa8, 0x9b, 0x5a, 0x17, 0xa5, 0x94, 0x57, 0x4c, 0xf5, 0x1d, 0xf2, 0x02, 0xb2, 0x6e, 0xdf, 0x3c, 0x24, 0x0f, 0x8b, 0x9a, 0xa1, 0x76, 0x50, 0x4c, 0xba, 0xc5, 0x14, 0x2f, 0x20, 0x4f, 0x21,
0xf2, 0x38, 0x4a, 0xe9, 0x8a, 0x50, 0xcb, 0x37, 0x1e, 0xcf, 0x54, 0x48, 0xf5, 0xb1, 0x6a, 0x98, 0xe3, 0xf4, 0x4c, 0x57, 0xa3, 0x98, 0x2a, 0x0b, 0xd5, 0x5c, 0xfd, 0xfe, 0x5c, 0x85, 0x14, 0x0f,
0x84, 0xd4, 0x20, 0x4d, 0x1d, 0x6c, 0x4b, 0x8b, 0x3c, 0x59, 0x41, 0xf1, 0xbb, 0xa1, 0x84, 0xdd, 0xab, 0x04, 0x24, 0xa4, 0x0a, 0x29, 0x6a, 0x63, 0x4b, 0x5c, 0xe4, 0x64, 0x79, 0xd9, 0x73, 0x43,
0x50, 0x36, 0xad, 0xa1, 0xca, 0x23, 0x48, 0x05, 0xf2, 0xd4, 0xd2, 0x1c, 0x7a, 0x62, 0x33, 0x86, 0x0e, 0xdc, 0x90, 0x37, 0xcd, 0x81, 0xc2, 0x33, 0x48, 0x19, 0x72, 0xd4, 0x54, 0x6d, 0x7a, 0x62,
0xae, 0x94, 0xe1, 0xac, 0xe2, 0x8f, 0xc8, 0x32, 0xfc, 0x17, 0xba, 0x47, 0x3d, 0x1c, 0x4a, 0xd9, 0x31, 0x86, 0x8e, 0x98, 0xe6, 0xaa, 0xa2, 0x1f, 0x91, 0x65, 0xf8, 0x2b, 0x08, 0x8f, 0xba, 0x38,
0xab, 0x21, 0xcf, 0x71, 0x48, 0xb6, 0x00, 0xda, 0x2e, 0x6a, 0x0c, 0x3b, 0x47, 0x1a, 0x93, 0x96, 0x10, 0x33, 0x97, 0x53, 0x9e, 0xe0, 0x80, 0x6c, 0x01, 0xb4, 0x1c, 0x54, 0x19, 0xb6, 0x8f, 0x54,
0x78, 0x51, 0x79, 0xa2, 0xe8, 0xcb, 0x70, 0x04, 0xcd, 0xa5, 0xd3, 0xb3, 0xf2, 0xc2, 0x97, 0x9f, 0x26, 0x2e, 0xf1, 0xa2, 0xd2, 0x44, 0xd1, 0xe7, 0x41, 0x0b, 0x1a, 0x4b, 0xa7, 0x67, 0xa5, 0x85,
0x65, 0x41, 0xcd, 0x05, 0xb8, 0x4d, 0xe6, 0x25, 0xe9, 0x3b, 0x9d, 0x30, 0x49, 0x6e, 0x96, 0x24, 0x4f, 0xdf, 0x4b, 0x82, 0x92, 0xf5, 0x71, 0x9b, 0xcc, 0x25, 0xe9, 0xd9, 0xed, 0x80, 0x24, 0x3b,
0x01, 0x6e, 0x93, 0x91, 0x75, 0x00, 0xfc, 0xc0, 0xd0, 0xa2, 0xba, 0x6d, 0x51, 0x09, 0xf8, 0xd0, 0x0f, 0x89, 0x8f, 0xdb, 0x64, 0xa4, 0x09, 0x80, 0xef, 0x18, 0x9a, 0x54, 0xb3, 0x4c, 0x2a, 0x02,
0x12, 0xaf, 0xdf, 0x4c, 0x7b, 0x70, 0x35, 0x16, 0x2d, 0xaf, 0x41, 0x3e, 0x36, 0x31, 0xf2, 0x3f, 0x6f, 0xda, 0xa3, 0xb9, 0xbc, 0xdc, 0x19, 0xc1, 0x79, 0xe3, 0x1a, 0x29, 0xb7, 0x8c, 0x12, 0x61,
0xa4, 0xbc, 0xeb, 0x72, 0x51, 0xa8, 0x9e, 0xe9, 0xcd, 0x6e, 0xa0, 0x19, 0x7d, 0x94, 0x44, 0x7f, 0x95, 0xd6, 0x20, 0x17, 0xe9, 0x2c, 0xf9, 0x07, 0x92, 0xae, 0x2d, 0x7c, 0x78, 0x14, 0xf7, 0xe8,
0x76, 0xdc, 0x59, 0x17, 0x57, 0x05, 0xf9, 0x00, 0xb2, 0xc1, 0x0c, 0x08, 0x81, 0xb4, 0xa5, 0x99, 0xf6, 0xb8, 0xaf, 0xea, 0x3d, 0x14, 0x13, 0x5e, 0x8f, 0x79, 0xb0, 0x9e, 0x58, 0x15, 0xa4, 0x03,
0x18, 0xe0, 0xb8, 0x4d, 0x14, 0xc8, 0xda, 0x0e, 0xe3, 0x94, 0xc4, 0xdf, 0x4c, 0x24, 0x0c, 0xaa, 0xc8, 0xf8, 0xbd, 0x22, 0x04, 0x52, 0xa6, 0x6a, 0xa0, 0x8f, 0xe3, 0x67, 0x22, 0x43, 0xc6, 0xb2,
0x3e, 0x80, 0x5b, 0x7b, 0xc8, 0x2e, 0xe7, 0xab, 0xe2, 0xfb, 0x3e, 0x52, 0x76, 0x9d, 0x4a, 0xab, 0x19, 0x97, 0x9e, 0xf8, 0x49, 0xe7, 0x82, 0x24, 0xe9, 0x19, 0xfc, 0x3d, 0x26, 0x37, 0x46, 0xcd,
0x27, 0x50, 0xb8, 0x1a, 0x4e, 0x1d, 0xdb, 0xa2, 0x48, 0x0e, 0x21, 0x77, 0xa9, 0x18, 0x0e, 0xcb, 0xad, 0xa8, 0x9a, 0x69, 0x94, 0xa1, 0xc6, 0xca, 0x1d, 0xf8, 0x77, 0x0f, 0xd9, 0xc8, 0x10, 0x05,
0x37, 0xee, 0xcf, 0xa2, 0xab, 0xa0, 0x47, 0x51, 0x92, 0xea, 0x0a, 0xdc, 0xde, 0xd7, 0x69, 0x54, 0xdf, 0xf6, 0x90, 0xb2, 0x69, 0x2b, 0x52, 0x39, 0x81, 0xfc, 0xe5, 0x74, 0x6a, 0x5b, 0x26, 0x45,
0x8a, 0x86, 0xd4, 0x24, 0xc8, 0x1e, 0xeb, 0x06, 0x43, 0x97, 0x4a, 0x42, 0x25, 0x55, 0xcb, 0xa9, 0x72, 0x08, 0xd9, 0x91, 0xc5, 0x1c, 0x96, 0xab, 0xdf, 0x9e, 0xa7, 0x11, 0xbe, 0xf1, 0x21, 0x49,
0xa1, 0x5b, 0x35, 0xa0, 0x38, 0x0e, 0x09, 0xe8, 0xa9, 0x00, 0x51, 0x61, 0x0e, 0xbb, 0x19, 0xbf, 0x65, 0x05, 0xfe, 0xdb, 0xd7, 0x68, 0x58, 0x8a, 0x06, 0xd2, 0x44, 0xc8, 0x1c, 0x6b, 0x3a, 0x43,
0x58, 0x96, 0xea, 0x3b, 0x28, 0x6e, 0x71, 0x45, 0x4d, 0x34, 0xef, 0xdf, 0x37, 0xa3, 0x07, 0x77, 0x87, 0x8a, 0x42, 0x39, 0x59, 0xcd, 0x2a, 0x41, 0x58, 0xd1, 0xa1, 0x30, 0x0e, 0xf1, 0xe5, 0x29,
0x26, 0x6a, 0xcd, 0xad, 0xf3, 0xdf, 0x04, 0x28, 0xbe, 0xe2, 0x32, 0x9f, 0xff, 0xcd, 0xc8, 0x06, 0x00, 0x61, 0x61, 0x0e, 0xbb, 0x9a, 0xbe, 0x08, 0x4b, 0xe5, 0x0d, 0x14, 0xb6, 0xf8, 0x38, 0x4f,
0xe4, 0xfd, 0x95, 0xe2, 0xaf, 0xd4, 0x40, 0xb3, 0x93, 0xbb, 0xb8, 0xeb, 0xbd, 0x75, 0x0f, 0x34, 0x98, 0xf7, 0xe7, 0xcd, 0xe8, 0xc2, 0xff, 0x13, 0xb5, 0xae, 0xcd, 0xf9, 0x2f, 0x02, 0x14, 0x5e,
0xda, 0x53, 0x83, 0xcd, 0xf5, 0x6c, 0xaf, 0x2d, 0x13, 0x44, 0xe7, 0xd6, 0x96, 0x87, 0x50, 0xdc, 0xf0, 0x1d, 0xbb, 0xfe, 0x6f, 0x46, 0x36, 0x20, 0xe7, 0xed, 0x33, 0x7f, 0xcf, 0xfd, 0xa9, 0x9d,
0x46, 0x03, 0x13, 0xba, 0x72, 0xcd, 0xb2, 0x34, 0xce, 0xd3, 0x00, 0x91, 0x18, 0xc9, 0x00, 0x52, 0x7c, 0x08, 0x76, 0xdd, 0x27, 0xff, 0x40, 0xa5, 0x5d, 0xc5, 0x7f, 0x36, 0xdc, 0xb3, 0x6b, 0xcb,
0x7b, 0xc8, 0xc8, 0x93, 0x29, 0x68, 0x24, 0xac, 0xa4, 0xfc, 0x74, 0x66, 0x5c, 0xd0, 0x8a, 0x8f, 0x84, 0xd0, 0x6b, 0xb3, 0xe5, 0x2e, 0x14, 0xb6, 0x51, 0xc7, 0x18, 0x57, 0xa6, 0x2c, 0x4b, 0xfd,
0x90, 0xf6, 0xd6, 0x82, 0x4c, 0xf3, 0x45, 0x49, 0x5c, 0x39, 0x79, 0xed, 0x06, 0xc8, 0xa0, 0xf8, 0x3c, 0x05, 0x10, 0x0e, 0x23, 0xe9, 0x43, 0x72, 0x0f, 0x19, 0x79, 0x30, 0x83, 0x8c, 0x98, 0x95,
0x27, 0xc8, 0xf8, 0xca, 0x25, 0xd3, 0x24, 0x49, 0x5e, 0x28, 0x79, 0xfd, 0x26, 0xd0, 0x88, 0x80, 0x94, 0x1e, 0xce, 0x8d, 0xf3, 0xad, 0x78, 0x0f, 0x29, 0x77, 0x2d, 0xc8, 0x2c, 0x3f, 0x67, 0xb1,
0xaf, 0x91, 0xa9, 0x08, 0x24, 0xeb, 0x7e, 0x2a, 0x02, 0xd7, 0x29, 0xf1, 0x0d, 0x64, 0x7c, 0xdd, 0x2b, 0x27, 0xad, 0x5d, 0x01, 0xe9, 0x17, 0xff, 0x00, 0x69, 0x6f, 0x72, 0xc9, 0x2c, 0x24, 0xf1,
0x4c, 0x45, 0x20, 0x59, 0x62, 0x72, 0x71, 0x62, 0x23, 0x76, 0xbc, 0x9f, 0x94, 0xe6, 0xdb, 0xd3, 0x0b, 0x25, 0xad, 0x5f, 0x05, 0x1a, 0x0a, 0xf0, 0x66, 0x64, 0x26, 0x01, 0xf1, 0x73, 0x3f, 0x93,
0x8b, 0xd2, 0xc2, 0x8f, 0x8b, 0xd2, 0xc2, 0xe7, 0x51, 0x49, 0x38, 0x1d, 0x95, 0x84, 0xef, 0xa3, 0x80, 0x69, 0x93, 0xf8, 0x0a, 0xd2, 0xde, 0xdc, 0xcc, 0x24, 0x20, 0x7e, 0xc4, 0xa4, 0xc2, 0xc4,
0x92, 0x70, 0x3e, 0x2a, 0x09, 0xaf, 0x77, 0xff, 0xe2, 0xbf, 0x6b, 0x23, 0xf2, 0x5a, 0x19, 0x5e, 0x46, 0xec, 0xb8, 0xff, 0x90, 0x1a, 0xaf, 0x4f, 0x2f, 0x8a, 0x0b, 0xdf, 0x2e, 0x8a, 0x0b, 0x1f,
0xf1, 0xd1, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x4b, 0xd5, 0xe0, 0xc8, 0x09, 0x00, 0x00, 0x87, 0x45, 0xe1, 0x74, 0x58, 0x14, 0xbe, 0x0e, 0x8b, 0xc2, 0xf9, 0xb0, 0x28, 0xbc, 0xdc, 0xfd,
0x8d, 0x3f, 0x7d, 0x1b, 0x61, 0xd4, 0x4c, 0xf3, 0x8a, 0xf7, 0x7e, 0x04, 0x00, 0x00, 0xff, 0xff,
0x17, 0x73, 0xba, 0x43, 0x45, 0x0a, 0x00, 0x00,
} }

View File

@ -89,7 +89,12 @@ message Container {
// associated with the container. One may provide protobuf, json, or other // associated with the container. One may provide protobuf, json, or other
// encoding formats. The primary use of this is to further decorate the // encoding formats. The primary use of this is to further decorate the
// container object with fields that may be specific to a client integration. // container object with fields that may be specific to a client integration.
repeated google.protobuf.Any extensions = 10 [(gogoproto.nullable) = false]; //
// The key portion of this map should identify a "name" for the extension
// that should be unique against other extensions. When updating extension
// data, one should only update the specified extension using field paths
// to select a specific map key.
map<string, google.protobuf.Any> extensions = 10 [(gogoproto.nullable) = false];
} }
message GetContainerRequest { message GetContainerRequest {

View File

@ -44,7 +44,7 @@ type Container interface {
// SetLabels sets the provided labels for the container and returns the final label set // SetLabels sets the provided labels for the container and returns the final label set
SetLabels(context.Context, map[string]string) (map[string]string, error) SetLabels(context.Context, map[string]string) (map[string]string, error)
// Extensions returns the extensions set on the container // Extensions returns the extensions set on the container
Extensions() []prototypes.Any Extensions() map[string]prototypes.Any
} }
func containerFromRecord(client *Client, c containers.Container) *container { func containerFromRecord(client *Client, c containers.Container) *container {
@ -161,7 +161,7 @@ func (c *container) Image(ctx context.Context) (Image, error) {
}, nil }, nil
} }
func (c *container) Extensions() []prototypes.Any { func (c *container) Extensions() map[string]prototypes.Any {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
return c.c.Extensions return c.c.Extensions

View File

@ -135,13 +135,20 @@ func setSnapshotterIfEmpty(c *containers.Container) {
// Make sure to register the type of `extension` in the typeurl package via // Make sure to register the type of `extension` in the typeurl package via
// `typeurl.Register` otherwise the type data will be inferred, including how // `typeurl.Register` otherwise the type data will be inferred, including how
// to encode and decode the object. // to encode and decode the object.
func WithContainerExtension(extension interface{}) NewContainerOpts { func WithContainerExtension(name string, extension interface{}) NewContainerOpts {
return func(ctx context.Context, client *Client, c *containers.Container) error { return func(ctx context.Context, client *Client, c *containers.Container) error {
any, err := typeurl.MarshalAny(extension) any, err := typeurl.MarshalAny(extension)
if err != nil { if err != nil {
return err return err
} }
c.Extensions = append(c.Extensions, *any)
if name == "" {
return errors.Wrapf(errdefs.ErrInvalidArgument, "extension key must not be zero-length")
}
if c.Extensions == nil {
c.Extensions = make(map[string]types.Any)
}
c.Extensions[name] = *any
return nil return nil
} }
} }

View File

@ -1384,7 +1384,7 @@ func TestContainerExtensions(t *testing.T) {
defer client.Close() defer client.Close()
ext := gogotypes.Any{TypeUrl: "test.ext.url", Value: []byte("hello")} ext := gogotypes.Any{TypeUrl: "test.ext.url", Value: []byte("hello")}
container, err := client.NewContainer(ctx, id, WithNewSpec(), WithContainerExtension(&ext)) container, err := client.NewContainer(ctx, id, WithNewSpec(), WithContainerExtension("hello", &ext))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1395,11 +1395,11 @@ func TestContainerExtensions(t *testing.T) {
if len(cExts) != 1 { if len(cExts) != 1 {
t.Fatal("expected 1 container extension") t.Fatal("expected 1 container extension")
} }
if cExts[0].TypeUrl != ext.TypeUrl { if cExts["hello"].TypeUrl != ext.TypeUrl {
t.Fatalf("got unexpected type url for extension: %s", cExts[0].TypeUrl) t.Fatalf("got unexpected type url for extension: %s", cExts["hello"].TypeUrl)
} }
if !bytes.Equal(cExts[0].Value, ext.Value) { if !bytes.Equal(cExts["hello"].Value, ext.Value) {
t.Fatalf("expected extension value %q, got: %q", ext.Value, cExts[0].Value) t.Fatalf("expected extension value %q, got: %q", ext.Value, cExts["hello"].Value)
} }
} }

View File

@ -59,7 +59,7 @@ type Container struct {
UpdatedAt time.Time UpdatedAt time.Time
// Extensions stores client-specified metadata // Extensions stores client-specified metadata
Extensions []types.Any Extensions map[string]types.Any
} }
type RuntimeInfo struct { type RuntimeInfo struct {

View File

@ -181,6 +181,15 @@ func (s *containerStore) Update(ctx context.Context, container containers.Contai
continue continue
} }
if strings.HasPrefix(path, "extensions.") {
if updated.Extensions == nil {
updated.Extensions = map[string]types.Any{}
}
key := strings.TrimPrefix(path, "extensions.")
updated.Extensions[key] = container.Extensions[key]
continue
}
switch path { switch path {
case "labels": case "labels":
updated.Labels = container.Labels updated.Labels = container.Labels
@ -228,6 +237,12 @@ func validateContainer(container *containers.Container) error {
return errors.Wrapf(err, "container.ID validation error") return errors.Wrapf(err, "container.ID validation error")
} }
for k := range container.Extensions {
if k == "" {
return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Extension keys must not be zero-length")
}
}
// labels and image have no validation // labels and image have no validation
if container.Runtime.Name == "" { if container.Runtime.Name == "" {
return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Runtime.Name must be set") return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Runtime.Name must be set")
@ -291,19 +306,25 @@ func readContainer(container *containers.Container, bkt *bolt.Bucket) error {
case string(bucketKeySnapshotter): case string(bucketKeySnapshotter):
container.Snapshotter = string(v) container.Snapshotter = string(v)
case string(bucketKeyExtensions): case string(bucketKeyExtensions):
buf := proto.NewBuffer(v) ebkt := bkt.Bucket(bucketKeyExtensions)
n, err := buf.DecodeVarint() if ebkt == nil {
if err != nil { return nil
return errors.Wrap(err, "error reading number of container extensions")
} }
extensions := make([]types.Any, 0, n)
for i := 0; i < int(n); i++ { extensions := make(map[string]types.Any)
var any types.Any if err := ebkt.ForEach(func(k, v []byte) error {
if err := buf.DecodeMessage(&any); err != nil { var a types.Any
return errors.Wrap(err, "error decoding container extension") if err := proto.Unmarshal(v, &a); err != nil {
return err
} }
extensions = append(extensions, any)
extensions[string(k)] = a
return nil
}); err != nil {
return err
} }
container.Extensions = extensions container.Extensions = extensions
} }
@ -352,18 +373,21 @@ func writeContainer(bkt *bolt.Bucket, container *containers.Container) error {
return err return err
} }
if container.Extensions != nil { if len(container.Extensions) > 0 {
buf := proto.NewBuffer(nil) ebkt, err := bkt.CreateBucketIfNotExists(bucketKeyExtensions)
if err := buf.EncodeVarint(uint64(len(container.Extensions))); err != nil { if err != nil {
return err return err
} }
for _, ext := range container.Extensions {
if err := buf.EncodeMessage(&ext); err != nil { for name, ext := range container.Extensions {
p, err := proto.Marshal(&ext)
if err != nil {
return err
}
if err := ebkt.Put([]byte(name), p); err != nil {
return err return err
} }
}
if err := bkt.Put(bucketKeyExtensions, buf.Bytes()); err != nil {
return err
} }
} }

View File

@ -428,8 +428,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) {
Runtime: containers.RuntimeInfo{ Runtime: containers.RuntimeInfo{
Name: "testruntime", Name: "testruntime",
}, },
Extensions: []types.Any{ Extensions: map[string]types.Any{
{ "hello": {
TypeUrl: "test.update.extensions", TypeUrl: "test.update.extensions",
Value: []byte("hello"), Value: []byte("hello"),
}, },
@ -440,8 +440,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) {
Runtime: containers.RuntimeInfo{ Runtime: containers.RuntimeInfo{
Name: "testruntime", Name: "testruntime",
}, },
Extensions: []types.Any{ Extensions: map[string]types.Any{
{ "hello": {
TypeUrl: "test.update.extensions", TypeUrl: "test.update.extensions",
Value: []byte("world"), Value: []byte("world"),
}, },
@ -452,8 +452,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) {
Runtime: containers.RuntimeInfo{ Runtime: containers.RuntimeInfo{
Name: "testruntime", Name: "testruntime",
}, },
Extensions: []types.Any{ Extensions: map[string]types.Any{
{ "hello": {
TypeUrl: "test.update.extensions", TypeUrl: "test.update.extensions",
Value: []byte("world"), Value: []byte("world"),
}, },
@ -467,8 +467,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) {
Runtime: containers.RuntimeInfo{ Runtime: containers.RuntimeInfo{
Name: "testruntime", Name: "testruntime",
}, },
Extensions: []types.Any{ Extensions: map[string]types.Any{
{ "hello": {
TypeUrl: "test.update.extensions", TypeUrl: "test.update.extensions",
Value: []byte("hello"), Value: []byte("hello"),
}, },
@ -479,8 +479,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) {
Runtime: containers.RuntimeInfo{ Runtime: containers.RuntimeInfo{
Name: "testruntime", Name: "testruntime",
}, },
Extensions: []types.Any{ Extensions: map[string]types.Any{
{ "hello": {
TypeUrl: "test.update.extensions", TypeUrl: "test.update.extensions",
Value: []byte("world"), Value: []byte("world"),
}, },
@ -492,8 +492,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) {
Runtime: containers.RuntimeInfo{ Runtime: containers.RuntimeInfo{
Name: "testruntime", Name: "testruntime",
}, },
Extensions: []types.Any{ Extensions: map[string]types.Any{
{ "hello": {
TypeUrl: "test.update.extensions", TypeUrl: "test.update.extensions",
Value: []byte("hello"), Value: []byte("hello"),
}, },
@ -507,8 +507,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) {
Runtime: containers.RuntimeInfo{ Runtime: containers.RuntimeInfo{
Name: "testruntime", Name: "testruntime",
}, },
Extensions: []types.Any{ Extensions: map[string]types.Any{
{ "hello": {
TypeUrl: "test.update.extensions", TypeUrl: "test.update.extensions",
Value: []byte("hello"), Value: []byte("hello"),
}, },
@ -518,8 +518,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) {
Labels: map[string]string{ Labels: map[string]string{
"foo": "one", "foo": "one",
}, },
Extensions: []types.Any{ Extensions: map[string]types.Any{
{ "hello": {
TypeUrl: "test.update.extensions", TypeUrl: "test.update.extensions",
Value: []byte("world"), Value: []byte("world"),
}, },
@ -531,14 +531,59 @@ func TestContainersCreateUpdateDelete(t *testing.T) {
Runtime: containers.RuntimeInfo{ Runtime: containers.RuntimeInfo{
Name: "testruntime", Name: "testruntime",
}, },
Extensions: []types.Any{ Extensions: map[string]types.Any{
{ "hello": {
TypeUrl: "test.update.extensions", TypeUrl: "test.update.extensions",
Value: []byte("world"), Value: []byte("world"),
}, },
}, },
}, },
}, },
{
name: "UpdateExtensionsFieldPathIsolated",
original: containers.Container{
Spec: encoded,
Runtime: containers.RuntimeInfo{
Name: "testruntime",
},
Extensions: map[string]types.Any{
// leaves hello in place.
"hello": {
TypeUrl: "test.update.extensions",
Value: []byte("hello"),
},
},
},
input: containers.Container{
Extensions: map[string]types.Any{
"hello": {
TypeUrl: "test.update.extensions",
Value: []byte("universe"), // this will be ignored
},
"bar": {
TypeUrl: "test.update.extensions",
Value: []byte("foo"), // this will be added
},
},
},
fieldpaths: []string{"extensions.bar"}, //
expected: containers.Container{
Spec: encoded,
Runtime: containers.RuntimeInfo{
Name: "testruntime",
},
Extensions: map[string]types.Any{
"hello": {
TypeUrl: "test.update.extensions",
Value: []byte("hello"), // remains as world
},
"bar": {
TypeUrl: "test.update.extensions",
Value: []byte("foo"), // this will be added
},
},
},
},
} { } {
t.Run(testcase.name, func(t *testing.T) { t.Run(testcase.name, func(t *testing.T) {
testcase.original.ID = testcase.name testcase.original.ID = testcase.name
@ -648,7 +693,7 @@ func checkContainerTimestamps(t *testing.T, c *containers.Container, now time.Ti
func checkContainersEqual(t *testing.T, a, b *containers.Container, format string, args ...interface{}) { func checkContainersEqual(t *testing.T, a, b *containers.Container, format string, args ...interface{}) {
if !reflect.DeepEqual(a, b) { if !reflect.DeepEqual(a, b) {
t.Fatalf("containers not equal %v != %v: "+format, append([]interface{}{a, b}, args...)...) t.Fatalf("containers not equal \n\t%v != \n\t%v: "+format, append([]interface{}{a, b}, args...)...)
} }
} }