From 3552ce5688d736c5c311f5ef802bbd279ba64865 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Thu, 17 Aug 2017 15:22:46 -0400 Subject: [PATCH 1/2] Add field to `Container` for client-defined data This field allows a client to store specialized information in the container metadata rather than having to store this itself and keep the data in sync with containerd. Signed-off-by: Brian Goff --- api/next.pb.txt | 11 ++ api/services/containers/v1/containers.pb.go | 150 ++++++++++++++------ api/services/containers/v1/containers.proto | 6 + container.go | 9 ++ container_opts.go | 18 +++ container_test.go | 43 ++++++ containers/containers.go | 3 + containerstore.go | 2 + metadata/buckets.go | 1 + metadata/containers.go | 34 ++++- metadata/containers_test.go | 119 ++++++++++++++++ services/containers/helpers.go | 2 + 12 files changed, 350 insertions(+), 48 deletions(-) diff --git a/api/next.pb.txt b/api/next.pb.txt index de855c830..a1ea79a93 100755 --- a/api/next.pb.txt +++ b/api/next.pb.txt @@ -185,6 +185,17 @@ file { } json_name: "updatedAt" } + field { + name: "extensions" + number: 10 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".google.protobuf.Any" + options { + 65001: 0 + } + json_name: "extensions" + } nested_type { name: "LabelsEntry" field { diff --git a/api/services/containers/v1/containers.pb.go b/api/services/containers/v1/containers.pb.go index 74b2bbe07..e86882fa3 100644 --- a/api/services/containers/v1/containers.pb.go +++ b/api/services/containers/v1/containers.pb.go @@ -94,6 +94,11 @@ type Container struct { CreatedAt time.Time `protobuf:"bytes,8,opt,name=created_at,json=createdAt,stdtime" json:"created_at"` // UpdatedAt is the last time the container was mutated. UpdatedAt time.Time `protobuf:"bytes,9,opt,name=updated_at,json=updatedAt,stdtime" json:"updated_at"` + // Extensions allow clients to provide zero or more blobs that are directly + // 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"` } func (m *Container) Reset() { *m = Container{} } @@ -518,6 +523,18 @@ func (m *Container) MarshalTo(dAtA []byte) (int, error) { return 0, err } i += n4 + if len(m.Extensions) > 0 { + for _, msg := range m.Extensions { + dAtA[i] = 0x52 + i++ + i = encodeVarintContainers(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } return i, nil } @@ -872,6 +889,12 @@ func (m *Container) Size() (n int) { n += 1 + l + sovContainers(uint64(l)) 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)) + } + } return n } @@ -1014,6 +1037,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) + `,`, `}`, }, "") return s @@ -1515,6 +1539,37 @@ func (m *Container) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Extensions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainers + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthContainers + } + postIndex := iNdEx + msglen + 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 + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipContainers(dAtA[iNdEx:]) @@ -2509,51 +2564,52 @@ func init() { } var fileDescriptorContainers = []byte{ - // 730 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcb, 0x72, 0x12, 0x41, - 0x14, 0xcd, 0xc0, 0x04, 0xc2, 0xc5, 0x85, 0xd5, 0x22, 0x8e, 0x63, 0x15, 0x10, 0x56, 0x2c, 0x74, - 0x30, 0x68, 0x69, 0x1e, 0xab, 0x90, 0x57, 0x59, 0x26, 0x56, 0xaa, 0x4b, 0x37, 0xba, 0x88, 0x0d, - 0x74, 0xc8, 0xc8, 0xbc, 0x9c, 0x6e, 0xa8, 0xa2, 0x5c, 0xe8, 0x27, 0xf8, 0x17, 0xfe, 0x4a, 0x96, - 0x2e, 0x5d, 0xc5, 0x84, 0x2f, 0xb1, 0xba, 0x67, 0x26, 0x43, 0x78, 0x94, 0x10, 0x65, 0x77, 0x2f, - 0x7d, 0xcf, 0xbd, 0x67, 0x4e, 0x9f, 0x3b, 0x0c, 0x1c, 0xb6, 0x4d, 0x7e, 0xd6, 0x6d, 0x18, 0x4d, - 0xd7, 0xae, 0x36, 0x5d, 0x87, 0x13, 0xd3, 0xa1, 0x7e, 0x6b, 0x38, 0x24, 0x9e, 0x59, 0x65, 0xd4, - 0xef, 0x99, 0x4d, 0xca, 0xe2, 0xdf, 0x59, 0xb5, 0xb7, 0x36, 0x94, 0x19, 0x9e, 0xef, 0x72, 0x17, - 0xad, 0xc6, 0x38, 0x23, 0xc2, 0x18, 0x43, 0x55, 0xbd, 0x35, 0x3d, 0xd7, 0x76, 0xdb, 0xae, 0xac, - 0xae, 0x8a, 0x28, 0x00, 0xea, 0x0f, 0xdb, 0xae, 0xdb, 0xb6, 0x68, 0x55, 0x66, 0x8d, 0xee, 0x69, - 0x95, 0x38, 0xfd, 0xf0, 0xe8, 0xd1, 0xe8, 0x11, 0xb5, 0x3d, 0x1e, 0x1d, 0x96, 0x46, 0x0f, 0x4f, - 0x4d, 0x6a, 0xb5, 0x4e, 0x6c, 0xc2, 0x3a, 0x61, 0x45, 0x71, 0xb4, 0x82, 0x9b, 0x36, 0x65, 0x9c, - 0xd8, 0x5e, 0x50, 0x50, 0xbe, 0x50, 0x21, 0xb3, 0x13, 0x51, 0x44, 0x79, 0x48, 0x98, 0x2d, 0x4d, - 0x29, 0x29, 0x95, 0x4c, 0x3d, 0x35, 0xb8, 0x28, 0x26, 0x5e, 0xed, 0xe2, 0x84, 0xd9, 0x42, 0xc7, - 0x90, 0xb2, 0x48, 0x83, 0x5a, 0x4c, 0x4b, 0x94, 0x92, 0x95, 0x6c, 0x6d, 0xdd, 0xf8, 0xeb, 0xa3, - 0x1a, 0xd7, 0x5d, 0x8d, 0x43, 0x09, 0xdd, 0x73, 0xb8, 0xdf, 0xc7, 0x61, 0x1f, 0x94, 0x83, 0x65, - 0xd3, 0x26, 0x6d, 0xaa, 0x25, 0xc5, 0x30, 0x1c, 0x24, 0xe8, 0x0d, 0xa4, 0xfd, 0xae, 0x23, 0x38, - 0x6a, 0x6a, 0x49, 0xa9, 0x64, 0x6b, 0xcf, 0xe7, 0x1a, 0x84, 0x03, 0x2c, 0x8e, 0x9a, 0xa0, 0x0a, - 0xa8, 0xcc, 0xa3, 0x4d, 0x6d, 0x59, 0x36, 0xcb, 0x19, 0x81, 0x1a, 0x46, 0xa4, 0x86, 0xb1, 0xed, - 0xf4, 0xb1, 0xac, 0x40, 0x25, 0xc8, 0x32, 0x87, 0x78, 0xec, 0xcc, 0xe5, 0x9c, 0xfa, 0x5a, 0x4a, - 0xb2, 0x1a, 0xfe, 0x09, 0xad, 0xc2, 0x9d, 0x28, 0x3d, 0xe9, 0xd0, 0xbe, 0x96, 0xbe, 0x59, 0xf2, - 0x9a, 0xf6, 0xd1, 0x0e, 0x40, 0xd3, 0xa7, 0x84, 0xd3, 0xd6, 0x09, 0xe1, 0xda, 0x8a, 0x1c, 0xaa, - 0x8f, 0x0d, 0x7d, 0x1b, 0x5d, 0x41, 0x7d, 0xe5, 0xfc, 0xa2, 0xb8, 0xf4, 0xfd, 0x77, 0x51, 0xc1, - 0x99, 0x10, 0xb7, 0xcd, 0x45, 0x93, 0xae, 0xd7, 0x8a, 0x9a, 0x64, 0xe6, 0x69, 0x12, 0xe2, 0xb6, - 0xb9, 0xbe, 0x01, 0xd9, 0x21, 0xd5, 0xd1, 0x5d, 0x48, 0x0a, 0xca, 0xf2, 0x62, 0xb1, 0x08, 0x85, - 0xfe, 0x3d, 0x62, 0x75, 0xa9, 0x96, 0x08, 0xf4, 0x97, 0xc9, 0x66, 0x62, 0x5d, 0xd1, 0x8f, 0x20, - 0x1d, 0xea, 0x88, 0x10, 0xa8, 0x0e, 0xb1, 0x69, 0x88, 0x93, 0x31, 0x32, 0x20, 0xed, 0x7a, 0xdc, - 0x74, 0x1d, 0x26, 0xa1, 0xd3, 0x54, 0x8d, 0x8a, 0xca, 0x4f, 0xe0, 0xde, 0x01, 0xe5, 0xd7, 0x77, - 0x84, 0xe9, 0xe7, 0x2e, 0x65, 0x7c, 0x9a, 0xd3, 0xca, 0x67, 0x90, 0xbb, 0x59, 0xce, 0x3c, 0xd7, - 0x61, 0x14, 0x1d, 0x43, 0xe6, 0xfa, 0xd6, 0x25, 0x2c, 0x5b, 0x7b, 0x3c, 0x8f, 0x37, 0xea, 0xaa, - 0x90, 0x09, 0xc7, 0x4d, 0xca, 0x6b, 0x70, 0xff, 0xd0, 0x64, 0xf1, 0x28, 0x16, 0x51, 0xd3, 0x20, - 0x7d, 0x6a, 0x5a, 0x9c, 0xfa, 0x4c, 0x53, 0x4a, 0xc9, 0x4a, 0x06, 0x47, 0x69, 0xd9, 0x82, 0xfc, - 0x28, 0x24, 0xa4, 0x87, 0x01, 0xe2, 0xc1, 0x12, 0x76, 0x3b, 0x7e, 0x43, 0x5d, 0xca, 0x9f, 0x20, - 0xbf, 0x23, 0x5d, 0x31, 0x26, 0xde, 0xff, 0x17, 0xa3, 0x03, 0x0f, 0xc6, 0x66, 0x2d, 0x4c, 0xf9, - 0x1f, 0x0a, 0xe4, 0xdf, 0x49, 0xab, 0x2e, 0xfe, 0xc9, 0xd0, 0x16, 0x64, 0x83, 0xb5, 0x90, 0xaf, - 0xc5, 0xd0, 0xb3, 0xe3, 0xfb, 0xb4, 0x2f, 0xde, 0x9c, 0x47, 0x84, 0x75, 0x70, 0xb8, 0x7d, 0x22, - 0x16, 0xb2, 0x8c, 0x11, 0x5d, 0x98, 0x2c, 0x4f, 0x21, 0xbf, 0x4b, 0x2d, 0x3a, 0x41, 0x95, 0x29, - 0xcb, 0x52, 0xbb, 0x54, 0x01, 0x62, 0x33, 0xa2, 0x1e, 0x24, 0x0f, 0x28, 0x47, 0x2f, 0x66, 0xa0, - 0x31, 0x61, 0x25, 0xf5, 0x97, 0x73, 0xe3, 0x42, 0x29, 0xbe, 0x80, 0x2a, 0xd6, 0x02, 0xcd, 0xf2, - 0xaf, 0x30, 0x71, 0xe5, 0xf4, 0x8d, 0x5b, 0x20, 0xc3, 0xe1, 0x5f, 0x21, 0x15, 0x38, 0x17, 0xcd, - 0xd2, 0x64, 0xf2, 0x42, 0xe9, 0x9b, 0xb7, 0x81, 0xc6, 0x04, 0x02, 0x8f, 0xcc, 0x44, 0x60, 0xb2, - 0xef, 0x67, 0x22, 0x30, 0xcd, 0x89, 0x1f, 0x20, 0x15, 0xf8, 0x66, 0x26, 0x02, 0x93, 0x2d, 0xa6, - 0xe7, 0xc7, 0x36, 0x62, 0x4f, 0x7c, 0x68, 0xd4, 0x3f, 0x9e, 0x5f, 0x15, 0x96, 0x7e, 0x5d, 0x15, - 0x96, 0xbe, 0x0d, 0x0a, 0xca, 0xf9, 0xa0, 0xa0, 0xfc, 0x1c, 0x14, 0x94, 0xcb, 0x41, 0x41, 0x79, - 0xbf, 0xff, 0x0f, 0xdf, 0x4e, 0x5b, 0x71, 0xd6, 0x48, 0xc9, 0x89, 0xcf, 0xfe, 0x04, 0x00, 0x00, - 0xff, 0xff, 0x7e, 0x6d, 0xca, 0xbd, 0x8c, 0x09, 0x00, 0x00, + // 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, } diff --git a/api/services/containers/v1/containers.proto b/api/services/containers/v1/containers.proto index 572ecde12..a2c3e32d2 100644 --- a/api/services/containers/v1/containers.proto +++ b/api/services/containers/v1/containers.proto @@ -84,6 +84,12 @@ message Container { // UpdatedAt is the last time the container was mutated. google.protobuf.Timestamp updated_at = 9 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + + // Extensions allow clients to provide zero or more blobs that are directly + // 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]; } message GetContainerRequest { diff --git a/container.go b/container.go index a9657d1e9..468140ff4 100644 --- a/container.go +++ b/container.go @@ -12,6 +12,7 @@ import ( "github.com/containerd/containerd/containers" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/typeurl" + prototypes "github.com/gogo/protobuf/types" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) @@ -42,6 +43,8 @@ type Container interface { Labels(context.Context) (map[string]string, error) // 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 } func containerFromRecord(client *Client, c containers.Container) *container { @@ -158,6 +161,12 @@ func (c *container) Image(ctx context.Context) (Image, error) { }, nil } +func (c *container) Extensions() []prototypes.Any { + c.mu.Lock() + defer c.mu.Unlock() + return c.c.Extensions +} + func (c *container) NewTask(ctx context.Context, ioCreate IOCreation, opts ...NewTaskOpts) (Task, error) { c.mu.Lock() defer c.mu.Unlock() diff --git a/container_opts.go b/container_opts.go index 008adfca2..9087761a0 100644 --- a/container_opts.go +++ b/container_opts.go @@ -127,3 +127,21 @@ func setSnapshotterIfEmpty(c *containers.Container) { c.Snapshotter = DefaultSnapshotter } } + +// WithContainerExtension appends extension data to the container object. +// Use this to decorate the container object with additional data for the client +// integration. +// +// 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 { + 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) + return nil + } +} diff --git a/container_test.go b/container_test.go index cbae4e67c..8ca2c5925 100644 --- a/container_test.go +++ b/container_test.go @@ -14,6 +14,7 @@ import ( _ "github.com/containerd/containerd/runtime" "github.com/containerd/containerd/errdefs" + gogotypes "github.com/gogo/protobuf/types" ) func empty() IOCreation { @@ -1368,3 +1369,45 @@ func TestContainerMetrics(t *testing.T) { <-statusC } + +func TestContainerExtensions(t *testing.T) { + t.Parallel() + + ctx, cancel := testContext() + defer cancel() + id := t.Name() + + client, err := newClient(t, address) + if err != nil { + t.Fatal(err) + } + defer client.Close() + + ext := gogotypes.Any{TypeUrl: "test.ext.url", Value: []byte("hello")} + container, err := client.NewContainer(ctx, id, WithNewSpec(), WithContainerExtension(&ext)) + if err != nil { + t.Fatal(err) + } + defer container.Delete(ctx) + + checkExt := func(container Container) { + cExts := container.Extensions() + 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 !bytes.Equal(cExts[0].Value, ext.Value) { + t.Fatalf("expected extension value %q, got: %q", ext.Value, cExts[0].Value) + } + } + + checkExt(container) + + container, err = client.LoadContainer(ctx, container.ID()) + if err != nil { + t.Fatal(err) + } + checkExt(container) +} diff --git a/containers/containers.go b/containers/containers.go index 6ad161f8f..4491a70d6 100644 --- a/containers/containers.go +++ b/containers/containers.go @@ -57,6 +57,9 @@ type Container struct { // UpdatedAt is the time at which the container was updated. UpdatedAt time.Time + + // Extensions stores client-specified metadata + Extensions []types.Any } type RuntimeInfo struct { diff --git a/containerstore.go b/containerstore.go index 8d75fbe9e..7647ba1b6 100644 --- a/containerstore.go +++ b/containerstore.go @@ -97,6 +97,7 @@ func containerToProto(container *containers.Container) containersapi.Container { Spec: container.Spec, Snapshotter: container.Snapshotter, SnapshotKey: container.SnapshotKey, + Extensions: container.Extensions, } } @@ -116,6 +117,7 @@ func containerFromProto(containerpb *containersapi.Container) containers.Contain Spec: containerpb.Spec, Snapshotter: containerpb.Snapshotter, SnapshotKey: containerpb.SnapshotKey, + Extensions: containerpb.Extensions, } } diff --git a/metadata/buckets.go b/metadata/buckets.go index 57dcb89e5..6097cbf8f 100644 --- a/metadata/buckets.go +++ b/metadata/buckets.go @@ -50,6 +50,7 @@ var ( bucketKeySnapshotKey = []byte("snapshotKey") bucketKeySnapshotter = []byte("snapshotter") bucketKeyTarget = []byte("target") + bucketKeyExtensions = []byte("extensions") ) func getBucket(tx *bolt.Tx, keys ...[]byte) *bolt.Bucket { diff --git a/metadata/containers.go b/metadata/containers.go index 2f384c45a..026a9b5f6 100644 --- a/metadata/containers.go +++ b/metadata/containers.go @@ -146,7 +146,7 @@ func (s *containerStore) Update(ctx context.Context, container containers.Contai if len(fieldpaths) == 0 { // only allow updates to these field on full replace. - fieldpaths = []string{"labels", "spec"} + fieldpaths = []string{"labels", "spec", "extensions"} // Fields that are immutable must cause an error when no field paths // are provided. This allows these fields to become mutable in the @@ -186,6 +186,8 @@ func (s *containerStore) Update(ctx context.Context, container containers.Contai updated.Labels = container.Labels case "spec": updated.Spec = container.Spec + case "extensions": + updated.Extensions = container.Extensions default: return containers.Container{}, errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on %q", path, container.ID) } @@ -288,6 +290,21 @@ func readContainer(container *containers.Container, bkt *bolt.Bucket) error { container.SnapshotKey = string(v) 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") + } + 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 = append(extensions, any) + } + container.Extensions = extensions } return nil @@ -335,6 +352,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 { + return err + } + for _, ext := range container.Extensions { + if err := buf.EncodeMessage(&ext); err != nil { + return err + } + } + if err := bkt.Put(bucketKeyExtensions, buf.Bytes()); err != nil { + return err + } + } + if container.Runtime.Options != nil { data, err := proto.Marshal(container.Runtime.Options) if err != nil { diff --git a/metadata/containers_test.go b/metadata/containers_test.go index 11233c409..d99269308 100644 --- a/metadata/containers_test.go +++ b/metadata/containers_test.go @@ -16,6 +16,7 @@ import ( "github.com/containerd/containerd/filters" "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/typeurl" + "github.com/gogo/protobuf/types" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) @@ -420,6 +421,124 @@ func TestContainersCreateUpdateDelete(t *testing.T) { }, createerr: errdefs.ErrInvalidArgument, }, + { + name: "UpdateExtensionsFull", + original: containers.Container{ + Spec: encoded, + Runtime: containers.RuntimeInfo{ + Name: "testruntime", + }, + Extensions: []types.Any{ + { + TypeUrl: "test.update.extensions", + Value: []byte("hello"), + }, + }, + }, + input: containers.Container{ + Spec: encoded, + Runtime: containers.RuntimeInfo{ + Name: "testruntime", + }, + Extensions: []types.Any{ + { + TypeUrl: "test.update.extensions", + Value: []byte("world"), + }, + }, + }, + expected: containers.Container{ + Spec: encoded, + Runtime: containers.RuntimeInfo{ + Name: "testruntime", + }, + Extensions: []types.Any{ + { + TypeUrl: "test.update.extensions", + Value: []byte("world"), + }, + }, + }, + }, + { + name: "UpdateExtensionsNotInFieldpath", + original: containers.Container{ + Spec: encoded, + Runtime: containers.RuntimeInfo{ + Name: "testruntime", + }, + Extensions: []types.Any{ + { + TypeUrl: "test.update.extensions", + Value: []byte("hello"), + }, + }, + }, + input: containers.Container{ + Spec: encoded, + Runtime: containers.RuntimeInfo{ + Name: "testruntime", + }, + Extensions: []types.Any{ + { + TypeUrl: "test.update.extensions", + Value: []byte("world"), + }, + }, + }, + fieldpaths: []string{"labels"}, + expected: containers.Container{ + Spec: encoded, + Runtime: containers.RuntimeInfo{ + Name: "testruntime", + }, + Extensions: []types.Any{ + { + TypeUrl: "test.update.extensions", + Value: []byte("hello"), + }, + }, + }, + }, + { + name: "UpdateExtensionsFieldPath", + original: containers.Container{ + Spec: encoded, + Runtime: containers.RuntimeInfo{ + Name: "testruntime", + }, + Extensions: []types.Any{ + { + TypeUrl: "test.update.extensions", + Value: []byte("hello"), + }, + }, + }, + input: containers.Container{ + Labels: map[string]string{ + "foo": "one", + }, + Extensions: []types.Any{ + { + TypeUrl: "test.update.extensions", + Value: []byte("world"), + }, + }, + }, + fieldpaths: []string{"extensions"}, + expected: containers.Container{ + Spec: encoded, + Runtime: containers.RuntimeInfo{ + Name: "testruntime", + }, + Extensions: []types.Any{ + { + TypeUrl: "test.update.extensions", + Value: []byte("world"), + }, + }, + }, + }, } { t.Run(testcase.name, func(t *testing.T) { testcase.original.ID = testcase.name diff --git a/services/containers/helpers.go b/services/containers/helpers.go index d648d1bc4..f6a4c3f09 100644 --- a/services/containers/helpers.go +++ b/services/containers/helpers.go @@ -27,6 +27,7 @@ func containerToProto(container *containers.Container) api.Container { Spec: container.Spec, Snapshotter: container.Snapshotter, SnapshotKey: container.SnapshotKey, + Extensions: container.Extensions, } } @@ -46,5 +47,6 @@ func containerFromProto(containerpb *api.Container) containers.Container { Spec: containerpb.Spec, Snapshotter: containerpb.Snapshotter, SnapshotKey: containerpb.SnapshotKey, + Extensions: containerpb.Extensions, } } From f7d31e2be40702c70b081f2d0cb471b35c58bcc8 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Fri, 8 Sep 2017 14:51:30 -0700 Subject: [PATCH 2/2] 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...)...) } }