cmd/ctr, service/containers: implement container filter

Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
Stephen J Day 2017-06-21 15:29:58 -07:00
parent 3332042ab6
commit 396d89e423
No known key found for this signature in database
GPG Key ID: 67B3DED84EDC823F
16 changed files with 409 additions and 189 deletions

View File

@ -125,7 +125,17 @@ func (*GetContainerResponse) ProtoMessage() {}
func (*GetContainerResponse) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{2} } func (*GetContainerResponse) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{2} }
type ListContainersRequest struct { type ListContainersRequest struct {
Filter string `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` // Filters contains one or more filters using the syntax defined in the
// containerd filter package.
//
// The returned result will be those that match any of the provided
// filters. Expanded, containers that match the following will be
// returned:
//
// filters[0] or filters[1] or ... or filters[n-1] or filters[n]
//
// If filters is zero-length or nil, all items will be returned.
Filters []string `protobuf:"bytes,1,rep,name=filters" json:"filters,omitempty"`
} }
func (m *ListContainersRequest) Reset() { *m = ListContainersRequest{} } func (m *ListContainersRequest) Reset() { *m = ListContainersRequest{} }
@ -601,11 +611,20 @@ func (m *ListContainersRequest) MarshalTo(dAtA []byte) (int, error) {
_ = i _ = i
var l int var l int
_ = l _ = l
if len(m.Filter) > 0 { if len(m.Filters) > 0 {
dAtA[i] = 0xa for _, s := range m.Filters {
i++ dAtA[i] = 0xa
i = encodeVarintContainers(dAtA, i, uint64(len(m.Filter))) i++
i += copy(dAtA[i:], m.Filter) l = len(s)
for l >= 1<<7 {
dAtA[i] = uint8(uint64(l)&0x7f | 0x80)
l >>= 7
i++
}
dAtA[i] = uint8(l)
i++
i += copy(dAtA[i:], s)
}
} }
return i, nil return i, nil
} }
@ -878,9 +897,11 @@ func (m *GetContainerResponse) Size() (n int) {
func (m *ListContainersRequest) Size() (n int) { func (m *ListContainersRequest) Size() (n int) {
var l int var l int
_ = l _ = l
l = len(m.Filter) if len(m.Filters) > 0 {
if l > 0 { for _, s := range m.Filters {
n += 1 + l + sovContainers(uint64(l)) l = len(s)
n += 1 + l + sovContainers(uint64(l))
}
} }
return n return n
} }
@ -1019,7 +1040,7 @@ func (this *ListContainersRequest) String() string {
return "nil" return "nil"
} }
s := strings.Join([]string{`&ListContainersRequest{`, s := strings.Join([]string{`&ListContainersRequest{`,
`Filter:` + fmt.Sprintf("%v", this.Filter) + `,`, `Filters:` + fmt.Sprintf("%v", this.Filters) + `,`,
`}`, `}`,
}, "") }, "")
return s return s
@ -1774,7 +1795,7 @@ func (m *ListContainersRequest) Unmarshal(dAtA []byte) error {
switch fieldNum { switch fieldNum {
case 1: case 1:
if wireType != 2 { if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Filter", wireType) return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType)
} }
var stringLen uint64 var stringLen uint64
for shift := uint(0); ; shift += 7 { for shift := uint(0); ; shift += 7 {
@ -1799,7 +1820,7 @@ func (m *ListContainersRequest) Unmarshal(dAtA []byte) error {
if postIndex > l { if postIndex > l {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
m.Filter = string(dAtA[iNdEx:postIndex]) m.Filters = append(m.Filters, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
@ -2445,52 +2466,52 @@ func init() {
} }
var fileDescriptorContainers = []byte{ var fileDescriptorContainers = []byte{
// 738 bytes of a gzipped FileDescriptorProto // 742 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcd, 0x6e, 0xd3, 0x4a, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcd, 0x6e, 0xd3, 0x4a,
0x14, 0xae, 0x93, 0xd4, 0x69, 0x4e, 0x36, 0x57, 0x73, 0x73, 0x73, 0x8d, 0x91, 0x92, 0x90, 0x55, 0x14, 0xae, 0x93, 0xd4, 0x69, 0x4e, 0x36, 0x57, 0x73, 0x73, 0x73, 0x8d, 0x91, 0x92, 0x90, 0x55,
0x16, 0x60, 0xd3, 0x80, 0xa0, 0x3f, 0xab, 0xa6, 0x7f, 0x42, 0x6a, 0x51, 0x35, 0xc0, 0x06, 0x16, 0x16, 0xe0, 0xd0, 0x80, 0xa0, 0x3f, 0xab, 0xa6, 0x7f, 0x42, 0x6a, 0x51, 0x35, 0xc0, 0x06, 0x16,
0xc5, 0x49, 0x26, 0xa9, 0x89, 0xe3, 0x31, 0x9e, 0x49, 0xa4, 0x88, 0x05, 0x3c, 0x02, 0x6f, 0xc1, 0xc5, 0x49, 0x26, 0xa9, 0x89, 0xed, 0x31, 0x9e, 0x49, 0xa4, 0x88, 0x05, 0x3c, 0x02, 0x6f, 0xc1,
0x53, 0xb0, 0xef, 0x92, 0x25, 0xab, 0xd2, 0xe6, 0x49, 0x90, 0xc7, 0xe3, 0x3a, 0xe4, 0x47, 0x38, 0x53, 0xb0, 0xef, 0x92, 0x25, 0xab, 0xd2, 0xe6, 0x49, 0x90, 0xc7, 0xe3, 0x3a, 0xe4, 0x47, 0x38,
0x85, 0xee, 0xe6, 0x78, 0xce, 0xf7, 0x9d, 0x33, 0xdf, 0x7c, 0x67, 0x64, 0x38, 0xea, 0xda, 0xfc, 0x85, 0xee, 0xe6, 0x78, 0xce, 0xf7, 0x9d, 0x33, 0xdf, 0x7c, 0x67, 0x64, 0x38, 0xea, 0x59, 0xfc,
0x6c, 0xd0, 0x34, 0x5a, 0xb4, 0x6f, 0xb6, 0xa8, 0xcb, 0x2d, 0xdb, 0x25, 0x7e, 0x7b, 0x72, 0x69, 0x6c, 0xd0, 0x32, 0xda, 0xd4, 0xa9, 0xb7, 0xa9, 0xcb, 0x4d, 0xcb, 0x25, 0x7e, 0x67, 0x72, 0x69,
0x79, 0xb6, 0xc9, 0x88, 0x3f, 0xb4, 0x5b, 0x84, 0xc5, 0xdf, 0x99, 0x39, 0x5c, 0x9f, 0x88, 0x0c, 0x7a, 0x56, 0x9d, 0x11, 0x7f, 0x68, 0xb5, 0x09, 0x8b, 0xbf, 0xb3, 0xfa, 0x70, 0x7d, 0x22, 0x32,
0xcf, 0xa7, 0x9c, 0xa2, 0x7b, 0x31, 0xce, 0x88, 0x30, 0xc6, 0x44, 0xd6, 0x70, 0x5d, 0x2f, 0x74, 0x3c, 0x9f, 0x72, 0x8a, 0xee, 0xc5, 0x38, 0x23, 0xc2, 0x18, 0x13, 0x59, 0xc3, 0x75, 0xbd, 0xd0,
0x69, 0x97, 0x8a, 0x6c, 0x33, 0x58, 0x85, 0x40, 0xfd, 0x4e, 0x97, 0xd2, 0xae, 0x43, 0x4c, 0x11, 0xa3, 0x3d, 0x2a, 0xb2, 0xeb, 0xc1, 0x2a, 0x04, 0xea, 0x77, 0x7a, 0x94, 0xf6, 0x6c, 0x52, 0x17,
0x35, 0x07, 0x1d, 0xd3, 0x72, 0x47, 0x72, 0xeb, 0xee, 0xf4, 0x16, 0xe9, 0x7b, 0x3c, 0xda, 0xac, 0x51, 0x6b, 0xd0, 0xad, 0x9b, 0xee, 0x48, 0x6e, 0xdd, 0x9d, 0xde, 0x22, 0x8e, 0xc7, 0xa3, 0xcd,
0x4c, 0x6f, 0x76, 0x6c, 0xe2, 0xb4, 0x4f, 0xfb, 0x16, 0xeb, 0xc9, 0x8c, 0xf2, 0x74, 0x06, 0xb7, 0xca, 0xf4, 0x66, 0xd7, 0x22, 0x76, 0xe7, 0xd4, 0x31, 0x59, 0x5f, 0x66, 0x94, 0xa7, 0x33, 0xb8,
0xfb, 0x84, 0x71, 0xab, 0xef, 0xc9, 0x84, 0xed, 0x44, 0x0a, 0xf0, 0x91, 0x47, 0x98, 0xd9, 0x26, 0xe5, 0x10, 0xc6, 0x4d, 0xc7, 0x93, 0x09, 0xdb, 0x89, 0x14, 0xe0, 0x23, 0x8f, 0xb0, 0x7a, 0x87,
0xac, 0xe5, 0xdb, 0x1e, 0xa7, 0x7e, 0x08, 0xae, 0x7e, 0xcd, 0x40, 0x6e, 0x37, 0xca, 0x44, 0x45, 0xb0, 0xb6, 0x6f, 0x79, 0x9c, 0xfa, 0x21, 0xb8, 0xfa, 0x35, 0x03, 0xb9, 0xdd, 0x28, 0x13, 0x15,
0x48, 0xd9, 0x6d, 0x4d, 0xa9, 0x28, 0xb5, 0x5c, 0x43, 0x1d, 0x5f, 0x94, 0x53, 0xcf, 0xf6, 0x70, 0x21, 0x65, 0x75, 0x34, 0xa5, 0xa2, 0xd4, 0x72, 0x4d, 0x75, 0x7c, 0x51, 0x4e, 0x3d, 0xdb, 0xc3,
0xca, 0x6e, 0xa3, 0x13, 0x50, 0x1d, 0xab, 0x49, 0x1c, 0xa6, 0xa5, 0x2a, 0xe9, 0x5a, 0xbe, 0xbe, 0x29, 0xab, 0x83, 0x4e, 0x40, 0xb5, 0xcd, 0x16, 0xb1, 0x99, 0x96, 0xaa, 0xa4, 0x6b, 0xf9, 0xc6,
0x61, 0xfc, 0x56, 0x27, 0xe3, 0x9a, 0xd5, 0x38, 0x12, 0xd0, 0x7d, 0x97, 0xfb, 0x23, 0x2c, 0x79, 0x86, 0xf1, 0x5b, 0x9d, 0x8c, 0x6b, 0x56, 0xe3, 0x48, 0x40, 0xf7, 0x5d, 0xee, 0x8f, 0xb0, 0xe4,
0x50, 0x01, 0x56, 0xed, 0xbe, 0xd5, 0x25, 0x5a, 0x3a, 0x28, 0x86, 0xc3, 0x00, 0x3d, 0x87, 0xac, 0x41, 0x05, 0x58, 0xb5, 0x1c, 0xb3, 0x47, 0xb4, 0x74, 0x50, 0x0c, 0x87, 0x01, 0x7a, 0x0e, 0x59,
0x3f, 0x70, 0x83, 0x03, 0x6a, 0x99, 0x8a, 0x52, 0xcb, 0xd7, 0x1f, 0x2f, 0x55, 0x08, 0x87, 0x58, 0x7f, 0xe0, 0x06, 0x07, 0xd4, 0x32, 0x15, 0xa5, 0x96, 0x6f, 0x3c, 0x5e, 0xaa, 0x10, 0x0e, 0xb1,
0x1c, 0x91, 0xa0, 0x1a, 0x64, 0x98, 0x47, 0x5a, 0xda, 0xaa, 0x20, 0x2b, 0x18, 0xa1, 0x94, 0x46, 0x38, 0x22, 0x41, 0x35, 0xc8, 0x30, 0x8f, 0xb4, 0xb5, 0x55, 0x41, 0x56, 0x30, 0x42, 0x29, 0x8d,
0x24, 0xa5, 0xb1, 0xe3, 0x8e, 0xb0, 0xc8, 0x40, 0x55, 0x50, 0x7d, 0x4a, 0x79, 0x87, 0x69, 0xaa, 0x48, 0x4a, 0x63, 0xc7, 0x1d, 0x61, 0x91, 0x81, 0xaa, 0xa0, 0xfa, 0x94, 0xf2, 0x2e, 0xd3, 0x54,
0x38, 0x3d, 0x8c, 0x2f, 0xca, 0x2a, 0xa6, 0x94, 0x1f, 0xbc, 0xc0, 0x72, 0x07, 0xed, 0x02, 0xb4, 0x71, 0x7a, 0x18, 0x5f, 0x94, 0x55, 0x4c, 0x29, 0x3f, 0x78, 0x81, 0xe5, 0x0e, 0xda, 0x05, 0x68,
0x7c, 0x62, 0x71, 0xd2, 0x3e, 0xb5, 0xb8, 0x96, 0x15, 0x9c, 0xfa, 0x0c, 0xe7, 0xcb, 0xe8, 0x7a, 0xfb, 0xc4, 0xe4, 0xa4, 0x73, 0x6a, 0x72, 0x2d, 0x2b, 0x38, 0xf5, 0x19, 0xce, 0x97, 0xd1, 0xf5,
0x1a, 0x6b, 0xe7, 0x17, 0xe5, 0x95, 0xcf, 0x3f, 0xca, 0x0a, 0xce, 0x49, 0xdc, 0x0e, 0x0f, 0x48, 0x34, 0xd7, 0xce, 0x2f, 0xca, 0x2b, 0x9f, 0x7f, 0x94, 0x15, 0x9c, 0x93, 0xb8, 0x1d, 0x1e, 0x90,
0x06, 0x5e, 0x3b, 0x22, 0x59, 0x5b, 0x86, 0x44, 0xe2, 0x76, 0xb8, 0xbe, 0x09, 0xf9, 0x09, 0x51, 0x0c, 0xbc, 0x4e, 0x44, 0xb2, 0xb6, 0x0c, 0x89, 0xc4, 0xed, 0x70, 0x7d, 0x13, 0xf2, 0x13, 0xa2,
0xd1, 0x3f, 0x90, 0xee, 0x91, 0x51, 0x78, 0x6f, 0x38, 0x58, 0x06, 0xf2, 0x0e, 0x2d, 0x67, 0x40, 0xa2, 0x7f, 0x20, 0xdd, 0x27, 0xa3, 0xf0, 0xde, 0x70, 0xb0, 0x0c, 0xe4, 0x1d, 0x9a, 0xf6, 0x80,
0xb4, 0x54, 0x28, 0xaf, 0x08, 0xb6, 0x52, 0x1b, 0x8a, 0x7e, 0x0c, 0x59, 0x29, 0x13, 0x42, 0x90, 0x68, 0xa9, 0x50, 0x5e, 0x11, 0x6c, 0xa5, 0x36, 0x14, 0xfd, 0x18, 0xb2, 0x52, 0x26, 0x84, 0x20,
0x71, 0xad, 0x3e, 0x91, 0x38, 0xb1, 0x46, 0x06, 0x64, 0xa9, 0xc7, 0x6d, 0xea, 0x32, 0x01, 0x5d, 0xe3, 0x9a, 0x0e, 0x91, 0x38, 0xb1, 0x46, 0x06, 0x64, 0xa9, 0xc7, 0x2d, 0xea, 0x32, 0x01, 0x5d,
0x24, 0x5a, 0x94, 0x54, 0x7d, 0x00, 0xff, 0x1e, 0x12, 0x7e, 0x7d, 0x05, 0x98, 0xbc, 0x1f, 0x10, 0x24, 0x5a, 0x94, 0x54, 0x7d, 0x00, 0xff, 0x1e, 0x12, 0x7e, 0x7d, 0x05, 0x98, 0xbc, 0x1f, 0x10,
0xc6, 0x17, 0x19, 0xa9, 0x7a, 0x06, 0x85, 0x5f, 0xd3, 0x99, 0x47, 0x5d, 0x46, 0xd0, 0x09, 0xe4, 0xc6, 0x17, 0x19, 0xa9, 0x7a, 0x06, 0x85, 0x5f, 0xd3, 0x99, 0x47, 0x5d, 0x46, 0xd0, 0x09, 0xe4,
0xae, 0x2f, 0x55, 0xc0, 0xf2, 0xf5, 0xfb, 0xcb, 0x5c, 0x7d, 0x23, 0x13, 0xc8, 0x84, 0x63, 0x92, 0xae, 0x2f, 0x55, 0xc0, 0xf2, 0x8d, 0xfb, 0xcb, 0x5c, 0x7d, 0x33, 0x13, 0xc8, 0x84, 0x63, 0x92,
0xaa, 0x09, 0xff, 0x1d, 0xd9, 0x2c, 0x2e, 0xc5, 0xe2, 0xd6, 0xd4, 0x8e, 0xed, 0x70, 0x59, 0x27, 0xea, 0x3a, 0xfc, 0x77, 0x64, 0xb1, 0xb8, 0x14, 0x8b, 0x5a, 0xd3, 0x20, 0xdb, 0xb5, 0x6c, 0x4e,
0x87, 0x65, 0x54, 0x75, 0xa0, 0x38, 0x0d, 0x90, 0xcd, 0x61, 0x80, 0xb8, 0xac, 0xa6, 0x88, 0x09, 0x7c, 0xa6, 0x29, 0x95, 0x74, 0x2d, 0x87, 0xa3, 0xb0, 0x6a, 0x43, 0x71, 0x1a, 0x22, 0xdb, 0xc3,
0xb8, 0x49, 0x77, 0x13, 0x2c, 0xd5, 0x77, 0x50, 0xdc, 0x15, 0x9e, 0x98, 0x91, 0xee, 0xef, 0x4b, 0x00, 0x71, 0x61, 0x01, 0xbb, 0x59, 0x7f, 0x13, 0x2c, 0xd5, 0x77, 0x50, 0xdc, 0x15, 0xae, 0x98,
0xd1, 0x83, 0xff, 0x67, 0x6a, 0xdd, 0x9a, 0xee, 0x5f, 0x14, 0x28, 0xbe, 0x12, 0x46, 0xbd, 0xfd, 0x11, 0xef, 0xef, 0x8b, 0xd1, 0x87, 0xff, 0x67, 0x6a, 0xdd, 0x9a, 0xf2, 0x5f, 0x14, 0x28, 0xbe,
0x93, 0xa1, 0x6d, 0xc8, 0x87, 0x43, 0x21, 0x1e, 0x4c, 0xe9, 0xd8, 0xd9, 0x69, 0x3a, 0x08, 0xde, 0x12, 0x56, 0xbd, 0xfd, 0x93, 0xa1, 0x6d, 0xc8, 0x87, 0x63, 0x21, 0x9e, 0x4c, 0xe9, 0xd9, 0xd9,
0xd4, 0x63, 0x8b, 0xf5, 0xb0, 0x9c, 0xbd, 0x60, 0x1d, 0xc8, 0x32, 0xd3, 0xe8, 0xad, 0xc9, 0xf2, 0x79, 0x3a, 0x08, 0x5e, 0xd5, 0x63, 0x93, 0xf5, 0xb1, 0x9c, 0xbe, 0x60, 0x1d, 0xc8, 0x32, 0xd3,
0x10, 0x8a, 0x7b, 0xc4, 0x21, 0x73, 0x54, 0x59, 0x30, 0x2a, 0xf5, 0xcb, 0x0c, 0x40, 0x6c, 0x46, 0xe8, 0xad, 0xc9, 0xf2, 0x10, 0x8a, 0x7b, 0xc4, 0x26, 0x73, 0x54, 0x59, 0x30, 0x2c, 0x8d, 0xcb,
0x34, 0x84, 0xf4, 0x21, 0xe1, 0xe8, 0x49, 0x82, 0x36, 0xe6, 0x0c, 0xa4, 0xfe, 0x74, 0x69, 0x9c, 0x0c, 0x40, 0x6c, 0x46, 0x34, 0x84, 0xf4, 0x21, 0xe1, 0xe8, 0x49, 0x82, 0x36, 0xe6, 0x8c, 0xa4,
0x94, 0xe2, 0x03, 0x64, 0x82, 0xb1, 0x40, 0x49, 0x9e, 0xfc, 0xb9, 0x03, 0xa7, 0x6f, 0xde, 0x00, 0xfe, 0x74, 0x69, 0x9c, 0x94, 0xe2, 0x03, 0x64, 0x82, 0xb1, 0x40, 0x49, 0x1e, 0xfd, 0xb9, 0x23,
0x29, 0x8b, 0x7f, 0x04, 0x35, 0x74, 0x2e, 0x4a, 0x42, 0x32, 0x7f, 0xa0, 0xf4, 0xad, 0x9b, 0x40, 0xa7, 0x6f, 0xde, 0x00, 0x29, 0x8b, 0x7f, 0x04, 0x35, 0x74, 0x2e, 0x4a, 0x42, 0x32, 0x7f, 0xa0,
0xe3, 0x06, 0x42, 0x8f, 0x24, 0x6a, 0x60, 0xbe, 0xef, 0x13, 0x35, 0xb0, 0xc8, 0x89, 0x6f, 0x40, 0xf4, 0xad, 0x9b, 0x40, 0xe3, 0x06, 0x42, 0x8f, 0x24, 0x6a, 0x60, 0xbe, 0xef, 0x13, 0x35, 0xb0,
0x0d, 0x7d, 0x93, 0xa8, 0x81, 0xf9, 0x16, 0xd3, 0x8b, 0x33, 0x13, 0xb1, 0x1f, 0xfc, 0x82, 0x34, 0xc8, 0x89, 0x6f, 0x40, 0x0d, 0x7d, 0x93, 0xa8, 0x81, 0xf9, 0x16, 0xd3, 0x8b, 0x33, 0x13, 0xb1,
0xde, 0x9e, 0x5f, 0x95, 0x56, 0xbe, 0x5f, 0x95, 0x56, 0x3e, 0x8d, 0x4b, 0xca, 0xf9, 0xb8, 0xa4, 0x1f, 0xfc, 0x84, 0x34, 0xdf, 0x9e, 0x5f, 0x95, 0x56, 0xbe, 0x5f, 0x95, 0x56, 0x3e, 0x8d, 0x4b,
0x7c, 0x1b, 0x97, 0x94, 0xcb, 0x71, 0x49, 0x79, 0x7d, 0xf0, 0x07, 0x7f, 0x55, 0xdb, 0x71, 0xd4, 0xca, 0xf9, 0xb8, 0xa4, 0x7c, 0x1b, 0x97, 0x94, 0xcb, 0x71, 0x49, 0x79, 0x7d, 0xf0, 0x07, 0xff,
0x54, 0x45, 0xc5, 0x47, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x07, 0x2d, 0x75, 0x3c, 0xa6, 0x09, 0x55, 0xdb, 0x71, 0xd4, 0x52, 0x45, 0xc5, 0x47, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x6b, 0x95,
0x00, 0x00, 0xa9, 0x10, 0xa8, 0x09, 0x00, 0x00,
} }

View File

@ -91,7 +91,17 @@ message GetContainerResponse {
} }
message ListContainersRequest { message ListContainersRequest {
string filter = 1; // TODO(stevvooe): Define a filtering syntax to make these queries. // Filters contains one or more filters using the syntax defined in the
// containerd filter package.
//
// The returned result will be those that match any of the provided
// filters. Expanded, containers that match the following will be
// returned:
//
// filters[0] or filters[1] or ... or filters[n-1] or filters[n]
//
// If filters is zero-length or nil, all items will be returned.
repeated string filters = 1;
} }
message ListContainersResponse { message ListContainersResponse {

View File

@ -127,8 +127,10 @@ func (c *Client) IsServing(ctx context.Context) (bool, error) {
} }
// Containers returns all containers created in containerd // Containers returns all containers created in containerd
func (c *Client) Containers(ctx context.Context) ([]Container, error) { func (c *Client) Containers(ctx context.Context, filters ...string) ([]Container, error) {
r, err := c.ContainerService().List(ctx, &containers.ListContainersRequest{}) r, err := c.ContainerService().List(ctx, &containers.ListContainersRequest{
Filters: filters,
})
if err != nil { if err != nil {
return nil, err return nil, err
} }

52
cmd/ctr/labels.go Normal file
View File

@ -0,0 +1,52 @@
package main
import (
"errors"
"fmt"
"strings"
"github.com/urfave/cli"
)
var containersSetLabelsCommand = cli.Command{
Name: "set-labels",
Usage: "Set and clear labels for a container.",
ArgsUsage: "[flags] <name> [<key>=<value>, ...]",
Description: "Set and clear labels for a container.",
Flags: []cli.Flag{},
Action: func(clicontext *cli.Context) error {
var (
ctx, cancel = appContext(clicontext)
containerID, labels = objectWithLabelArgs(clicontext)
)
defer cancel()
client, err := newClient(clicontext)
if err != nil {
return err
}
if containerID == "" {
return errors.New("please specify a container")
}
container, err := client.LoadContainer(ctx, containerID)
if err != nil {
return err
}
setlabels, err := container.SetLabels(ctx, labels)
if err != nil {
return err
}
var labelStrings []string
for k, v := range setlabels {
labelStrings = append(labelStrings, fmt.Sprintf("%s=%s", k, v))
}
fmt.Println(strings.Join(labelStrings, ","))
return nil
},
}

View File

@ -3,11 +3,11 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"strings"
"text/tabwriter" "text/tabwriter"
"github.com/containerd/containerd" "github.com/containerd/containerd"
tasks "github.com/containerd/containerd/api/services/tasks/v1" tasks "github.com/containerd/containerd/api/services/tasks/v1"
tasktypes "github.com/containerd/containerd/api/types/task"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -31,24 +31,18 @@ var taskListCommand = cli.Command{
}, },
} }
var containerListCommand = cli.Command{ var containersListCommand = cli.Command{
Name: "containers", Name: "list",
Usage: "manage containers (metadata)", Usage: "list all tasks or those that match a filter",
Aliases: []string{"c"}, ArgsUsage: "[filter, ...]",
Subcommands: []cli.Command{ Aliases: []string{"ls"},
{ Flags: []cli.Flag{
Name: "list", cli.BoolFlag{
Usage: "list tasks", Name: "quiet, q",
Aliases: []string{"ls"}, Usage: "print only the container id",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the container id",
},
},
Action: containerListFn,
}, },
}, },
Action: containerListFn,
} }
func taskListFn(context *cli.Context) error { func taskListFn(context *cli.Context) error {
@ -62,66 +56,25 @@ func taskListFn(context *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
containers, err := client.Containers(ctx)
if err != nil {
return err
}
s := client.TaskService() s := client.TaskService()
response, err := s.List(ctx, &tasks.ListTasksRequest{})
tasksResponse, err := s.List(ctx, &tasks.ListTasksRequest{})
if err != nil { if err != nil {
return err return err
} }
// Join with tasks to get status.
tasksByContainerID := map[string]*tasktypes.Task{}
for _, task := range tasksResponse.Tasks {
tasksByContainerID[task.ContainerID] = task
}
if quiet { if quiet {
for _, c := range containers { for _, task := range response.Tasks {
task, ok := tasksByContainerID[c.ID()] fmt.Println(task.ID)
if ok {
fmt.Printf("%s\t%d\n", c.ID(), task.Pid)
} else {
//Since task is not running, PID is printed 0
fmt.Printf("%s\t%d\n", c.ID(), 0)
}
} }
} else { } else {
w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0)
w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0) fmt.Fprintln(w, "TASK\tPID\tSTATUS\t")
fmt.Fprintln(w, "TASK-ID\tIMAGE\tPID\tSTATUS") for _, task := range response.Tasks {
for _, c := range containers { if _, err := fmt.Fprintf(w, "%s\t%d\t%s\n",
var imageName string task.ID,
if image, err := c.Image(ctx); err != nil { task.Pid,
if err != containerd.ErrNoImage { task.Status.String(),
return err
}
imageName = "-"
} else {
imageName = image.Name()
}
var (
status string
pid uint32
)
task, ok := tasksByContainerID[c.ID()]
if ok {
status = task.Status.String()
pid = task.Pid
} else {
status = "STOPPED" // TODO(stevvooe): Is this assumption correct?
pid = 0
}
if _, err := fmt.Fprintf(w, "%s\t%s\t%d\t%s\n",
c.ID(),
imageName,
pid,
status,
); err != nil { ); err != nil {
return err return err
} }
@ -133,6 +86,7 @@ func taskListFn(context *cli.Context) error {
func containerListFn(context *cli.Context) error { func containerListFn(context *cli.Context) error {
var ( var (
filters = context.Args()
quiet = context.Bool("quiet") quiet = context.Bool("quiet")
ctx, cancel = appContext(context) ctx, cancel = appContext(context)
) )
@ -142,18 +96,28 @@ func containerListFn(context *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
containers, err := client.Containers(ctx) containers, err := client.Containers(ctx, filters...)
if err != nil { if err != nil {
return err return err
} }
if quiet { if quiet {
for _, c := range containers { for _, c := range containers {
fmt.Printf("%s\n", c.ID()) fmt.Printf("%s\n", c.ID())
} }
} else { } else {
cl := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0)
fmt.Fprintln(cl, "ID\tIMAGE\tRUNTIME\tSIZE") fmt.Fprintln(w, "CONTAINER\tIMAGE\tRUNTIME\tLABELS\t")
for _, c := range containers { for _, c := range containers {
var labelStrings []string
for k, v := range c.Proto().Labels {
labelStrings = append(labelStrings, strings.Join([]string{k, v}, "="))
}
labels := strings.Join(labelStrings, ",")
if labels == "" {
labels = "-"
}
var imageName string var imageName string
if image, err := c.Image(ctx); err != nil { if image, err := c.Image(ctx); err != nil {
if err != containerd.ErrNoImage { if err != containerd.ErrNoImage {
@ -163,17 +127,18 @@ func containerListFn(context *cli.Context) error {
} else { } else {
imageName = image.Name() imageName = image.Name()
} }
proto := c.Proto() proto := c.Proto()
if _, err := fmt.Fprintf(cl, "%s\t%s\t%s\t%d\n", if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%v\t\n",
c.ID(), c.ID(),
imageName, imageName,
proto.Runtime.Name, proto.Runtime.Name,
proto.Size(), labels,
); err != nil { ); err != nil {
return err return err
} }
} }
return cl.Flush() return w.Flush()
} }
return nil return nil
} }

View File

@ -60,7 +60,7 @@ containerd CLI
deleteCommand, deleteCommand,
namespacesCommand, namespacesCommand,
eventsCommand, eventsCommand,
containerListCommand, containersCommand,
taskListCommand, taskListCommand,
infoCommand, infoCommand,
killCommand, killCommand,
@ -83,3 +83,13 @@ containerd CLI
os.Exit(1) os.Exit(1)
} }
} }
var containersCommand = cli.Command{
Name: "containers",
Usage: "manage containers (metadata)",
Aliases: []string{"c"},
Subcommands: []cli.Command{
containersListCommand,
containersSetLabelsCommand,
},
}

View File

@ -33,7 +33,7 @@ var namespacesCreateCommand = cli.Command{
Action: func(clicontext *cli.Context) error { Action: func(clicontext *cli.Context) error {
var ( var (
ctx = context.Background() ctx = context.Background()
namespace, labels = namespaceWithLabelArgs(clicontext) namespace, labels = objectWithLabelArgs(clicontext)
) )
if namespace == "" { if namespace == "" {
@ -53,27 +53,6 @@ var namespacesCreateCommand = cli.Command{
}, },
} }
func namespaceWithLabelArgs(clicontext *cli.Context) (string, map[string]string) {
var (
namespace = clicontext.Args().First()
labelStrings = clicontext.Args().Tail()
labels = make(map[string]string, len(labelStrings))
)
for _, label := range labelStrings {
parts := strings.SplitN(label, "=", 2)
key := parts[0]
value := "true"
if len(parts) > 1 {
value = parts[1]
}
labels[key] = value
}
return namespace, labels
}
var namespacesSetLabelsCommand = cli.Command{ var namespacesSetLabelsCommand = cli.Command{
Name: "set-labels", Name: "set-labels",
Usage: "Set and clear labels for a namespace.", Usage: "Set and clear labels for a namespace.",
@ -83,7 +62,7 @@ var namespacesSetLabelsCommand = cli.Command{
Action: func(clicontext *cli.Context) error { Action: func(clicontext *cli.Context) error {
var ( var (
ctx = context.Background() ctx = context.Background()
namespace, labels = namespaceWithLabelArgs(clicontext) namespace, labels = objectWithLabelArgs(clicontext)
) )
namespaces, err := getNamespacesService(clicontext) namespaces, err := getNamespacesService(clicontext)

View File

@ -74,6 +74,10 @@ var runCommand = cli.Command{
Name: "env", Name: "env",
Usage: "specify additional container environment variables (i.e. FOO=bar)", Usage: "specify additional container environment variables (i.e. FOO=bar)",
}, },
cli.StringSliceFlag{
Name: "label",
Usage: "specify additional labels (foo=bar)",
},
cli.BoolFlag{ cli.BoolFlag{
Name: "rm", Name: "rm",
Usage: "remove the container after running", Usage: "remove the container after running",

View File

@ -57,11 +57,15 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
err error err error
checkpointIndex digest.Digest checkpointIndex digest.Digest
ref = context.Args().First() ref = context.Args().First()
id = context.Args().Get(1) id = context.Args().Get(1)
args = context.Args()[2:] args = context.Args()[2:]
tty = context.Bool("tty") tty = context.Bool("tty")
labelStrings = context.StringSlice("label")
) )
labels := labelArgs(labelStrings)
if raw := context.String("checkpoint"); raw != "" { if raw := context.String("checkpoint"); raw != "" {
if checkpointIndex, err = digest.Parse(raw); err != nil { if checkpointIndex, err = digest.Parse(raw); err != nil {
return nil, err return nil, err
@ -71,6 +75,7 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
if err != nil { if err != nil {
return nil, err return nil, err
} }
if checkpointIndex == "" { if checkpointIndex == "" {
opts := []containerd.SpecOpts{ opts := []containerd.SpecOpts{
containerd.WithImageConfig(ctx, image), containerd.WithImageConfig(ctx, image),
@ -96,12 +101,15 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
} else { } else {
rootfs = containerd.WithNewRootFS(id, image) rootfs = containerd.WithNewRootFS(id, image)
} }
return client.NewContainer(ctx, id, return client.NewContainer(ctx, id,
containerd.WithSpec(spec), containerd.WithSpec(spec),
containerd.WithImage(image), containerd.WithImage(image),
containerd.WithContainerLabels(labels),
rootfs, rootfs,
) )
} }
return client.NewContainer(ctx, id, containerd.WithCheckpoint(v1.Descriptor{ return client.NewContainer(ctx, id, containerd.WithCheckpoint(v1.Descriptor{
Digest: checkpointIndex, Digest: checkpointIndex,
}, id)) }, id))

View File

@ -247,3 +247,28 @@ func replaceOrAppendEnvValues(defaults, overrides []string) []string {
return defaults return defaults
} }
func objectWithLabelArgs(clicontext *cli.Context) (string, map[string]string) {
var (
namespace = clicontext.Args().First()
labelStrings = clicontext.Args().Tail()
)
return namespace, labelArgs(labelStrings)
}
func labelArgs(labelStrings []string) map[string]string {
labels := make(map[string]string, len(labelStrings))
for _, label := range labelStrings {
parts := strings.SplitN(label, "=", 2)
key := parts[0]
value := "true"
if len(parts) > 1 {
value = parts[1]
}
labels[key] = value
}
return labels
}

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"path/filepath" "path/filepath"
"strings"
"sync" "sync"
"google.golang.org/grpc" "google.golang.org/grpc"
@ -12,6 +13,7 @@ import (
"github.com/containerd/containerd/api/services/containers/v1" "github.com/containerd/containerd/api/services/containers/v1"
"github.com/containerd/containerd/api/services/tasks/v1" "github.com/containerd/containerd/api/services/tasks/v1"
"github.com/containerd/containerd/api/types" "github.com/containerd/containerd/api/types"
ptypes "github.com/gogo/protobuf/types"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -33,6 +35,8 @@ type Container interface {
Spec() (*specs.Spec, error) Spec() (*specs.Spec, error)
Task(context.Context, IOAttach) (Task, error) Task(context.Context, IOAttach) (Task, error)
Image(context.Context) (Image, error) Image(context.Context) (Image, error)
Labels(context.Context) (map[string]string, error)
SetLabels(context.Context, map[string]string) (map[string]string, error)
} }
func containerFromProto(client *Client, c containers.Container) *container { func containerFromProto(client *Client, c containers.Container) *container {
@ -60,6 +64,53 @@ func (c *container) Proto() containers.Container {
return c.c return c.c
} }
func (c *container) Labels(ctx context.Context) (map[string]string, error) {
resp, err := c.client.ContainerService().Get(ctx, &containers.GetContainerRequest{
ID: c.ID(),
})
if err != nil {
return nil, err
}
c.c = resp.Container
m := make(map[string]string, len(resp.Container.Labels))
for k, v := range c.c.Labels {
m[k] = v
}
return m, nil
}
func (c *container) SetLabels(ctx context.Context, labels map[string]string) (map[string]string, error) {
var req containers.UpdateContainerRequest
req.Container.ID = c.ID()
req.Container.Labels = labels
req.UpdateMask = &ptypes.FieldMask{
Paths: make([]string, 0, len(labels)),
}
// mask off paths so we only muck with the labels encountered in labels.
// Labels not in the passed in argument will be left alone.
for k := range labels {
req.UpdateMask.Paths = append(req.UpdateMask.Paths, strings.Join([]string{"labels", k}, "."))
}
resp, err := c.client.ContainerService().Update(ctx, &req)
if err != nil {
return nil, err
}
c.c = resp.Container // update our local container
m := make(map[string]string, len(resp.Container.Labels))
for k, v := range c.c.Labels {
m[k] = v
}
return m, nil
}
// Spec returns the current OCI specification for the container // Spec returns the current OCI specification for the container
func (c *container) Spec() (*specs.Spec, error) { func (c *container) Spec() (*specs.Spec, error) {
var s specs.Spec var s specs.Spec

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"time" "time"
"github.com/containerd/containerd/filters"
"github.com/gogo/protobuf/types" "github.com/gogo/protobuf/types"
) )
@ -29,7 +30,10 @@ type RuntimeInfo struct {
type Store interface { type Store interface {
Get(ctx context.Context, id string) (Container, error) Get(ctx context.Context, id string) (Container, error)
List(ctx context.Context, filter string) ([]Container, error)
// List returns containers that match one or more of the provided filters.
List(ctx context.Context, filters ...filters.Filter) ([]Container, error)
Create(ctx context.Context, container Container) (Container, error) Create(ctx context.Context, container Container) (Container, error)
Update(ctx context.Context, container Container) (Container, error) Update(ctx context.Context, container Container) (Container, error)
Delete(ctx context.Context, id string) error Delete(ctx context.Context, id string) error

View File

@ -2,11 +2,13 @@ package metadata
import ( import (
"context" "context"
"strings"
"time" "time"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/filters"
"github.com/containerd/containerd/identifiers" "github.com/containerd/containerd/identifiers"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
@ -43,16 +45,22 @@ func (s *containerStore) Get(ctx context.Context, id string) (containers.Contain
return container, nil return container, nil
} }
func (s *containerStore) List(ctx context.Context, filter string) ([]containers.Container, error) { func (s *containerStore) List(ctx context.Context, fs ...filters.Filter) ([]containers.Container, error) {
namespace, err := namespaces.NamespaceRequired(ctx) namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var ( var (
m []containers.Container m []containers.Container
bkt = getContainersBucket(s.tx, namespace) filter = filters.Filter(filters.Any(fs))
bkt = getContainersBucket(s.tx, namespace)
) )
if len(fs) == 0 {
filter = filters.Always
}
if bkt == nil { if bkt == nil {
return m, nil return m, nil
} }
@ -66,7 +74,10 @@ func (s *containerStore) List(ctx context.Context, filter string) ([]containers.
if err := readContainer(&container, cbkt); err != nil { if err := readContainer(&container, cbkt); err != nil {
return errors.Wrap(err, "failed to read container") return errors.Wrap(err, "failed to read container")
} }
m = append(m, container)
if filter.Match(adaptContainer(container)) {
m = append(m, container)
}
return nil return nil
}); err != nil { }); err != nil {
return nil, err return nil, err
@ -75,6 +86,46 @@ func (s *containerStore) List(ctx context.Context, filter string) ([]containers.
return m, nil return m, nil
} }
func adaptContainer(o interface{}) filters.Adaptor {
obj := o.(containers.Container)
return filters.AdapterFunc(func(fieldpath []string) (string, bool) {
if len(fieldpath) == 0 {
return "", false
}
switch fieldpath[0] {
case "id":
return obj.ID, len(obj.ID) > 0
case "runtime":
if len(fieldpath) <= 1 {
return "", false
}
switch fieldpath[1] {
case "name":
return obj.Runtime.Name, len(obj.Runtime.Name) > 0
default:
return "", false
}
case "image":
return obj.Image, len(obj.Image) > 0
case "labels":
return checkMap(fieldpath[1:], obj.Labels)
}
return "", false
})
}
func checkMap(fieldpath []string, m map[string]string) (string, bool) {
if len(m) == 0 {
return "", false
}
value, ok := m[strings.Join(fieldpath, ".")]
return value, ok
}
func (s *containerStore) Create(ctx context.Context, container containers.Container) (containers.Container, error) { func (s *containerStore) Create(ctx context.Context, container containers.Container) (containers.Container, error) {
namespace, err := namespaces.NamespaceRequired(ctx) namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil { if err != nil {
@ -268,14 +319,22 @@ func writeContainer(container *containers.Container, bkt *bolt.Bucket) error {
return err return err
} }
} }
lbkt, err := bkt.CreateBucket(bucketKeyLabels) lbkt, err := bkt.CreateBucket(bucketKeyLabels)
if err != nil { if err != nil {
return err return err
} }
for k, v := range container.Labels { for k, v := range container.Labels {
if v == "" {
delete(container.Labels, k) // remove since we don't actually set it
continue
}
if err := lbkt.Put([]byte(k), []byte(v)); err != nil { if err := lbkt.Put([]byte(k), []byte(v)); err != nil {
return err return err
} }
} }
return nil return nil
} }

View File

@ -136,7 +136,7 @@ func (s *namespaceStore) namespaceEmpty(ctx context.Context, namespace string) (
} }
containerStore := NewContainerStore(s.tx) containerStore := NewContainerStore(s.tx)
containers, err := containerStore.List(ctx, "") containers, err := containerStore.List(ctx)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -35,15 +35,25 @@ func containerToProto(container *containers.Container) api.Container {
} }
func containerFromProto(containerpb *api.Container) containers.Container { func containerFromProto(containerpb *api.Container) containers.Container {
return containers.Container{ var runtime containers.RuntimeInfo
ID: containerpb.ID, if containerpb.Runtime != nil {
Labels: containerpb.Labels, runtime = containers.RuntimeInfo{
Image: containerpb.Image,
Runtime: containers.RuntimeInfo{
Name: containerpb.Runtime.Name, Name: containerpb.Runtime.Name,
Options: containerpb.Runtime.Options, Options: containerpb.Runtime.Options,
}, }
Spec: containerpb.Spec.Value, }
RootFS: containerpb.RootFS,
var spec []byte
if containerpb.Spec != nil {
spec = containerpb.Spec.Value
}
return containers.Container{
ID: containerpb.ID,
Labels: containerpb.Labels,
Image: containerpb.Image,
Runtime: runtime,
Spec: spec,
RootFS: containerpb.RootFS,
} }
} }

View File

@ -1,12 +1,15 @@
package containers package containers
import ( import (
"strings"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
api "github.com/containerd/containerd/api/services/containers/v1" api "github.com/containerd/containerd/api/services/containers/v1"
eventsapi "github.com/containerd/containerd/api/services/events/v1" eventsapi "github.com/containerd/containerd/api/services/events/v1"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events" "github.com/containerd/containerd/events"
"github.com/containerd/containerd/filters"
"github.com/containerd/containerd/metadata" "github.com/containerd/containerd/metadata"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/golang/protobuf/ptypes/empty" "github.com/golang/protobuf/ptypes/empty"
@ -65,8 +68,18 @@ func (s *Service) Get(ctx context.Context, req *api.GetContainerRequest) (*api.G
func (s *Service) List(ctx context.Context, req *api.ListContainersRequest) (*api.ListContainersResponse, error) { func (s *Service) List(ctx context.Context, req *api.ListContainersRequest) (*api.ListContainersResponse, error) {
var resp api.ListContainersResponse var resp api.ListContainersResponse
var fs []filters.Filter
for _, s := range req.Filters {
f, err := filters.Parse(s)
if err != nil {
return nil, grpc.Errorf(codes.InvalidArgument, err.Error())
}
fs = append(fs, f)
}
return &resp, errdefs.ToGRPC(s.withStoreView(ctx, func(ctx context.Context, store containers.Store) error { return &resp, errdefs.ToGRPC(s.withStoreView(ctx, func(ctx context.Context, store containers.Store) error {
containers, err := store.List(ctx, req.Filter) containers, err := store.List(ctx, fs...)
if err != nil { if err != nil {
return err return err
} }
@ -108,18 +121,20 @@ func (s *Service) Create(ctx context.Context, req *api.CreateContainerRequest) (
} }
func (s *Service) Update(ctx context.Context, req *api.UpdateContainerRequest) (*api.UpdateContainerResponse, error) { func (s *Service) Update(ctx context.Context, req *api.UpdateContainerRequest) (*api.UpdateContainerResponse, error) {
var resp api.UpdateContainerResponse var (
resp api.UpdateContainerResponse
incoming = containerFromProto(&req.Container)
)
if err := s.withStoreUpdate(ctx, func(ctx context.Context, store containers.Store) error { if err := s.withStoreUpdate(ctx, func(ctx context.Context, store containers.Store) error {
container := containerFromProto(&req.Container)
current, err := store.Get(ctx, container.ID) container, err := store.Get(ctx, incoming.ID)
if err != nil { if err != nil {
return err return err
} }
if current.ID != container.ID { if container.ID != incoming.ID {
return grpc.Errorf(codes.InvalidArgument, "container ids must match: %v != %v", current.ID, container.ID) return grpc.Errorf(codes.InvalidArgument, "container ids must match: %v != %v", container.ID, incoming.ID)
} }
// apply the field mask. If you update this code, you better follow the // apply the field mask. If you update this code, you better follow the
@ -127,34 +142,39 @@ func (s *Service) Update(ctx context.Context, req *api.UpdateContainerRequest) (
// is, do not update this code. // is, do not update this code.
if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 { if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 {
for _, path := range req.UpdateMask.Paths { for _, path := range req.UpdateMask.Paths {
if strings.HasPrefix(path, "labels.") {
key := strings.TrimPrefix(path, "labels.")
container.Labels[key] = incoming.Labels[key]
continue
}
switch path { switch path {
case "labels": case "labels":
current.Labels = container.Labels container.Labels = incoming.Labels
case "image": case "image":
current.Image = container.Image container.Image = incoming.Image
case "runtime": case "runtime":
// TODO(stevvooe): Should this actually be allowed? // TODO(stevvooe): Should this actually be allowed?
current.Runtime = container.Runtime container.Runtime = incoming.Runtime
case "spec": case "spec":
current.Spec = container.Spec container.Spec = incoming.Spec
case "rootfs": case "rootfs":
current.RootFS = container.RootFS container.RootFS = incoming.RootFS
default: default:
return grpc.Errorf(codes.InvalidArgument, "cannot update %q field", path) return grpc.Errorf(codes.InvalidArgument, "cannot update %q field", path)
} }
} }
} else { } else {
// no field mask present, just replace everything // no field mask present, just replace everything
current = container container = incoming
} }
created, err := store.Update(ctx, container) updated, err := store.Update(ctx, container)
if err != nil { if err != nil {
return err return err
} }
resp.Container = containerToProto(&created) resp.Container = containerToProto(&updated)
return nil return nil
}); err != nil { }); err != nil {
return &resp, errdefs.ToGRPC(err) return &resp, errdefs.ToGRPC(err)