From f7d31e2be40702c70b081f2d0cb471b35c58bcc8 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Fri, 8 Sep 2017 14:51:30 -0700 Subject: [PATCH] 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 --- api/next.pb.txt | 23 +- api/services/containers/v1/containers.pb.go | 272 ++++++++++++++------ api/services/containers/v1/containers.proto | 7 +- container.go | 4 +- container_opts.go | 11 +- container_test.go | 10 +- containers/containers.go | 2 +- metadata/containers.go | 60 +++-- metadata/containers_test.go | 83 ++++-- 9 files changed, 349 insertions(+), 123 deletions(-) diff --git a/api/next.pb.txt b/api/next.pb.txt index a1ea79a93..1bf2524a6 100755 --- a/api/next.pb.txt +++ b/api/next.pb.txt @@ -190,7 +190,7 @@ file { number: 10 label: LABEL_REPEATED type: TYPE_MESSAGE - type_name: ".google.protobuf.Any" + type_name: ".containerd.services.containers.v1.Container.ExtensionsEntry" options { 65001: 0 } @@ -234,6 +234,27 @@ file { 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 { name: "GetContainerRequest" diff --git a/api/services/containers/v1/containers.pb.go b/api/services/containers/v1/containers.pb.go index e86882fa3..edcd93c6b 100644 --- a/api/services/containers/v1/containers.pb.go +++ b/api/services/containers/v1/containers.pb.go @@ -98,7 +98,12 @@ type Container struct { // associated with the container. One may provide protobuf, json, or other // encoding formats. The primary use of this is to further decorate the // 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{} } @@ -524,15 +529,29 @@ func (m *Container) MarshalTo(dAtA []byte) (int, error) { } i += n4 if len(m.Extensions) > 0 { - for _, msg := range m.Extensions { + for k, _ := range m.Extensions { dAtA[i] = 0x52 i++ - i = encodeVarintContainers(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + v := m.Extensions[k] + 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 { return 0, err } - i += n + i += n5 } } return i, nil @@ -563,11 +582,11 @@ func (m *Container_Runtime) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ 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 { return 0, err } - i += n5 + i += n6 } return i, nil } @@ -614,11 +633,11 @@ func (m *GetContainerResponse) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ 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 { return 0, err } - i += n6 + i += n7 return i, nil } @@ -703,11 +722,11 @@ func (m *CreateContainerRequest) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ 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 { return 0, err } - i += n7 + i += n8 return i, nil } @@ -729,11 +748,11 @@ func (m *CreateContainerResponse) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ 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 { return 0, err } - i += n8 + i += n9 return i, nil } @@ -755,20 +774,20 @@ func (m *UpdateContainerRequest) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ 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 { return 0, err } - i += n9 + i += n10 if m.UpdateMask != nil { dAtA[i] = 0x12 i++ 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 { return 0, err } - i += n10 + i += n11 } return i, nil } @@ -791,11 +810,11 @@ func (m *UpdateContainerResponse) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ 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 { return 0, err } - i += n11 + i += n12 return i, nil } @@ -890,9 +909,12 @@ func (m *Container) Size() (n int) { l = github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt) n += 1 + l + sovContainers(uint64(l)) if len(m.Extensions) > 0 { - for _, e := range m.Extensions { - l = e.Size() - n += 1 + l + sovContainers(uint64(l)) + for k, v := range m.Extensions { + _ = k + _ = 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 @@ -1027,6 +1049,16 @@ func (this *Container) String() string { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) } 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{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Labels:` + mapStringForLabels + `,`, @@ -1037,7 +1069,7 @@ func (this *Container) String() string { `SnapshotKey:` + fmt.Sprintf("%v", this.SnapshotKey) + `,`, `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) + `,`, - `Extensions:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Extensions), "Any", "google_protobuf1.Any", 1), `&`, ``, 1) + `,`, + `Extensions:` + mapStringForExtensions + `,`, `}`, }, "") return s @@ -1565,9 +1597,99 @@ func (m *Container) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Extensions = append(m.Extensions, google_protobuf1.Any{}) - if err := m.Extensions[len(m.Extensions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + var keykey uint64 + for shift := uint(0); ; shift += 7 { + 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 default: @@ -2564,52 +2686,54 @@ func init() { } var fileDescriptorContainers = []byte{ - // 752 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4d, 0x73, 0xd2, 0x5c, - 0x14, 0x6e, 0x02, 0x85, 0x72, 0x78, 0x17, 0xef, 0x5c, 0x11, 0x63, 0x9c, 0x01, 0xca, 0x8a, 0x85, - 0x06, 0x8b, 0x8e, 0xf6, 0x63, 0x55, 0xfa, 0x35, 0x8e, 0xad, 0xd3, 0xc9, 0xe8, 0x46, 0x17, 0x35, - 0xc0, 0x29, 0x8d, 0xe4, 0xcb, 0xdc, 0x0b, 0x23, 0xe3, 0x42, 0x7f, 0x82, 0x6b, 0xff, 0x80, 0x7f, - 0xa5, 0x4b, 0x97, 0xae, 0x6a, 0xcb, 0x2f, 0x71, 0x72, 0x93, 0x34, 0x29, 0xa4, 0x0a, 0x55, 0x76, - 0xe7, 0xe4, 0x9e, 0xe7, 0x9c, 0xe7, 0x9e, 0xf3, 0x9c, 0x24, 0xb0, 0xdf, 0xd5, 0xd9, 0x49, 0xbf, - 0xa5, 0xb4, 0x6d, 0xb3, 0xde, 0xb6, 0x2d, 0xa6, 0xe9, 0x16, 0xba, 0x9d, 0xb8, 0xa9, 0x39, 0x7a, - 0x9d, 0xa2, 0x3b, 0xd0, 0xdb, 0x48, 0xa3, 0xe7, 0xb4, 0x3e, 0x58, 0x89, 0x79, 0x8a, 0xe3, 0xda, - 0xcc, 0x26, 0xcb, 0x11, 0x4e, 0x09, 0x31, 0x4a, 0x2c, 0x6a, 0xb0, 0x22, 0x17, 0xba, 0x76, 0xd7, - 0xe6, 0xd1, 0x75, 0xcf, 0xf2, 0x81, 0xf2, 0xdd, 0xae, 0x6d, 0x77, 0x0d, 0xac, 0x73, 0xaf, 0xd5, - 0x3f, 0xae, 0x6b, 0xd6, 0x30, 0x38, 0xba, 0x37, 0x7e, 0x84, 0xa6, 0xc3, 0xc2, 0xc3, 0xca, 0xf8, - 0xe1, 0xb1, 0x8e, 0x46, 0xe7, 0xc8, 0xd4, 0x68, 0x2f, 0x88, 0x28, 0x8f, 0x47, 0x30, 0xdd, 0x44, - 0xca, 0x34, 0xd3, 0xf1, 0x03, 0xaa, 0x5f, 0x17, 0x21, 0xb7, 0x15, 0x52, 0x24, 0x45, 0x10, 0xf5, - 0x8e, 0x24, 0x54, 0x84, 0x5a, 0xae, 0x99, 0x19, 0x9d, 0x95, 0xc5, 0x67, 0xdb, 0xaa, 0xa8, 0x77, - 0xc8, 0x21, 0x64, 0x0c, 0xad, 0x85, 0x06, 0x95, 0xc4, 0x4a, 0xaa, 0x96, 0x6f, 0xac, 0x2a, 0x7f, - 0xbc, 0xaa, 0x72, 0x99, 0x55, 0xd9, 0xe7, 0xd0, 0x1d, 0x8b, 0xb9, 0x43, 0x35, 0xc8, 0x43, 0x0a, - 0xb0, 0xa8, 0x9b, 0x5a, 0x17, 0xa5, 0x94, 0x57, 0x4c, 0xf5, 0x1d, 0xf2, 0x02, 0xb2, 0x6e, 0xdf, - 0xf2, 0x38, 0x4a, 0xe9, 0x8a, 0x50, 0xcb, 0x37, 0x1e, 0xcf, 0x54, 0x48, 0xf5, 0xb1, 0x6a, 0x98, - 0x84, 0xd4, 0x20, 0x4d, 0x1d, 0x6c, 0x4b, 0x8b, 0x3c, 0x59, 0x41, 0xf1, 0xbb, 0xa1, 0x84, 0xdd, - 0x50, 0x36, 0xad, 0xa1, 0xca, 0x23, 0x48, 0x05, 0xf2, 0xd4, 0xd2, 0x1c, 0x7a, 0x62, 0x33, 0x86, - 0xae, 0x94, 0xe1, 0xac, 0xe2, 0x8f, 0xc8, 0x32, 0xfc, 0x17, 0xba, 0x47, 0x3d, 0x1c, 0x4a, 0xd9, - 0xab, 0x21, 0xcf, 0x71, 0x48, 0xb6, 0x00, 0xda, 0x2e, 0x6a, 0x0c, 0x3b, 0x47, 0x1a, 0x93, 0x96, - 0x78, 0x51, 0x79, 0xa2, 0xe8, 0xcb, 0x70, 0x04, 0xcd, 0xa5, 0xd3, 0xb3, 0xf2, 0xc2, 0x97, 0x9f, - 0x65, 0x41, 0xcd, 0x05, 0xb8, 0x4d, 0xe6, 0x25, 0xe9, 0x3b, 0x9d, 0x30, 0x49, 0x6e, 0x96, 0x24, - 0x01, 0x6e, 0x93, 0x91, 0x75, 0x00, 0xfc, 0xc0, 0xd0, 0xa2, 0xba, 0x6d, 0x51, 0x09, 0xf8, 0xd0, - 0x12, 0xaf, 0xdf, 0x4c, 0x7b, 0x70, 0x35, 0x16, 0x2d, 0xaf, 0x41, 0x3e, 0x36, 0x31, 0xf2, 0x3f, - 0xa4, 0xbc, 0xeb, 0x72, 0x51, 0xa8, 0x9e, 0xe9, 0xcd, 0x6e, 0xa0, 0x19, 0x7d, 0x94, 0x44, 0x7f, - 0x76, 0xdc, 0x59, 0x17, 0x57, 0x05, 0xf9, 0x00, 0xb2, 0xc1, 0x0c, 0x08, 0x81, 0xb4, 0xa5, 0x99, - 0x18, 0xe0, 0xb8, 0x4d, 0x14, 0xc8, 0xda, 0x0e, 0xe3, 0x94, 0xc4, 0xdf, 0x4c, 0x24, 0x0c, 0xaa, - 0x3e, 0x80, 0x5b, 0x7b, 0xc8, 0x2e, 0xe7, 0xab, 0xe2, 0xfb, 0x3e, 0x52, 0x76, 0x9d, 0x4a, 0xab, - 0x27, 0x50, 0xb8, 0x1a, 0x4e, 0x1d, 0xdb, 0xa2, 0x48, 0x0e, 0x21, 0x77, 0xa9, 0x18, 0x0e, 0xcb, - 0x37, 0xee, 0xcf, 0xa2, 0xab, 0xa0, 0x47, 0x51, 0x92, 0xea, 0x0a, 0xdc, 0xde, 0xd7, 0x69, 0x54, - 0x8a, 0x86, 0xd4, 0x24, 0xc8, 0x1e, 0xeb, 0x06, 0x43, 0x97, 0x4a, 0x42, 0x25, 0x55, 0xcb, 0xa9, - 0xa1, 0x5b, 0x35, 0xa0, 0x38, 0x0e, 0x09, 0xe8, 0xa9, 0x00, 0x51, 0x61, 0x0e, 0xbb, 0x19, 0xbf, - 0x58, 0x96, 0xea, 0x3b, 0x28, 0x6e, 0x71, 0x45, 0x4d, 0x34, 0xef, 0xdf, 0x37, 0xa3, 0x07, 0x77, - 0x26, 0x6a, 0xcd, 0xad, 0xf3, 0xdf, 0x04, 0x28, 0xbe, 0xe2, 0x32, 0x9f, 0xff, 0xcd, 0xc8, 0x06, - 0xe4, 0xfd, 0x95, 0xe2, 0xaf, 0xd4, 0x40, 0xb3, 0x93, 0xbb, 0xb8, 0xeb, 0xbd, 0x75, 0x0f, 0x34, - 0xda, 0x53, 0x83, 0xcd, 0xf5, 0x6c, 0xaf, 0x2d, 0x13, 0x44, 0xe7, 0xd6, 0x96, 0x87, 0x50, 0xdc, - 0x46, 0x03, 0x13, 0xba, 0x72, 0xcd, 0xb2, 0x34, 0xce, 0xd3, 0x00, 0x91, 0x18, 0xc9, 0x00, 0x52, - 0x7b, 0xc8, 0xc8, 0x93, 0x29, 0x68, 0x24, 0xac, 0xa4, 0xfc, 0x74, 0x66, 0x5c, 0xd0, 0x8a, 0x8f, - 0x90, 0xf6, 0xd6, 0x82, 0x4c, 0xf3, 0x45, 0x49, 0x5c, 0x39, 0x79, 0xed, 0x06, 0xc8, 0xa0, 0xf8, - 0x27, 0xc8, 0xf8, 0xca, 0x25, 0xd3, 0x24, 0x49, 0x5e, 0x28, 0x79, 0xfd, 0x26, 0xd0, 0x88, 0x80, - 0xaf, 0x91, 0xa9, 0x08, 0x24, 0xeb, 0x7e, 0x2a, 0x02, 0xd7, 0x29, 0xf1, 0x0d, 0x64, 0x7c, 0xdd, - 0x4c, 0x45, 0x20, 0x59, 0x62, 0x72, 0x71, 0x62, 0x23, 0x76, 0xbc, 0x9f, 0x94, 0xe6, 0xdb, 0xd3, - 0x8b, 0xd2, 0xc2, 0x8f, 0x8b, 0xd2, 0xc2, 0xe7, 0x51, 0x49, 0x38, 0x1d, 0x95, 0x84, 0xef, 0xa3, - 0x92, 0x70, 0x3e, 0x2a, 0x09, 0xaf, 0x77, 0xff, 0xe2, 0xbf, 0x6b, 0x23, 0xf2, 0x5a, 0x19, 0x5e, - 0xf1, 0xd1, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x4b, 0xd5, 0xe0, 0xc8, 0x09, 0x00, 0x00, + // 776 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcd, 0x72, 0x12, 0x5b, + 0x10, 0xce, 0x00, 0x81, 0xd0, 0xdc, 0xaa, 0x7b, 0xeb, 0x5c, 0x2e, 0x77, 0x1c, 0xab, 0x80, 0xb0, + 0xa2, 0x2c, 0x1d, 0x0c, 0x5a, 0x9a, 0x1f, 0x37, 0x21, 0x7f, 0x65, 0x99, 0x58, 0xa9, 0x51, 0x37, + 0xba, 0x88, 0x03, 0x74, 0xc8, 0xc8, 0xfc, 0x39, 0xe7, 0x40, 0x49, 0xb9, 0xd0, 0x47, 0x70, 0xe7, + 0x23, 0xf8, 0x2a, 0x59, 0xba, 0x74, 0x15, 0x13, 0x9e, 0xc4, 0x9a, 0x33, 0x33, 0xcc, 0x04, 0x06, + 0x85, 0x68, 0x76, 0xa7, 0x39, 0xfd, 0x7d, 0xfd, 0xf1, 0x75, 0xf7, 0x01, 0xd8, 0xef, 0x68, 0xec, + 0xa4, 0xd7, 0x94, 0x5b, 0x96, 0x51, 0x6b, 0x59, 0x26, 0x53, 0x35, 0x13, 0x9d, 0x76, 0xf4, 0xa8, + 0xda, 0x5a, 0x8d, 0xa2, 0xd3, 0xd7, 0x5a, 0x48, 0xc3, 0xcf, 0x69, 0xad, 0xbf, 0x12, 0x89, 0x64, + 0xdb, 0xb1, 0x98, 0x45, 0x96, 0x43, 0x9c, 0x1c, 0x60, 0xe4, 0x48, 0x56, 0x7f, 0x45, 0xca, 0x77, + 0xac, 0x8e, 0xc5, 0xb3, 0x6b, 0xee, 0xc9, 0x03, 0x4a, 0x37, 0x3a, 0x96, 0xd5, 0xd1, 0xb1, 0xc6, + 0xa3, 0x66, 0xef, 0xb8, 0xa6, 0x9a, 0x03, 0xff, 0xea, 0xe6, 0xf8, 0x15, 0x1a, 0x36, 0x0b, 0x2e, + 0xcb, 0xe3, 0x97, 0xc7, 0x1a, 0xea, 0xed, 0x23, 0x43, 0xa5, 0x5d, 0x3f, 0xa3, 0x34, 0x9e, 0xc1, + 0x34, 0x03, 0x29, 0x53, 0x0d, 0xdb, 0x4b, 0xa8, 0x7c, 0x4e, 0x43, 0x76, 0x2b, 0x90, 0x48, 0x0a, + 0x90, 0xd0, 0xda, 0xa2, 0x50, 0x16, 0xaa, 0xd9, 0x46, 0x7a, 0x78, 0x56, 0x4a, 0x3c, 0xde, 0x56, + 0x12, 0x5a, 0x9b, 0x1c, 0x42, 0x5a, 0x57, 0x9b, 0xa8, 0x53, 0x31, 0x51, 0x4e, 0x56, 0x73, 0xf5, + 0x55, 0xf9, 0x97, 0x5f, 0x55, 0x1e, 0xb1, 0xca, 0xfb, 0x1c, 0xba, 0x63, 0x32, 0x67, 0xa0, 0xf8, + 0x3c, 0x24, 0x0f, 0x8b, 0x9a, 0xa1, 0x76, 0x50, 0x4c, 0xba, 0xc5, 0x14, 0x2f, 0x20, 0x4f, 0x21, + 0xe3, 0xf4, 0x4c, 0x57, 0xa3, 0x98, 0x2a, 0x0b, 0xd5, 0x5c, 0xfd, 0xfe, 0x5c, 0x85, 0x14, 0x0f, + 0xab, 0x04, 0x24, 0xa4, 0x0a, 0x29, 0x6a, 0x63, 0x4b, 0x5c, 0xe4, 0x64, 0x79, 0xd9, 0x73, 0x43, + 0x0e, 0xdc, 0x90, 0x37, 0xcd, 0x81, 0xc2, 0x33, 0x48, 0x19, 0x72, 0xd4, 0x54, 0x6d, 0x7a, 0x62, + 0x31, 0x86, 0x8e, 0x98, 0xe6, 0xaa, 0xa2, 0x1f, 0x91, 0x65, 0xf8, 0x2b, 0x08, 0x8f, 0xba, 0x38, + 0x10, 0x33, 0x97, 0x53, 0x9e, 0xe0, 0x80, 0x6c, 0x01, 0xb4, 0x1c, 0x54, 0x19, 0xb6, 0x8f, 0x54, + 0x26, 0x2e, 0xf1, 0xa2, 0xd2, 0x44, 0xd1, 0xe7, 0x41, 0x0b, 0x1a, 0x4b, 0xa7, 0x67, 0xa5, 0x85, + 0x4f, 0xdf, 0x4b, 0x82, 0x92, 0xf5, 0x71, 0x9b, 0xcc, 0x25, 0xe9, 0xd9, 0xed, 0x80, 0x24, 0x3b, + 0x0f, 0x89, 0x8f, 0xdb, 0x64, 0xa4, 0x09, 0x80, 0xef, 0x18, 0x9a, 0x54, 0xb3, 0x4c, 0x2a, 0x02, + 0x6f, 0xda, 0xa3, 0xb9, 0xbc, 0xdc, 0x19, 0xc1, 0x79, 0xe3, 0x1a, 0x29, 0xb7, 0x8c, 0x12, 0x61, + 0x95, 0xd6, 0x20, 0x17, 0xe9, 0x2c, 0xf9, 0x07, 0x92, 0xae, 0x2d, 0x7c, 0x78, 0x14, 0xf7, 0xe8, + 0xf6, 0xb8, 0xaf, 0xea, 0x3d, 0x14, 0x13, 0x5e, 0x8f, 0x79, 0xb0, 0x9e, 0x58, 0x15, 0xa4, 0x03, + 0xc8, 0xf8, 0xbd, 0x22, 0x04, 0x52, 0xa6, 0x6a, 0xa0, 0x8f, 0xe3, 0x67, 0x22, 0x43, 0xc6, 0xb2, + 0x19, 0x97, 0x9e, 0xf8, 0x49, 0xe7, 0x82, 0x24, 0xe9, 0x19, 0xfc, 0x3d, 0x26, 0x37, 0x46, 0xcd, + 0xad, 0xa8, 0x9a, 0x69, 0x94, 0xa1, 0xc6, 0xca, 0x1d, 0xf8, 0x77, 0x0f, 0xd9, 0xc8, 0x10, 0x05, + 0xdf, 0xf6, 0x90, 0xb2, 0x69, 0x2b, 0x52, 0x39, 0x81, 0xfc, 0xe5, 0x74, 0x6a, 0x5b, 0x26, 0x45, + 0x72, 0x08, 0xd9, 0x91, 0xc5, 0x1c, 0x96, 0xab, 0xdf, 0x9e, 0xa7, 0x11, 0xbe, 0xf1, 0x21, 0x49, + 0x65, 0x05, 0xfe, 0xdb, 0xd7, 0x68, 0x58, 0x8a, 0x06, 0xd2, 0x44, 0xc8, 0x1c, 0x6b, 0x3a, 0x43, + 0x87, 0x8a, 0x42, 0x39, 0x59, 0xcd, 0x2a, 0x41, 0x58, 0xd1, 0xa1, 0x30, 0x0e, 0xf1, 0xe5, 0x29, + 0x00, 0x61, 0x61, 0x0e, 0xbb, 0x9a, 0xbe, 0x08, 0x4b, 0xe5, 0x0d, 0x14, 0xb6, 0xf8, 0x38, 0x4f, + 0x98, 0xf7, 0xe7, 0xcd, 0xe8, 0xc2, 0xff, 0x13, 0xb5, 0xae, 0xcd, 0xf9, 0x2f, 0x02, 0x14, 0x5e, + 0xf0, 0x1d, 0xbb, 0xfe, 0x6f, 0x46, 0x36, 0x20, 0xe7, 0xed, 0x33, 0x7f, 0xcf, 0xfd, 0xa9, 0x9d, + 0x7c, 0x08, 0x76, 0xdd, 0x27, 0xff, 0x40, 0xa5, 0x5d, 0xc5, 0x7f, 0x36, 0xdc, 0xb3, 0x6b, 0xcb, + 0x84, 0xd0, 0x6b, 0xb3, 0xe5, 0x2e, 0x14, 0xb6, 0x51, 0xc7, 0x18, 0x57, 0xa6, 0x2c, 0x4b, 0xfd, + 0x3c, 0x05, 0x10, 0x0e, 0x23, 0xe9, 0x43, 0x72, 0x0f, 0x19, 0x79, 0x30, 0x83, 0x8c, 0x98, 0x95, + 0x94, 0x1e, 0xce, 0x8d, 0xf3, 0xad, 0x78, 0x0f, 0x29, 0x77, 0x2d, 0xc8, 0x2c, 0x3f, 0x67, 0xb1, + 0x2b, 0x27, 0xad, 0x5d, 0x01, 0xe9, 0x17, 0xff, 0x00, 0x69, 0x6f, 0x72, 0xc9, 0x2c, 0x24, 0xf1, + 0x0b, 0x25, 0xad, 0x5f, 0x05, 0x1a, 0x0a, 0xf0, 0x66, 0x64, 0x26, 0x01, 0xf1, 0x73, 0x3f, 0x93, + 0x80, 0x69, 0x93, 0xf8, 0x0a, 0xd2, 0xde, 0xdc, 0xcc, 0x24, 0x20, 0x7e, 0xc4, 0xa4, 0xc2, 0xc4, + 0x46, 0xec, 0xb8, 0xff, 0x90, 0x1a, 0xaf, 0x4f, 0x2f, 0x8a, 0x0b, 0xdf, 0x2e, 0x8a, 0x0b, 0x1f, + 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, } diff --git a/api/services/containers/v1/containers.proto b/api/services/containers/v1/containers.proto index a2c3e32d2..cfc7ebdea 100644 --- a/api/services/containers/v1/containers.proto +++ b/api/services/containers/v1/containers.proto @@ -89,7 +89,12 @@ message Container { // associated with the container. One may provide protobuf, json, or other // encoding formats. The primary use of this is to further decorate the // 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 extensions = 10 [(gogoproto.nullable) = false]; } message GetContainerRequest { diff --git a/container.go b/container.go index 468140ff4..af42cd44c 100644 --- a/container.go +++ b/container.go @@ -44,7 +44,7 @@ type Container interface { // SetLabels sets the provided labels for the container and returns the final label set SetLabels(context.Context, map[string]string) (map[string]string, error) // Extensions returns the extensions set on the container - Extensions() []prototypes.Any + Extensions() map[string]prototypes.Any } func containerFromRecord(client *Client, c containers.Container) *container { @@ -161,7 +161,7 @@ func (c *container) Image(ctx context.Context) (Image, error) { }, nil } -func (c *container) Extensions() []prototypes.Any { +func (c *container) Extensions() map[string]prototypes.Any { c.mu.Lock() defer c.mu.Unlock() return c.c.Extensions diff --git a/container_opts.go b/container_opts.go index 9087761a0..58bb99a85 100644 --- a/container_opts.go +++ b/container_opts.go @@ -135,13 +135,20 @@ func setSnapshotterIfEmpty(c *containers.Container) { // Make sure to register the type of `extension` in the typeurl package via // `typeurl.Register` otherwise the type data will be inferred, including how // 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 { any, err := typeurl.MarshalAny(extension) if err != nil { 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 } } diff --git a/container_test.go b/container_test.go index 8ca2c5925..2816f1310 100644 --- a/container_test.go +++ b/container_test.go @@ -1384,7 +1384,7 @@ func TestContainerExtensions(t *testing.T) { defer client.Close() 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 { t.Fatal(err) } @@ -1395,11 +1395,11 @@ func TestContainerExtensions(t *testing.T) { if len(cExts) != 1 { t.Fatal("expected 1 container extension") } - if cExts[0].TypeUrl != ext.TypeUrl { - t.Fatalf("got unexpected type url for extension: %s", cExts[0].TypeUrl) + if cExts["hello"].TypeUrl != ext.TypeUrl { + t.Fatalf("got unexpected type url for extension: %s", cExts["hello"].TypeUrl) } - if !bytes.Equal(cExts[0].Value, ext.Value) { - t.Fatalf("expected extension value %q, got: %q", ext.Value, cExts[0].Value) + if !bytes.Equal(cExts["hello"].Value, ext.Value) { + t.Fatalf("expected extension value %q, got: %q", ext.Value, cExts["hello"].Value) } } diff --git a/containers/containers.go b/containers/containers.go index 4491a70d6..1ea593bdf 100644 --- a/containers/containers.go +++ b/containers/containers.go @@ -59,7 +59,7 @@ type Container struct { UpdatedAt time.Time // Extensions stores client-specified metadata - Extensions []types.Any + Extensions map[string]types.Any } type RuntimeInfo struct { diff --git a/metadata/containers.go b/metadata/containers.go index 026a9b5f6..90a33e553 100644 --- a/metadata/containers.go +++ b/metadata/containers.go @@ -181,6 +181,15 @@ func (s *containerStore) Update(ctx context.Context, container containers.Contai 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 { case "labels": updated.Labels = container.Labels @@ -228,6 +237,12 @@ func validateContainer(container *containers.Container) 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 if container.Runtime.Name == "" { 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): container.Snapshotter = string(v) case string(bucketKeyExtensions): - buf := proto.NewBuffer(v) - n, err := buf.DecodeVarint() - if err != nil { - return errors.Wrap(err, "error reading number of container extensions") + ebkt := bkt.Bucket(bucketKeyExtensions) + if ebkt == nil { + return nil } - extensions := make([]types.Any, 0, n) - for i := 0; i < int(n); i++ { - var any types.Any - if err := buf.DecodeMessage(&any); err != nil { - return errors.Wrap(err, "error decoding container extension") + + extensions := make(map[string]types.Any) + if err := ebkt.ForEach(func(k, v []byte) error { + var a types.Any + 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 } @@ -352,18 +373,21 @@ func writeContainer(bkt *bolt.Bucket, container *containers.Container) error { return err } - if container.Extensions != nil { - buf := proto.NewBuffer(nil) - if err := buf.EncodeVarint(uint64(len(container.Extensions))); err != nil { + if len(container.Extensions) > 0 { + ebkt, err := bkt.CreateBucketIfNotExists(bucketKeyExtensions) + if err != nil { 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 } - } - if err := bkt.Put(bucketKeyExtensions, buf.Bytes()); err != nil { - return err } } diff --git a/metadata/containers_test.go b/metadata/containers_test.go index d99269308..35411d5c8 100644 --- a/metadata/containers_test.go +++ b/metadata/containers_test.go @@ -428,8 +428,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) { Runtime: containers.RuntimeInfo{ Name: "testruntime", }, - Extensions: []types.Any{ - { + Extensions: map[string]types.Any{ + "hello": { TypeUrl: "test.update.extensions", Value: []byte("hello"), }, @@ -440,8 +440,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) { Runtime: containers.RuntimeInfo{ Name: "testruntime", }, - Extensions: []types.Any{ - { + Extensions: map[string]types.Any{ + "hello": { TypeUrl: "test.update.extensions", Value: []byte("world"), }, @@ -452,8 +452,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) { Runtime: containers.RuntimeInfo{ Name: "testruntime", }, - Extensions: []types.Any{ - { + Extensions: map[string]types.Any{ + "hello": { TypeUrl: "test.update.extensions", Value: []byte("world"), }, @@ -467,8 +467,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) { Runtime: containers.RuntimeInfo{ Name: "testruntime", }, - Extensions: []types.Any{ - { + Extensions: map[string]types.Any{ + "hello": { TypeUrl: "test.update.extensions", Value: []byte("hello"), }, @@ -479,8 +479,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) { Runtime: containers.RuntimeInfo{ Name: "testruntime", }, - Extensions: []types.Any{ - { + Extensions: map[string]types.Any{ + "hello": { TypeUrl: "test.update.extensions", Value: []byte("world"), }, @@ -492,8 +492,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) { Runtime: containers.RuntimeInfo{ Name: "testruntime", }, - Extensions: []types.Any{ - { + Extensions: map[string]types.Any{ + "hello": { TypeUrl: "test.update.extensions", Value: []byte("hello"), }, @@ -507,8 +507,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) { Runtime: containers.RuntimeInfo{ Name: "testruntime", }, - Extensions: []types.Any{ - { + Extensions: map[string]types.Any{ + "hello": { TypeUrl: "test.update.extensions", Value: []byte("hello"), }, @@ -518,8 +518,8 @@ func TestContainersCreateUpdateDelete(t *testing.T) { Labels: map[string]string{ "foo": "one", }, - Extensions: []types.Any{ - { + Extensions: map[string]types.Any{ + "hello": { TypeUrl: "test.update.extensions", Value: []byte("world"), }, @@ -531,14 +531,59 @@ func TestContainersCreateUpdateDelete(t *testing.T) { Runtime: containers.RuntimeInfo{ Name: "testruntime", }, - Extensions: []types.Any{ - { + Extensions: map[string]types.Any{ + "hello": { TypeUrl: "test.update.extensions", 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) { 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{}) { 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...)...) } }