Merge pull request #1050 from stevvooe/container-filters

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

View File

@ -91,7 +91,17 @@ message GetContainerResponse {
}
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 {

View File

@ -127,8 +127,10 @@ func (c *Client) IsServing(ctx context.Context) (bool, error) {
}
// Containers returns all containers created in containerd
func (c *Client) Containers(ctx context.Context) ([]Container, error) {
r, err := c.ContainerService().List(ctx, &containers.ListContainersRequest{})
func (c *Client) Containers(ctx context.Context, filters ...string) ([]Container, error) {
r, err := c.ContainerService().List(ctx, &containers.ListContainersRequest{
Filters: filters,
})
if err != nil {
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 (
"fmt"
"os"
"strings"
"text/tabwriter"
"github.com/containerd/containerd"
tasks "github.com/containerd/containerd/api/services/tasks/v1"
tasktypes "github.com/containerd/containerd/api/types/task"
"github.com/urfave/cli"
)
@ -31,24 +31,18 @@ var taskListCommand = cli.Command{
},
}
var containerListCommand = cli.Command{
Name: "containers",
Usage: "manage containers (metadata)",
Aliases: []string{"c"},
Subcommands: []cli.Command{
{
Name: "list",
Usage: "list tasks",
Aliases: []string{"ls"},
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the container id",
},
},
Action: containerListFn,
var containersListCommand = cli.Command{
Name: "list",
Usage: "list all tasks or those that match a filter",
ArgsUsage: "[filter, ...]",
Aliases: []string{"ls"},
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the container id",
},
},
Action: containerListFn,
}
func taskListFn(context *cli.Context) error {
@ -62,66 +56,25 @@ func taskListFn(context *cli.Context) error {
if err != nil {
return err
}
containers, err := client.Containers(ctx)
if err != nil {
return err
}
s := client.TaskService()
tasksResponse, err := s.List(ctx, &tasks.ListTasksRequest{})
response, err := s.List(ctx, &tasks.ListTasksRequest{})
if err != nil {
return err
}
// Join with tasks to get status.
tasksByContainerID := map[string]*tasktypes.Task{}
for _, task := range tasksResponse.Tasks {
tasksByContainerID[task.ContainerID] = task
}
if quiet {
for _, c := range containers {
task, ok := tasksByContainerID[c.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)
}
for _, task := range response.Tasks {
fmt.Println(task.ID)
}
} else {
w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
fmt.Fprintln(w, "TASK-ID\tIMAGE\tPID\tSTATUS")
for _, c := range containers {
var imageName string
if image, err := c.Image(ctx); err != nil {
if err != containerd.ErrNoImage {
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,
w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0)
fmt.Fprintln(w, "TASK\tPID\tSTATUS\t")
for _, task := range response.Tasks {
if _, err := fmt.Fprintf(w, "%s\t%d\t%s\n",
task.ID,
task.Pid,
task.Status.String(),
); err != nil {
return err
}
@ -133,6 +86,7 @@ func taskListFn(context *cli.Context) error {
func containerListFn(context *cli.Context) error {
var (
filters = context.Args()
quiet = context.Bool("quiet")
ctx, cancel = appContext(context)
)
@ -142,18 +96,28 @@ func containerListFn(context *cli.Context) error {
if err != nil {
return err
}
containers, err := client.Containers(ctx)
containers, err := client.Containers(ctx, filters...)
if err != nil {
return err
}
if quiet {
for _, c := range containers {
fmt.Printf("%s\n", c.ID())
}
} else {
cl := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
fmt.Fprintln(cl, "ID\tIMAGE\tRUNTIME\tSIZE")
w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0)
fmt.Fprintln(w, "CONTAINER\tIMAGE\tRUNTIME\tLABELS\t")
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
if image, err := c.Image(ctx); err != nil {
if err != containerd.ErrNoImage {
@ -163,17 +127,18 @@ func containerListFn(context *cli.Context) error {
} else {
imageName = image.Name()
}
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(),
imageName,
proto.Runtime.Name,
proto.Size(),
labels,
); err != nil {
return err
}
}
return cl.Flush()
return w.Flush()
}
return nil
}

View File

@ -60,7 +60,7 @@ containerd CLI
deleteCommand,
namespacesCommand,
eventsCommand,
containerListCommand,
containersCommand,
taskListCommand,
infoCommand,
killCommand,
@ -83,3 +83,13 @@ containerd CLI
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 {
var (
ctx = context.Background()
namespace, labels = namespaceWithLabelArgs(clicontext)
namespace, labels = objectWithLabelArgs(clicontext)
)
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{
Name: "set-labels",
Usage: "Set and clear labels for a namespace.",
@ -83,7 +62,7 @@ var namespacesSetLabelsCommand = cli.Command{
Action: func(clicontext *cli.Context) error {
var (
ctx = context.Background()
namespace, labels = namespaceWithLabelArgs(clicontext)
namespace, labels = objectWithLabelArgs(clicontext)
)
namespaces, err := getNamespacesService(clicontext)

View File

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

View File

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

View File

@ -247,3 +247,28 @@ func replaceOrAppendEnvValues(defaults, overrides []string) []string {
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"
"encoding/json"
"path/filepath"
"strings"
"sync"
"google.golang.org/grpc"
@ -12,6 +13,7 @@ import (
"github.com/containerd/containerd/api/services/containers/v1"
"github.com/containerd/containerd/api/services/tasks/v1"
"github.com/containerd/containerd/api/types"
ptypes "github.com/gogo/protobuf/types"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
@ -33,6 +35,8 @@ type Container interface {
Spec() (*specs.Spec, error)
Task(context.Context, IOAttach) (Task, 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 {
@ -60,6 +64,53 @@ func (c *container) Proto() containers.Container {
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
func (c *container) Spec() (*specs.Spec, error) {
var s specs.Spec

View File

@ -4,6 +4,7 @@ import (
"context"
"time"
"github.com/containerd/containerd/filters"
"github.com/gogo/protobuf/types"
)
@ -29,7 +30,10 @@ type RuntimeInfo struct {
type Store interface {
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)
Update(ctx context.Context, container Container) (Container, error)
Delete(ctx context.Context, id string) error

View File

@ -2,11 +2,13 @@ package metadata
import (
"context"
"strings"
"time"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/filters"
"github.com/containerd/containerd/identifiers"
"github.com/containerd/containerd/namespaces"
"github.com/gogo/protobuf/proto"
@ -43,16 +45,22 @@ func (s *containerStore) Get(ctx context.Context, id string) (containers.Contain
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)
if err != nil {
return nil, err
}
var (
m []containers.Container
bkt = getContainersBucket(s.tx, namespace)
m []containers.Container
filter = filters.Filter(filters.Any(fs))
bkt = getContainersBucket(s.tx, namespace)
)
if len(fs) == 0 {
filter = filters.Always
}
if bkt == 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 {
return errors.Wrap(err, "failed to read container")
}
m = append(m, container)
if filter.Match(adaptContainer(container)) {
m = append(m, container)
}
return nil
}); err != nil {
return nil, err
@ -75,6 +86,46 @@ func (s *containerStore) List(ctx context.Context, filter string) ([]containers.
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) {
namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil {
@ -268,14 +319,22 @@ func writeContainer(container *containers.Container, bkt *bolt.Bucket) error {
return err
}
}
lbkt, err := bkt.CreateBucket(bucketKeyLabels)
if err != nil {
return err
}
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 {
return err
}
}
return nil
}

View File

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

View File

@ -35,15 +35,25 @@ func containerToProto(container *containers.Container) api.Container {
}
func containerFromProto(containerpb *api.Container) containers.Container {
return containers.Container{
ID: containerpb.ID,
Labels: containerpb.Labels,
Image: containerpb.Image,
Runtime: containers.RuntimeInfo{
var runtime containers.RuntimeInfo
if containerpb.Runtime != nil {
runtime = containers.RuntimeInfo{
Name: containerpb.Runtime.Name,
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
import (
"strings"
"github.com/boltdb/bolt"
api "github.com/containerd/containerd/api/services/containers/v1"
eventsapi "github.com/containerd/containerd/api/services/events/v1"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events"
"github.com/containerd/containerd/filters"
"github.com/containerd/containerd/metadata"
"github.com/containerd/containerd/plugin"
"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) {
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 {
containers, err := store.List(ctx, req.Filter)
containers, err := store.List(ctx, fs...)
if err != nil {
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) {
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 {
container := containerFromProto(&req.Container)
current, err := store.Get(ctx, container.ID)
container, err := store.Get(ctx, incoming.ID)
if err != nil {
return err
}
if current.ID != container.ID {
return grpc.Errorf(codes.InvalidArgument, "container ids must match: %v != %v", current.ID, container.ID)
if container.ID != incoming.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
@ -127,34 +142,39 @@ func (s *Service) Update(ctx context.Context, req *api.UpdateContainerRequest) (
// is, do not update this code.
if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 {
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 {
case "labels":
current.Labels = container.Labels
container.Labels = incoming.Labels
case "image":
current.Image = container.Image
container.Image = incoming.Image
case "runtime":
// TODO(stevvooe): Should this actually be allowed?
current.Runtime = container.Runtime
container.Runtime = incoming.Runtime
case "spec":
current.Spec = container.Spec
container.Spec = incoming.Spec
case "rootfs":
current.RootFS = container.RootFS
container.RootFS = incoming.RootFS
default:
return grpc.Errorf(codes.InvalidArgument, "cannot update %q field", path)
}
}
} else {
// 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 {
return err
}
resp.Container = containerToProto(&created)
resp.Container = containerToProto(&updated)
return nil
}); err != nil {
return &resp, errdefs.ToGRPC(err)