Merge pull request #963 from stevvooe/namespaces-support
namespaces: support within containerd
This commit is contained in:
commit
bdf9f5f738
@ -52,7 +52,7 @@ var _ = math.Inf
|
|||||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
type Namespace struct {
|
type Namespace struct {
|
||||||
Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"`
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
// Labels provides an area to include arbitrary data on namespaces.
|
// Labels provides an area to include arbitrary data on namespaces.
|
||||||
//
|
//
|
||||||
// Note that to add a new value to this field, read the existing set and
|
// Note that to add a new value to this field, read the existing set and
|
||||||
@ -124,6 +124,10 @@ type UpdateNamespaceRequest struct {
|
|||||||
Namespace Namespace `protobuf:"bytes,1,opt,name=namespace" json:"namespace"`
|
Namespace Namespace `protobuf:"bytes,1,opt,name=namespace" json:"namespace"`
|
||||||
// UpdateMask specifies which fields to perform the update on. If empty,
|
// UpdateMask specifies which fields to perform the update on. If empty,
|
||||||
// the operation applies to all fields.
|
// the operation applies to all fields.
|
||||||
|
//
|
||||||
|
// For the most part, this applies only to selectively updating labels on
|
||||||
|
// the namespace. While field masks are typically limited to ascii alphas
|
||||||
|
// and digits, we just take everything after the "labels." as the map key.
|
||||||
UpdateMask *google_protobuf2.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask" json:"update_mask,omitempty"`
|
UpdateMask *google_protobuf2.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask" json:"update_mask,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +144,7 @@ func (*UpdateNamespaceResponse) ProtoMessage() {}
|
|||||||
func (*UpdateNamespaceResponse) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{8} }
|
func (*UpdateNamespaceResponse) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{8} }
|
||||||
|
|
||||||
type DeleteNamespaceRequest struct {
|
type DeleteNamespaceRequest struct {
|
||||||
Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"`
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DeleteNamespaceRequest) Reset() { *m = DeleteNamespaceRequest{} }
|
func (m *DeleteNamespaceRequest) Reset() { *m = DeleteNamespaceRequest{} }
|
||||||
@ -379,11 +383,11 @@ func (m *Namespace) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
_ = i
|
_ = i
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
if len(m.Namespace) > 0 {
|
if len(m.Name) > 0 {
|
||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
i++
|
i++
|
||||||
i = encodeVarintNamespace(dAtA, i, uint64(len(m.Namespace)))
|
i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name)))
|
||||||
i += copy(dAtA[i:], m.Namespace)
|
i += copy(dAtA[i:], m.Name)
|
||||||
}
|
}
|
||||||
if len(m.Labels) > 0 {
|
if len(m.Labels) > 0 {
|
||||||
for k, _ := range m.Labels {
|
for k, _ := range m.Labels {
|
||||||
@ -638,11 +642,11 @@ func (m *DeleteNamespaceRequest) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
_ = i
|
_ = i
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
if len(m.Namespace) > 0 {
|
if len(m.Name) > 0 {
|
||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
i++
|
i++
|
||||||
i = encodeVarintNamespace(dAtA, i, uint64(len(m.Namespace)))
|
i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name)))
|
||||||
i += copy(dAtA[i:], m.Namespace)
|
i += copy(dAtA[i:], m.Name)
|
||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
@ -677,7 +681,7 @@ func encodeVarintNamespace(dAtA []byte, offset int, v uint64) int {
|
|||||||
func (m *Namespace) Size() (n int) {
|
func (m *Namespace) Size() (n int) {
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
l = len(m.Namespace)
|
l = len(m.Name)
|
||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sovNamespace(uint64(l))
|
n += 1 + l + sovNamespace(uint64(l))
|
||||||
}
|
}
|
||||||
@ -771,7 +775,7 @@ func (m *UpdateNamespaceResponse) Size() (n int) {
|
|||||||
func (m *DeleteNamespaceRequest) Size() (n int) {
|
func (m *DeleteNamespaceRequest) Size() (n int) {
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
l = len(m.Namespace)
|
l = len(m.Name)
|
||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sovNamespace(uint64(l))
|
n += 1 + l + sovNamespace(uint64(l))
|
||||||
}
|
}
|
||||||
@ -806,7 +810,7 @@ func (this *Namespace) String() string {
|
|||||||
}
|
}
|
||||||
mapStringForLabels += "}"
|
mapStringForLabels += "}"
|
||||||
s := strings.Join([]string{`&Namespace{`,
|
s := strings.Join([]string{`&Namespace{`,
|
||||||
`Namespace:` + fmt.Sprintf("%v", this.Namespace) + `,`,
|
`Name:` + fmt.Sprintf("%v", this.Name) + `,`,
|
||||||
`Labels:` + mapStringForLabels + `,`,
|
`Labels:` + mapStringForLabels + `,`,
|
||||||
`}`,
|
`}`,
|
||||||
}, "")
|
}, "")
|
||||||
@ -898,7 +902,7 @@ func (this *DeleteNamespaceRequest) String() string {
|
|||||||
return "nil"
|
return "nil"
|
||||||
}
|
}
|
||||||
s := strings.Join([]string{`&DeleteNamespaceRequest{`,
|
s := strings.Join([]string{`&DeleteNamespaceRequest{`,
|
||||||
`Namespace:` + fmt.Sprintf("%v", this.Namespace) + `,`,
|
`Name:` + fmt.Sprintf("%v", this.Name) + `,`,
|
||||||
`}`,
|
`}`,
|
||||||
}, "")
|
}, "")
|
||||||
return s
|
return s
|
||||||
@ -942,7 +946,7 @@ func (m *Namespace) 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 Namespace", wireType)
|
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
|
||||||
}
|
}
|
||||||
var stringLen uint64
|
var stringLen uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
@ -967,7 +971,7 @@ func (m *Namespace) Unmarshal(dAtA []byte) error {
|
|||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
m.Namespace = string(dAtA[iNdEx:postIndex])
|
m.Name = string(dAtA[iNdEx:postIndex])
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
case 2:
|
case 2:
|
||||||
if wireType != 2 {
|
if wireType != 2 {
|
||||||
@ -1809,7 +1813,7 @@ func (m *DeleteNamespaceRequest) 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 Namespace", wireType)
|
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
|
||||||
}
|
}
|
||||||
var stringLen uint64
|
var stringLen uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
@ -1834,7 +1838,7 @@ func (m *DeleteNamespaceRequest) Unmarshal(dAtA []byte) error {
|
|||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
m.Namespace = string(dAtA[iNdEx:postIndex])
|
m.Name = string(dAtA[iNdEx:postIndex])
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
@ -1967,39 +1971,38 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var fileDescriptorNamespace = []byte{
|
var fileDescriptorNamespace = []byte{
|
||||||
// 533 bytes of a gzipped FileDescriptorProto
|
// 528 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xcd, 0x8e, 0xd3, 0x30,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x54, 0xbd, 0x8e, 0xd3, 0x4c,
|
||||||
0x10, 0xae, 0xdb, 0x52, 0xa9, 0x93, 0x0b, 0x32, 0x25, 0x44, 0x01, 0x85, 0x2a, 0x5c, 0x96, 0x03,
|
0x14, 0xcd, 0x24, 0xf9, 0x2c, 0xe5, 0xba, 0xf9, 0x34, 0x04, 0x63, 0x19, 0xc9, 0x44, 0xa6, 0x59,
|
||||||
0x0e, 0x5b, 0x24, 0xc4, 0xcf, 0x6d, 0x61, 0x29, 0x48, 0x0b, 0x87, 0x48, 0x9c, 0x57, 0x4e, 0xeb,
|
0x24, 0x18, 0xb3, 0xa1, 0xe1, 0xa7, 0x5b, 0x58, 0x02, 0xd2, 0x42, 0x61, 0x89, 0x7a, 0x35, 0x4e,
|
||||||
0x86, 0xa8, 0xf9, 0x23, 0x76, 0x2a, 0xf5, 0xc6, 0x1b, 0xf0, 0x06, 0xbc, 0x01, 0xef, 0xd1, 0x23,
|
0x26, 0xc6, 0x8a, 0xff, 0xf0, 0x8c, 0x23, 0xa5, 0xe3, 0x0d, 0x78, 0x03, 0x1a, 0x5e, 0x26, 0x25,
|
||||||
0x47, 0x4e, 0x88, 0xed, 0x93, 0xa0, 0x38, 0x69, 0xd3, 0x6d, 0xd3, 0xa8, 0x2b, 0x95, 0xdb, 0xd8,
|
0x25, 0x15, 0x62, 0xf3, 0x24, 0xc8, 0x63, 0x27, 0xce, 0x6e, 0x1c, 0x2b, 0x2b, 0x85, 0xee, 0x8e,
|
||||||
0x9e, 0xcf, 0xdf, 0xcc, 0xf8, 0xfb, 0x12, 0x78, 0xef, 0x7a, 0xe2, 0x4b, 0xea, 0x90, 0x51, 0x14,
|
0x7d, 0x8e, 0xcf, 0xb9, 0xd7, 0xe7, 0x0e, 0xbc, 0xf3, 0x7c, 0xf1, 0x39, 0x73, 0xc9, 0x38, 0x0e,
|
||||||
0x58, 0xa3, 0x28, 0x14, 0xd4, 0x0b, 0x59, 0x32, 0xde, 0x0c, 0x69, 0xec, 0x59, 0x9c, 0x25, 0x33,
|
0xed, 0x71, 0x1c, 0x09, 0xea, 0x47, 0x2c, 0x9d, 0x6c, 0x97, 0x34, 0xf1, 0x6d, 0xce, 0xd2, 0xb9,
|
||||||
0x6f, 0xc4, 0xb8, 0x15, 0xd2, 0x80, 0xf1, 0x98, 0x5e, 0x0b, 0x49, 0x9c, 0x44, 0x22, 0xc2, 0x5a,
|
0x3f, 0x66, 0xdc, 0x8e, 0x68, 0xc8, 0x78, 0x42, 0xaf, 0x95, 0x24, 0x49, 0x63, 0x11, 0x63, 0xbd,
|
||||||
0x89, 0x21, 0xb3, 0x53, 0x52, 0x66, 0xea, 0x3d, 0x37, 0x72, 0x23, 0x99, 0x64, 0x65, 0x51, 0x9e,
|
0xe2, 0x90, 0xf9, 0x29, 0xa9, 0x90, 0x46, 0xdf, 0x8b, 0xbd, 0x58, 0x82, 0xec, 0xbc, 0x2a, 0xf0,
|
||||||
0xaf, 0xdf, 0x77, 0xa3, 0xc8, 0xf5, 0x99, 0x25, 0x57, 0x4e, 0x3a, 0xb1, 0x58, 0x10, 0x8b, 0x79,
|
0xc6, 0x7d, 0x2f, 0x8e, 0xbd, 0x80, 0xd9, 0xf2, 0xe4, 0x66, 0x53, 0x9b, 0x85, 0x89, 0x58, 0x94,
|
||||||
0x71, 0xd8, 0xdf, 0x3e, 0x9c, 0x78, 0xcc, 0x1f, 0x5f, 0x06, 0x94, 0x4f, 0xf3, 0x0c, 0xf3, 0x27,
|
0x2f, 0x07, 0x37, 0x5f, 0x4e, 0x7d, 0x16, 0x4c, 0x2e, 0x43, 0xca, 0x67, 0x05, 0xc2, 0xfa, 0x81,
|
||||||
0x82, 0xee, 0xa7, 0x15, 0x07, 0x7e, 0x00, 0xdd, 0x35, 0xa1, 0x86, 0xfa, 0xe8, 0xa4, 0x6b, 0x97,
|
0xa0, 0xf7, 0x71, 0xad, 0x81, 0x31, 0x74, 0x73, 0x41, 0x1d, 0x0d, 0xd0, 0x49, 0xcf, 0x91, 0x35,
|
||||||
0x1b, 0x78, 0x08, 0x1d, 0x9f, 0x3a, 0xcc, 0xe7, 0x5a, 0xb3, 0xdf, 0x3a, 0x51, 0x06, 0x16, 0xd9,
|
0x1e, 0x81, 0x12, 0x50, 0x97, 0x05, 0x5c, 0x6f, 0x0f, 0x3a, 0x27, 0xea, 0xd0, 0x26, 0xfb, 0x1c,
|
||||||
0x57, 0x2b, 0x59, 0x5f, 0x49, 0x2e, 0x24, 0xe2, 0x3c, 0x14, 0xc9, 0xdc, 0x2e, 0xe0, 0xfa, 0x4b,
|
0x92, 0xcd, 0x87, 0xc8, 0x85, 0x64, 0x9c, 0x47, 0x22, 0x5d, 0x38, 0x25, 0xdd, 0x78, 0x01, 0xea,
|
||||||
0x50, 0x36, 0xb6, 0xf1, 0x6d, 0x68, 0x4d, 0xd9, 0xbc, 0xe0, 0xcb, 0x42, 0xdc, 0x83, 0x5b, 0x33,
|
0xd6, 0x63, 0xfc, 0x3f, 0x74, 0x66, 0x6c, 0x51, 0x4a, 0xe5, 0x25, 0xee, 0xc3, 0x7f, 0x73, 0x1a,
|
||||||
0xea, 0xa7, 0x4c, 0x6b, 0xca, 0xbd, 0x7c, 0xf1, 0xaa, 0xf9, 0x02, 0x99, 0x8f, 0xe1, 0xce, 0x90,
|
0x64, 0x4c, 0x6f, 0xcb, 0x67, 0xc5, 0xe1, 0x65, 0xfb, 0x39, 0xb2, 0x1e, 0xc1, 0x9d, 0x11, 0x13,
|
||||||
0x89, 0xf5, 0xf5, 0x36, 0xfb, 0x9a, 0x32, 0x2e, 0x30, 0x86, 0x76, 0xc6, 0x5e, 0xdc, 0x21, 0x63,
|
0x9b, 0xcf, 0x3b, 0xec, 0x4b, 0xc6, 0xb8, 0xa8, 0xb3, 0x6b, 0x5d, 0x42, 0xff, 0x3a, 0x94, 0x27,
|
||||||
0xf3, 0x12, 0x7a, 0xd7, 0x53, 0x79, 0x1c, 0x85, 0x3c, 0x6b, 0x63, 0xab, 0x49, 0x65, 0xf0, 0xe8,
|
0x71, 0xc4, 0xf3, 0x36, 0x7a, 0x1b, 0xa7, 0x92, 0xa0, 0x0e, 0x1f, 0x1e, 0xd0, 0xc9, 0x59, 0x77,
|
||||||
0x80, 0x4e, 0xce, 0xda, 0x8b, 0x3f, 0x0f, 0x1b, 0x1b, 0xf3, 0x30, 0x2d, 0xb8, 0x7b, 0xe1, 0xf1,
|
0xf9, 0xfb, 0x41, 0xcb, 0xa9, 0xb8, 0x96, 0x0d, 0x77, 0x2f, 0x7c, 0x5e, 0x29, 0xf0, 0xb5, 0x1b,
|
||||||
0x92, 0x81, 0xaf, 0xaa, 0x51, 0xa1, 0x33, 0xf1, 0x7c, 0xc1, 0x92, 0xa2, 0x9e, 0x62, 0x65, 0x8e,
|
0x0d, 0x94, 0xa9, 0x1f, 0x08, 0x96, 0x96, 0x7e, 0xca, 0x93, 0x35, 0x06, 0xed, 0x26, 0xa1, 0xf4,
|
||||||
0x40, 0xdd, 0x06, 0x14, 0x35, 0x7d, 0x00, 0x28, 0x39, 0x35, 0x24, 0xc7, 0x7b, 0x83, 0xa2, 0x36,
|
0xf4, 0x1e, 0xa0, 0xd2, 0xd4, 0x91, 0x1c, 0xef, 0x2d, 0x4c, 0x6d, 0x91, 0x2d, 0x0a, 0xda, 0xeb,
|
||||||
0xc0, 0x26, 0x05, 0xf5, 0x4d, 0xc2, 0xa8, 0x60, 0x3b, 0x43, 0x3a, 0x5a, 0xe3, 0x0e, 0xdc, 0xdb,
|
0x94, 0x51, 0xc1, 0x76, 0x86, 0x74, 0xb4, 0xc6, 0x5d, 0xb8, 0xb7, 0x23, 0x71, 0xec, 0xe1, 0x7e,
|
||||||
0xa1, 0x38, 0xf6, 0x70, 0x7f, 0x20, 0x50, 0x3f, 0xc7, 0xe3, 0xff, 0xd9, 0x07, 0x7e, 0x0d, 0x4a,
|
0x47, 0xa0, 0x7d, 0x4a, 0x26, 0xff, 0xb2, 0x0f, 0xfc, 0x0a, 0xd4, 0x4c, 0x4a, 0xc8, 0x3d, 0x90,
|
||||||
0x2a, 0x29, 0xa4, 0x23, 0xa4, 0xd8, 0x94, 0x81, 0x4e, 0x72, 0xd3, 0x90, 0x95, 0x69, 0xc8, 0xbb,
|
0x61, 0x53, 0x87, 0x06, 0x29, 0x56, 0x85, 0xac, 0x57, 0x85, 0xbc, 0xcd, 0x57, 0xe5, 0x03, 0xe5,
|
||||||
0xcc, 0x34, 0x1f, 0x29, 0x9f, 0xda, 0x90, 0xa7, 0x67, 0x71, 0x36, 0x84, 0x9d, 0xfa, 0x8e, 0x3d,
|
0x33, 0x07, 0x0a, 0x78, 0x5e, 0xe7, 0x43, 0xd8, 0xf1, 0x77, 0xec, 0x21, 0x3c, 0x06, 0xed, 0x0d,
|
||||||
0x84, 0xe7, 0xa0, 0xbe, 0x65, 0x3e, 0xab, 0x98, 0x41, 0xad, 0x53, 0x07, 0xdf, 0xdb, 0x00, 0xa5,
|
0x0b, 0x58, 0xcd, 0x0c, 0x6a, 0x02, 0x3f, 0xfc, 0xd6, 0x05, 0xa8, 0xb2, 0x85, 0x27, 0xd0, 0x19,
|
||||||
0xca, 0xf0, 0x18, 0x5a, 0x43, 0x26, 0xf0, 0x93, 0xfd, 0x35, 0x54, 0x78, 0x4a, 0x27, 0x87, 0xa6,
|
0x31, 0x81, 0x9f, 0xec, 0x57, 0xae, 0xd9, 0x24, 0x83, 0x1c, 0x0a, 0x2f, 0x7b, 0xf5, 0xa1, 0x9b,
|
||||||
0x17, 0x5d, 0x7b, 0xd0, 0xce, 0xd4, 0x8d, 0x6b, 0x3e, 0x0b, 0x95, 0x76, 0xd1, 0x9f, 0x1e, 0x0e,
|
0x67, 0x1a, 0x37, 0x5c, 0x06, 0xb5, 0x4b, 0x62, 0x3c, 0x3d, 0x9c, 0x50, 0x4a, 0x85, 0xa0, 0x14,
|
||||||
0x28, 0xa8, 0x02, 0xe8, 0xe4, 0x02, 0xc4, 0x35, 0xd8, 0x6a, 0x17, 0xe8, 0xa7, 0x37, 0x40, 0x94,
|
0xb1, 0xc3, 0x0d, 0xdc, 0xfa, 0xec, 0x1b, 0xa7, 0xb7, 0x60, 0x54, 0x72, 0xc5, 0x0f, 0x6e, 0x92,
|
||||||
0x74, 0xf9, 0x53, 0xd7, 0xd1, 0x55, 0x8b, 0xb5, 0x8e, 0x6e, 0x9f, 0x7c, 0x6c, 0xe8, 0xe4, 0xaf,
|
0xab, 0x8f, 0x68, 0x93, 0xdc, 0xbe, 0xd0, 0x38, 0xa0, 0x14, 0xff, 0xba, 0x49, 0xae, 0x3e, 0x0d,
|
||||||
0x5e, 0x47, 0x57, 0xad, 0x0b, 0x5d, 0xdd, 0x51, 0xef, 0x79, 0xf6, 0x3f, 0x38, 0xd3, 0x16, 0x57,
|
0x86, 0xb6, 0x93, 0xd9, 0xf3, 0xfc, 0xee, 0x3f, 0xd3, 0x97, 0x57, 0x66, 0xeb, 0xd7, 0x95, 0xd9,
|
||||||
0x46, 0xe3, 0xf7, 0x95, 0xd1, 0xf8, 0xb6, 0x34, 0xd0, 0x62, 0x69, 0xa0, 0x5f, 0x4b, 0x03, 0xfd,
|
0xfa, 0xba, 0x32, 0xd1, 0x72, 0x65, 0xa2, 0x9f, 0x2b, 0x13, 0xfd, 0x59, 0x99, 0xc8, 0x55, 0x24,
|
||||||
0x5d, 0x1a, 0xc8, 0xe9, 0xc8, 0xcc, 0x67, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0xd8, 0xf7, 0xf8,
|
0xf2, 0xd9, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x2e, 0xc3, 0x29, 0xaf, 0x06, 0x00, 0x00,
|
||||||
0x92, 0xc3, 0x06, 0x00, 0x00,
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ service Namespaces {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message Namespace {
|
message Namespace {
|
||||||
string namespace = 1;
|
string name = 1;
|
||||||
|
|
||||||
// Labels provides an area to include arbitrary data on namespaces.
|
// Labels provides an area to include arbitrary data on namespaces.
|
||||||
//
|
//
|
||||||
@ -72,6 +72,10 @@ message UpdateNamespaceRequest {
|
|||||||
|
|
||||||
// UpdateMask specifies which fields to perform the update on. If empty,
|
// UpdateMask specifies which fields to perform the update on. If empty,
|
||||||
// the operation applies to all fields.
|
// the operation applies to all fields.
|
||||||
|
//
|
||||||
|
// For the most part, this applies only to selectively updating labels on
|
||||||
|
// the namespace. While field masks are typically limited to ascii alphas
|
||||||
|
// and digits, we just take everything after the "labels." as the map key.
|
||||||
google.protobuf.FieldMask update_mask = 2;
|
google.protobuf.FieldMask update_mask = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,5 +84,5 @@ message UpdateNamespaceResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message DeleteNamespaceRequest {
|
message DeleteNamespaceRequest {
|
||||||
string namespace = 1;
|
string name = 1;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package containerd
|
package containerd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -20,9 +19,11 @@ func TestCheckpointRestore(t *testing.T) {
|
|||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx = context.Background()
|
ctx, cancel = testContext()
|
||||||
id = "CheckpointRestore"
|
id = "CheckpointRestore"
|
||||||
)
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
image, err := client.GetImage(ctx, testImage)
|
image, err := client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -113,7 +114,9 @@ func TestCheckpointRestoreNewContainer(t *testing.T) {
|
|||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
const id = "CheckpointRestoreNewContainer"
|
const id = "CheckpointRestoreNewContainer"
|
||||||
ctx := context.Background()
|
ctx, cancel := testContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
image, err := client.GetImage(ctx, testImage)
|
image, err := client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
15
client.go
15
client.go
@ -15,6 +15,7 @@ import (
|
|||||||
diffapi "github.com/containerd/containerd/api/services/diff"
|
diffapi "github.com/containerd/containerd/api/services/diff"
|
||||||
"github.com/containerd/containerd/api/services/execution"
|
"github.com/containerd/containerd/api/services/execution"
|
||||||
imagesapi "github.com/containerd/containerd/api/services/images"
|
imagesapi "github.com/containerd/containerd/api/services/images"
|
||||||
|
namespacesapi "github.com/containerd/containerd/api/services/namespaces"
|
||||||
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
|
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/images"
|
"github.com/containerd/containerd/images"
|
||||||
@ -43,13 +44,6 @@ func init() {
|
|||||||
|
|
||||||
type NewClientOpts func(c *Client) error
|
type NewClientOpts func(c *Client) error
|
||||||
|
|
||||||
func WithNamespace(namespace string) NewClientOpts {
|
|
||||||
return func(c *Client) error {
|
|
||||||
c.namespace = namespace
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new containerd client that is connected to the containerd
|
// New returns a new containerd client that is connected to the containerd
|
||||||
// instance provided by address
|
// instance provided by address
|
||||||
func New(address string, opts ...NewClientOpts) (*Client, error) {
|
func New(address string, opts ...NewClientOpts) (*Client, error) {
|
||||||
@ -79,8 +73,7 @@ func New(address string, opts ...NewClientOpts) (*Client, error) {
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
conn *grpc.ClientConn
|
conn *grpc.ClientConn
|
||||||
|
|
||||||
runtime string
|
runtime string
|
||||||
namespace string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) IsServing(ctx context.Context) (bool, error) {
|
func (c *Client) IsServing(ctx context.Context) (bool, error) {
|
||||||
@ -438,6 +431,10 @@ func (c *Client) Close() error {
|
|||||||
return c.conn.Close()
|
return c.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) NamespaceService() namespacesapi.NamespacesClient {
|
||||||
|
return namespacesapi.NewNamespacesClient(c.conn)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) ContainerService() containers.ContainersClient {
|
func (c *Client) ContainerService() containers.ContainersClient {
|
||||||
return containers.NewContainersClient(c.conn)
|
return containers.NewContainersClient(c.conn)
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/namespaces"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -30,6 +32,12 @@ func init() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testContext() (context.Context, context.CancelFunc) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
ctx = namespaces.WithNamespace(ctx, "testing")
|
||||||
|
return ctx, cancel
|
||||||
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
@ -40,9 +48,12 @@ func TestMain(m *testing.M) {
|
|||||||
supportsCriu = err == nil
|
supportsCriu = err == nil
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
buf = bytes.NewBuffer(nil)
|
buf = bytes.NewBuffer(nil)
|
||||||
|
ctx, cancel = testContext()
|
||||||
)
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if !noDaemon {
|
if !noDaemon {
|
||||||
// setup a new containerd daemon if !testing.Short
|
// setup a new containerd daemon if !testing.Short
|
||||||
cmd = exec.Command("containerd",
|
cmd = exec.Command("containerd",
|
||||||
@ -61,12 +72,13 @@ func TestMain(m *testing.M) {
|
|||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if err := waitForDaemonStart(client); err != nil {
|
if err := waitForDaemonStart(ctx, client); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pull a seed image
|
// pull a seed image
|
||||||
if _, err = client.Pull(context.Background(), testImage, WithPullUnpack); err != nil {
|
if _, err = client.Pull(ctx, testImage, WithPullUnpack); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
||||||
@ -98,13 +110,14 @@ func TestMain(m *testing.M) {
|
|||||||
os.Exit(status)
|
os.Exit(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForDaemonStart(client *Client) error {
|
func waitForDaemonStart(ctx context.Context, client *Client) error {
|
||||||
var (
|
var (
|
||||||
serving bool
|
serving bool
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
serving, err = client.IsServing(context.Background())
|
serving, err = client.IsServing(ctx)
|
||||||
if serving {
|
if serving {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -133,13 +146,16 @@ func TestImagePull(t *testing.T) {
|
|||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip()
|
t.Skip()
|
||||||
}
|
}
|
||||||
|
ctx, cancel := testContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
client, err := New(address)
|
client, err := New(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
_, err = client.Pull(context.Background(), testImage)
|
_, err = client.Pull(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
_ "github.com/containerd/containerd/services/healthcheck"
|
_ "github.com/containerd/containerd/services/healthcheck"
|
||||||
_ "github.com/containerd/containerd/services/images"
|
_ "github.com/containerd/containerd/services/images"
|
||||||
_ "github.com/containerd/containerd/services/metrics"
|
_ "github.com/containerd/containerd/services/metrics"
|
||||||
|
_ "github.com/containerd/containerd/services/namespaces"
|
||||||
_ "github.com/containerd/containerd/services/snapshot"
|
_ "github.com/containerd/containerd/services/snapshot"
|
||||||
_ "github.com/containerd/containerd/services/version"
|
_ "github.com/containerd/containerd/services/version"
|
||||||
)
|
)
|
||||||
|
@ -23,11 +23,11 @@ import (
|
|||||||
diffapi "github.com/containerd/containerd/api/services/diff"
|
diffapi "github.com/containerd/containerd/api/services/diff"
|
||||||
api "github.com/containerd/containerd/api/services/execution"
|
api "github.com/containerd/containerd/api/services/execution"
|
||||||
imagesapi "github.com/containerd/containerd/api/services/images"
|
imagesapi "github.com/containerd/containerd/api/services/images"
|
||||||
|
namespacesapi "github.com/containerd/containerd/api/services/namespaces"
|
||||||
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
|
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
|
||||||
versionapi "github.com/containerd/containerd/api/services/version"
|
versionapi "github.com/containerd/containerd/api/services/version"
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/containerd/containerd/metadata"
|
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
"github.com/containerd/containerd/snapshot"
|
"github.com/containerd/containerd/snapshot"
|
||||||
"github.com/containerd/containerd/sys"
|
"github.com/containerd/containerd/sys"
|
||||||
@ -265,10 +265,6 @@ func resolveMetaDB(ctx *cli.Context) (*bolt.DB, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := metadata.InitDB(db); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,6 +470,8 @@ func interceptor(ctx gocontext.Context,
|
|||||||
ctx = log.WithModule(ctx, "snapshot")
|
ctx = log.WithModule(ctx, "snapshot")
|
||||||
case diffapi.DiffServer:
|
case diffapi.DiffServer:
|
||||||
ctx = log.WithModule(ctx, "diff")
|
ctx = log.WithModule(ctx, "diff")
|
||||||
|
case namespacesapi.NamespacesServer:
|
||||||
|
ctx = log.WithModule(ctx, "namespaces")
|
||||||
default:
|
default:
|
||||||
log.G(ctx).Warnf("unknown GRPC server type: %#v\n", info.Server)
|
log.G(ctx).Warnf("unknown GRPC server type: %#v\n", info.Server)
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,11 @@ var checkpointCommand = cli.Command{
|
|||||||
},
|
},
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
var (
|
var (
|
||||||
id = context.String("id")
|
id = context.String("id")
|
||||||
ctx = gocontext.Background()
|
ctx, cancel = appContext(context)
|
||||||
)
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gocontext "context"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
@ -18,6 +17,9 @@ var deleteCommand = cli.Command{
|
|||||||
Usage: "delete an existing container",
|
Usage: "delete an existing container",
|
||||||
ArgsUsage: "CONTAINER",
|
ArgsUsage: "CONTAINER",
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
|
ctx, cancel := appContext(context)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
containers, err := getContainersService(context)
|
containers, err := getContainersService(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -35,7 +37,7 @@ var deleteCommand = cli.Command{
|
|||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
}
|
}
|
||||||
ctx := gocontext.TODO()
|
|
||||||
_, err = containers.Delete(ctx, &containersapi.DeleteContainerRequest{
|
_, err = containers.Delete(ctx, &containersapi.DeleteContainerRequest{
|
||||||
ID: id,
|
ID: id,
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gocontext "context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
@ -14,11 +13,14 @@ var eventsCommand = cli.Command{
|
|||||||
Name: "events",
|
Name: "events",
|
||||||
Usage: "display containerd events",
|
Usage: "display containerd events",
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
|
ctx, cancel := appContext(context)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
tasks, err := getTasksService(context)
|
tasks, err := getTasksService(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
events, err := tasks.Events(gocontext.Background(), &execution.EventsRequest{})
|
events, err := tasks.Events(ctx, &execution.EventsRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gocontext "context"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
@ -30,9 +29,11 @@ var execCommand = cli.Command{
|
|||||||
},
|
},
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
var (
|
var (
|
||||||
id = context.String("id")
|
id = context.String("id")
|
||||||
ctx = gocontext.Background()
|
ctx, cancel = appContext(context)
|
||||||
)
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,12 @@ var infoCommand = cli.Command{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
id := context.String("id")
|
var (
|
||||||
|
id = context.String("id")
|
||||||
|
ctx, cancel = appContext(context)
|
||||||
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
}
|
}
|
||||||
@ -36,7 +41,7 @@ var infoCommand = cli.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
containerResponse, err := containers.Get(gocontext.TODO(), &containersapi.GetContainerRequest{ID: id})
|
containerResponse, err := containers.Get(ctx, &containersapi.GetContainerRequest{ID: id})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gocontext "context"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/api/services/execution"
|
"github.com/containerd/containerd/api/services/execution"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@ -27,7 +25,12 @@ var killCommand = cli.Command{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
id := context.String("id")
|
var (
|
||||||
|
id = context.String("id")
|
||||||
|
ctx, cancel = appContext(context)
|
||||||
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
}
|
}
|
||||||
@ -66,7 +69,7 @@ var killCommand = cli.Command{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = tasks.Kill(gocontext.Background(), killRequest)
|
_, err = tasks.Kill(ctx, killRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,11 @@ var listCommand = cli.Command{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
quiet := context.Bool("quiet")
|
var (
|
||||||
|
quiet = context.Bool("quiet")
|
||||||
|
ctx, cancel = appContext(context)
|
||||||
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
tasks, err := getTasksService(context)
|
tasks, err := getTasksService(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -50,7 +54,7 @@ var listCommand = cli.Command{
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
tasksResponse, err := tasks.List(gocontext.TODO(), &execution.ListRequest{})
|
tasksResponse, err := tasks.List(ctx, &execution.ListRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -42,12 +42,15 @@ containerd client
|
|||||||
Usage: "address for containerd's GRPC server",
|
Usage: "address for containerd's GRPC server",
|
||||||
Value: "/run/containerd/containerd.sock",
|
Value: "/run/containerd/containerd.sock",
|
||||||
},
|
},
|
||||||
|
cli.DurationFlag{
|
||||||
|
Name: "timeout",
|
||||||
|
Usage: "total timeout for ctr commands",
|
||||||
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
// TODO(stevvooe): for now, we allow circumventing the GRPC. Once
|
Name: "namespace, n",
|
||||||
// we have clear separation, this will likely go away.
|
Usage: "namespace to use with commands",
|
||||||
Name: "root",
|
Value: "default",
|
||||||
Usage: "path to content store root",
|
EnvVar: "CONTAINERD_NAMESPACE",
|
||||||
Value: "/var/lib/containerd",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
@ -55,6 +58,7 @@ containerd client
|
|||||||
runCommand,
|
runCommand,
|
||||||
eventsCommand,
|
eventsCommand,
|
||||||
deleteCommand,
|
deleteCommand,
|
||||||
|
namespacesCommand,
|
||||||
listCommand,
|
listCommand,
|
||||||
infoCommand,
|
infoCommand,
|
||||||
killCommand,
|
killCommand,
|
||||||
|
201
cmd/ctr/namespaces.go
Normal file
201
cmd/ctr/namespaces.go
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
|
"github.com/containerd/containerd/metadata"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var namespacesCommand = cli.Command{
|
||||||
|
Name: "namespaces",
|
||||||
|
Usage: "manage namespaces",
|
||||||
|
Subcommands: cli.Commands{
|
||||||
|
namespacesCreateCommand,
|
||||||
|
namespacesSetLabelsCommand,
|
||||||
|
namespacesListCommand,
|
||||||
|
namespacesRemoveCommand,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var namespacesCreateCommand = cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
Usage: "Create a new namespace.",
|
||||||
|
ArgsUsage: "[flags] <name> [<key>=<value]",
|
||||||
|
Description: "Create a new namespace. It must be unique.",
|
||||||
|
Action: func(clicontext *cli.Context) error {
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
namespace, labels = namespaceWithLabelArgs(clicontext)
|
||||||
|
)
|
||||||
|
|
||||||
|
if namespace == "" {
|
||||||
|
return errors.New("please specify a namespace")
|
||||||
|
}
|
||||||
|
|
||||||
|
namespaces, err := getNamespacesService(clicontext)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := namespaces.Create(ctx, namespace, labels); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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.",
|
||||||
|
ArgsUsage: "[flags] <name> [<key>=<value>, ...]",
|
||||||
|
Description: "Set and clear labels for a namespace.",
|
||||||
|
Flags: []cli.Flag{},
|
||||||
|
Action: func(clicontext *cli.Context) error {
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
namespace, labels = namespaceWithLabelArgs(clicontext)
|
||||||
|
)
|
||||||
|
|
||||||
|
namespaces, err := getNamespacesService(clicontext)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if namespace == "" {
|
||||||
|
return errors.New("please specify a namespace")
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range labels {
|
||||||
|
if err := namespaces.SetLabel(ctx, namespace, k, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var namespacesListCommand = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List namespaces.",
|
||||||
|
ArgsUsage: "[flags]",
|
||||||
|
Description: "List namespaces.",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "quiet, q",
|
||||||
|
Usage: "print only the namespace name.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(clicontext *cli.Context) error {
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
quiet = clicontext.Bool("quiet")
|
||||||
|
)
|
||||||
|
|
||||||
|
namespaces, err := getNamespacesService(clicontext)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nss, err := namespaces.List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if quiet {
|
||||||
|
for _, ns := range nss {
|
||||||
|
fmt.Println(ns)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
|
||||||
|
fmt.Fprintln(tw, "NAME\tLABELS\t")
|
||||||
|
|
||||||
|
for _, ns := range nss {
|
||||||
|
labels, err := namespaces.Labels(ctx, ns)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labelStrings []string
|
||||||
|
for k, v := range labels {
|
||||||
|
labelStrings = append(labelStrings, strings.Join([]string{k, v}, "="))
|
||||||
|
}
|
||||||
|
sort.Strings(labelStrings)
|
||||||
|
|
||||||
|
fmt.Fprintf(tw, "%v\t%v\t\n", ns, strings.Join(labelStrings, ","))
|
||||||
|
}
|
||||||
|
tw.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var namespacesRemoveCommand = cli.Command{
|
||||||
|
Name: "remove",
|
||||||
|
Aliases: []string{"rm"},
|
||||||
|
Usage: "Remove one or more namespaces",
|
||||||
|
ArgsUsage: "[flags] <name> [<name>, ...]",
|
||||||
|
Description: "Remove one or more namespaces. For now, the namespace must be empty.",
|
||||||
|
Action: func(clicontext *cli.Context) error {
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
exitErr error
|
||||||
|
)
|
||||||
|
|
||||||
|
namespaces, err := getNamespacesService(clicontext)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, target := range clicontext.Args() {
|
||||||
|
if err := namespaces.Delete(ctx, target); err != nil {
|
||||||
|
if !metadata.IsNotFound(err) {
|
||||||
|
if exitErr == nil {
|
||||||
|
exitErr = errors.Wrapf(err, "unable to delete %v", target)
|
||||||
|
}
|
||||||
|
log.G(ctx).WithError(err).Errorf("unable to delete %v", target)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
return exitErr
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gocontext "context"
|
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/containerd/containerd/api/services/execution"
|
"github.com/containerd/containerd/api/services/execution"
|
||||||
@ -13,6 +12,9 @@ var pauseCommand = cli.Command{
|
|||||||
Usage: "pause an existing container",
|
Usage: "pause an existing container",
|
||||||
ArgsUsage: "CONTAINER",
|
ArgsUsage: "CONTAINER",
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
|
ctx, cancel := appContext(context)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
tasks, err := getTasksService(context)
|
tasks, err := getTasksService(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -21,7 +23,7 @@ var pauseCommand = cli.Command{
|
|||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
}
|
}
|
||||||
_, err = tasks.Pause(gocontext.Background(), &execution.PauseRequest{
|
_, err = tasks.Pause(ctx, &execution.PauseRequest{
|
||||||
ContainerID: id,
|
ContainerID: id,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gocontext "context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
@ -21,7 +20,12 @@ var psCommand = cli.Command{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
id := context.String("id")
|
var (
|
||||||
|
id = context.String("id")
|
||||||
|
ctx, cancel = appContext(context)
|
||||||
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
}
|
}
|
||||||
@ -35,7 +39,7 @@ var psCommand = cli.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := tasks.Processes(gocontext.Background(), pr)
|
resp, err := tasks.Processes(ctx, pr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gocontext "context"
|
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/containerd/containerd/api/services/execution"
|
"github.com/containerd/containerd/api/services/execution"
|
||||||
@ -13,6 +12,9 @@ var resumeCommand = cli.Command{
|
|||||||
Usage: "resume a paused container",
|
Usage: "resume a paused container",
|
||||||
ArgsUsage: "CONTAINER",
|
ArgsUsage: "CONTAINER",
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
|
ctx, cancel := appContext(context)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
tasks, err := getTasksService(context)
|
tasks, err := getTasksService(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -21,7 +23,7 @@ var resumeCommand = cli.Command{
|
|||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
}
|
}
|
||||||
_, err = tasks.Resume(gocontext.Background(), &execution.ResumeRequest{
|
_, err = tasks.Resume(ctx, &execution.ResumeRequest{
|
||||||
ContainerID: id,
|
ContainerID: id,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gocontext "context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -79,9 +78,11 @@ var runCommand = cli.Command{
|
|||||||
mounts []mount.Mount
|
mounts []mount.Mount
|
||||||
imageConfig ocispec.Image
|
imageConfig ocispec.Image
|
||||||
|
|
||||||
ctx = gocontext.Background()
|
ctx, cancel = appContext(context)
|
||||||
id = context.String("id")
|
id = context.String("id")
|
||||||
)
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@ -20,6 +19,9 @@ var snapshotCommand = cli.Command{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: func(clicontext *cli.Context) error {
|
Action: func(clicontext *cli.Context) error {
|
||||||
|
ctx, cancel := appContext(clicontext)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
id := clicontext.String("id")
|
id := clicontext.String("id")
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.New("container id must be provided")
|
return errors.New("container id must be provided")
|
||||||
@ -37,7 +39,7 @@ var snapshotCommand = cli.Command{
|
|||||||
|
|
||||||
contentRef := fmt.Sprintf("diff-%s", id)
|
contentRef := fmt.Sprintf("diff-%s", id)
|
||||||
|
|
||||||
d, err := rootfs.Diff(context.TODO(), id, contentRef, snapshotter, differ)
|
d, err := rootfs.Diff(ctx, id, contentRef, snapshotter, differ)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -18,14 +18,17 @@ import (
|
|||||||
diffapi "github.com/containerd/containerd/api/services/diff"
|
diffapi "github.com/containerd/containerd/api/services/diff"
|
||||||
"github.com/containerd/containerd/api/services/execution"
|
"github.com/containerd/containerd/api/services/execution"
|
||||||
imagesapi "github.com/containerd/containerd/api/services/images"
|
imagesapi "github.com/containerd/containerd/api/services/images"
|
||||||
|
namespacesapi "github.com/containerd/containerd/api/services/namespaces"
|
||||||
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
|
snapshotapi "github.com/containerd/containerd/api/services/snapshot"
|
||||||
versionservice "github.com/containerd/containerd/api/services/version"
|
versionservice "github.com/containerd/containerd/api/services/version"
|
||||||
"github.com/containerd/containerd/api/types/task"
|
"github.com/containerd/containerd/api/types/task"
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/images"
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/namespaces"
|
||||||
contentservice "github.com/containerd/containerd/services/content"
|
contentservice "github.com/containerd/containerd/services/content"
|
||||||
"github.com/containerd/containerd/services/diff"
|
"github.com/containerd/containerd/services/diff"
|
||||||
imagesservice "github.com/containerd/containerd/services/images"
|
imagesservice "github.com/containerd/containerd/services/images"
|
||||||
|
namespacesservice "github.com/containerd/containerd/services/namespaces"
|
||||||
snapshotservice "github.com/containerd/containerd/services/snapshot"
|
snapshotservice "github.com/containerd/containerd/services/snapshot"
|
||||||
"github.com/containerd/containerd/snapshot"
|
"github.com/containerd/containerd/snapshot"
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
@ -35,6 +38,38 @@ import (
|
|||||||
|
|
||||||
var grpcConn *grpc.ClientConn
|
var grpcConn *grpc.ClientConn
|
||||||
|
|
||||||
|
// appContext returns the context for a command. Should only be called once per
|
||||||
|
// command, near the start.
|
||||||
|
//
|
||||||
|
// This will ensure the namespace is picked up and set the timeout, if one is
|
||||||
|
// defined.
|
||||||
|
func appContext(clicontext *cli.Context) (gocontext.Context, gocontext.CancelFunc) {
|
||||||
|
var (
|
||||||
|
ctx = gocontext.Background()
|
||||||
|
timeout = clicontext.GlobalDuration("timeout")
|
||||||
|
namespace = clicontext.GlobalString("namespace")
|
||||||
|
cancel = func() {}
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx = namespaces.WithNamespace(ctx, namespace)
|
||||||
|
|
||||||
|
if timeout > 0 {
|
||||||
|
ctx, cancel = gocontext.WithTimeout(ctx, timeout)
|
||||||
|
} else {
|
||||||
|
ctx, cancel = gocontext.WithCancel(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx, cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamespacesService(clicontext *cli.Context) (namespaces.Store, error) {
|
||||||
|
conn, err := getGRPCConnection(clicontext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return namespacesservice.NewStoreFromClient(namespacesapi.NewNamespacesClient(conn)), nil
|
||||||
|
}
|
||||||
|
|
||||||
func getContainersService(context *cli.Context) (containersapi.ContainersClient, error) {
|
func getContainersService(context *cli.Context) (containersapi.ContainersClient, error) {
|
||||||
conn, err := getGRPCConnection(context)
|
conn, err := getGRPCConnection(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
2
cmd/dist/active.go
vendored
2
cmd/dist/active.go
vendored
@ -32,7 +32,7 @@ var activeCommand = cli.Command{
|
|||||||
match = context.Args().First()
|
match = context.Args().First()
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(context)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
cs, err := resolveContentStore(context)
|
cs, err := resolveContentStore(context)
|
||||||
|
2
cmd/dist/apply.go
vendored
2
cmd/dist/apply.go
vendored
@ -18,7 +18,7 @@ var applyCommand = cli.Command{
|
|||||||
var (
|
var (
|
||||||
dir = context.Args().First()
|
dir = context.Args().First()
|
||||||
)
|
)
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(context)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
log.G(ctx).Info("applying layer from stdin")
|
log.G(ctx).Info("applying layer from stdin")
|
||||||
|
26
cmd/dist/common.go
vendored
26
cmd/dist/common.go
vendored
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
contextpkg "context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -17,6 +18,7 @@ import (
|
|||||||
imagesapi "github.com/containerd/containerd/api/services/images"
|
imagesapi "github.com/containerd/containerd/api/services/images"
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/images"
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/namespaces"
|
||||||
"github.com/containerd/containerd/remotes"
|
"github.com/containerd/containerd/remotes"
|
||||||
"github.com/containerd/containerd/remotes/docker"
|
"github.com/containerd/containerd/remotes/docker"
|
||||||
"github.com/containerd/containerd/rootfs"
|
"github.com/containerd/containerd/rootfs"
|
||||||
@ -54,6 +56,30 @@ func getClient(context *cli.Context) (*containerd.Client, error) {
|
|||||||
return containerd.New(address)
|
return containerd.New(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// appContext returns the context for a command. Should only be called once per
|
||||||
|
// command, near the start.
|
||||||
|
//
|
||||||
|
// This will ensure the namespace is picked up and set the timeout, if one is
|
||||||
|
// defined.
|
||||||
|
func appContext(clicontext *cli.Context) (contextpkg.Context, contextpkg.CancelFunc) {
|
||||||
|
var (
|
||||||
|
ctx = contextpkg.Background()
|
||||||
|
timeout = clicontext.GlobalDuration("timeout")
|
||||||
|
namespace = clicontext.GlobalString("namespace")
|
||||||
|
cancel = func() {}
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx = namespaces.WithNamespace(ctx, namespace)
|
||||||
|
|
||||||
|
if timeout > 0 {
|
||||||
|
ctx, cancel = contextpkg.WithTimeout(ctx, timeout)
|
||||||
|
} else {
|
||||||
|
ctx, cancel = contextpkg.WithCancel(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx, cancel
|
||||||
|
}
|
||||||
|
|
||||||
func resolveContentStore(context *cli.Context) (content.Store, error) {
|
func resolveContentStore(context *cli.Context) (content.Store, error) {
|
||||||
conn, err := connectGRPC(context)
|
conn, err := connectGRPC(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
2
cmd/dist/delete.go
vendored
2
cmd/dist/delete.go
vendored
@ -25,7 +25,7 @@ var deleteCommand = cli.Command{
|
|||||||
args = []string(context.Args())
|
args = []string(context.Args())
|
||||||
exitError error
|
exitError error
|
||||||
)
|
)
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(context)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
conn, err := connectGRPC(context)
|
conn, err := connectGRPC(context)
|
||||||
|
2
cmd/dist/edit.go
vendored
2
cmd/dist/edit.go
vendored
@ -30,7 +30,7 @@ var editCommand = cli.Command{
|
|||||||
validate = context.String("validate")
|
validate = context.String("validate")
|
||||||
object = context.Args().First()
|
object = context.Args().First()
|
||||||
)
|
)
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(context)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if validate != "" {
|
if validate != "" {
|
||||||
|
2
cmd/dist/fetch.go
vendored
2
cmd/dist/fetch.go
vendored
@ -44,7 +44,7 @@ Most of this is experimental and there are few leaps to make this work.`,
|
|||||||
var (
|
var (
|
||||||
ref = clicontext.Args().First()
|
ref = clicontext.Args().First()
|
||||||
)
|
)
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(clicontext)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
conn, err := connectGRPC(clicontext)
|
conn, err := connectGRPC(clicontext)
|
||||||
|
2
cmd/dist/fetchobject.go
vendored
2
cmd/dist/fetchobject.go
vendored
@ -22,7 +22,7 @@ var fetchObjectCommand = cli.Command{
|
|||||||
var (
|
var (
|
||||||
ref = context.Args().First()
|
ref = context.Args().First()
|
||||||
)
|
)
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(context)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
resolver, err := getResolver(ctx, context)
|
resolver, err := getResolver(ctx, context)
|
||||||
|
2
cmd/dist/get.go
vendored
2
cmd/dist/get.go
vendored
@ -15,7 +15,7 @@ var getCommand = cli.Command{
|
|||||||
Description: "Display the image object.",
|
Description: "Display the image object.",
|
||||||
Flags: []cli.Flag{},
|
Flags: []cli.Flag{},
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(context)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
dgst, err := digest.Parse(context.Args().First())
|
dgst, err := digest.Parse(context.Args().First())
|
||||||
|
4
cmd/dist/images.go
vendored
4
cmd/dist/images.go
vendored
@ -30,7 +30,7 @@ var imagesListCommand = cli.Command{
|
|||||||
Description: `List images registered with containerd.`,
|
Description: `List images registered with containerd.`,
|
||||||
Flags: []cli.Flag{},
|
Flags: []cli.Flag{},
|
||||||
Action: func(clicontext *cli.Context) error {
|
Action: func(clicontext *cli.Context) error {
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(clicontext)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
imageStore, err := resolveImageStore(clicontext)
|
imageStore, err := resolveImageStore(clicontext)
|
||||||
@ -75,7 +75,7 @@ var imageRemoveCommand = cli.Command{
|
|||||||
var (
|
var (
|
||||||
exitErr error
|
exitErr error
|
||||||
)
|
)
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(clicontext)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
imageStore, err := resolveImageStore(clicontext)
|
imageStore, err := resolveImageStore(clicontext)
|
||||||
|
2
cmd/dist/ingest.go
vendored
2
cmd/dist/ingest.go
vendored
@ -31,7 +31,7 @@ var ingestCommand = cli.Command{
|
|||||||
expectedDigest = digest.Digest(context.String("expected-digest"))
|
expectedDigest = digest.Digest(context.String("expected-digest"))
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(context)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if err := expectedDigest.Validate(); expectedDigest != "" && err != nil {
|
if err := expectedDigest.Validate(); expectedDigest != "" && err != nil {
|
||||||
|
2
cmd/dist/list.go
vendored
2
cmd/dist/list.go
vendored
@ -29,7 +29,7 @@ var listCommand = cli.Command{
|
|||||||
quiet = context.Bool("quiet")
|
quiet = context.Bool("quiet")
|
||||||
args = []string(context.Args())
|
args = []string(context.Args())
|
||||||
)
|
)
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(context)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
cs, err := resolveContentStore(context)
|
cs, err := resolveContentStore(context)
|
||||||
|
16
cmd/dist/main.go
vendored
16
cmd/dist/main.go
vendored
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
contextpkg "context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
@ -22,14 +21,6 @@ func init() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func appContext() (contextpkg.Context, contextpkg.CancelFunc) {
|
|
||||||
background := contextpkg.Background()
|
|
||||||
if timeout > 0 {
|
|
||||||
return contextpkg.WithTimeout(background, timeout)
|
|
||||||
}
|
|
||||||
return contextpkg.WithCancel(background)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Name = "dist"
|
app.Name = "dist"
|
||||||
@ -68,6 +59,12 @@ distribution tool
|
|||||||
Usage: "address for containerd's GRPC server",
|
Usage: "address for containerd's GRPC server",
|
||||||
Value: "/run/containerd/containerd.sock",
|
Value: "/run/containerd/containerd.sock",
|
||||||
},
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "namespace, n",
|
||||||
|
Usage: "namespace to use with commands",
|
||||||
|
Value: "default",
|
||||||
|
EnvVar: "CONTAINERD_NAMESPACE",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
imageCommand,
|
imageCommand,
|
||||||
@ -81,7 +78,6 @@ distribution tool
|
|||||||
pushObjectCommand,
|
pushObjectCommand,
|
||||||
}
|
}
|
||||||
app.Before = func(context *cli.Context) error {
|
app.Before = func(context *cli.Context) error {
|
||||||
timeout = context.GlobalDuration("timeout")
|
|
||||||
if context.GlobalBool("debug") {
|
if context.GlobalBool("debug") {
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
}
|
}
|
||||||
|
13
cmd/dist/pull.go
vendored
13
cmd/dist/pull.go
vendored
@ -39,7 +39,7 @@ command. As part of this process, we do the following:
|
|||||||
ref = clicontext.Args().First()
|
ref = clicontext.Args().First()
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(clicontext)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
cs, err := resolveContentStore(clicontext)
|
cs, err := resolveContentStore(clicontext)
|
||||||
@ -100,20 +100,17 @@ command. As part of this process, we do the following:
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
// we need new ctx here
|
// we need new ctx here, since we run on return.
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(clicontext)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
// TODO(stevvooe): This section unpacks the layers and resolves the
|
|
||||||
// root filesystem chainid for the image. For now, we just print
|
|
||||||
// it, but we should keep track of this in the metadata storage.
|
|
||||||
image, err := imageStore.Get(ctx, resolvedImageName)
|
image, err := imageStore.Get(ctx, resolvedImageName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.G(ctx).WithError(err).Fatal("Failed to get image")
|
log.G(ctx).WithError(err).Fatal("failed to get image")
|
||||||
}
|
}
|
||||||
|
|
||||||
layers, err := getImageLayers(ctx, image, cs)
|
layers, err := getImageLayers(ctx, image, cs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.G(ctx).WithError(err).Fatal("Failed to get rootfs layers")
|
log.G(ctx).WithError(err).Fatal("failed to get rootfs layers")
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := connectGRPC(clicontext)
|
conn, err := connectGRPC(clicontext)
|
||||||
|
2
cmd/dist/push.go
vendored
2
cmd/dist/push.go
vendored
@ -48,7 +48,7 @@ var pushCommand = cli.Command{
|
|||||||
desc ocispec.Descriptor
|
desc ocispec.Descriptor
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(clicontext)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
client, err := getClient(clicontext)
|
client, err := getClient(clicontext)
|
||||||
|
2
cmd/dist/pushobject.go
vendored
2
cmd/dist/pushobject.go
vendored
@ -26,7 +26,7 @@ var pushObjectCommand = cli.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(clicontext)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
resolver, err := getResolver(ctx, clicontext)
|
resolver, err := getResolver(ctx, clicontext)
|
||||||
|
4
cmd/dist/rootfs.go
vendored
4
cmd/dist/rootfs.go
vendored
@ -32,7 +32,7 @@ var rootfsUnpackCommand = cli.Command{
|
|||||||
ArgsUsage: "[flags] <digest>",
|
ArgsUsage: "[flags] <digest>",
|
||||||
Flags: []cli.Flag{},
|
Flags: []cli.Flag{},
|
||||||
Action: func(clicontext *cli.Context) error {
|
Action: func(clicontext *cli.Context) error {
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(clicontext)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
dgst, err := digest.Parse(clicontext.Args().First())
|
dgst, err := digest.Parse(clicontext.Args().First())
|
||||||
@ -84,7 +84,7 @@ var rootfsPrepareCommand = cli.Command{
|
|||||||
ArgsUsage: "[flags] <digest> <target>",
|
ArgsUsage: "[flags] <digest> <target>",
|
||||||
Flags: []cli.Flag{},
|
Flags: []cli.Flag{},
|
||||||
Action: func(clicontext *cli.Context) error {
|
Action: func(clicontext *cli.Context) error {
|
||||||
ctx, cancel := appContext()
|
ctx, cancel := appContext(clicontext)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if clicontext.NArg() != 2 {
|
if clicontext.NArg() != 2 {
|
||||||
|
@ -2,7 +2,6 @@ package containerd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -27,7 +26,10 @@ func TestContainerList(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
containers, err := client.Containers(context.Background())
|
ctx, cancel := testContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
containers, err := client.Containers(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("container list returned error %v", err)
|
t.Errorf("container list returned error %v", err)
|
||||||
return
|
return
|
||||||
@ -53,12 +55,16 @@ func TestNewContainer(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
container, err := client.NewContainer(context.Background(), id, WithSpec(spec))
|
|
||||||
|
ctx, cancel := testContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
container, err := client.NewContainer(ctx, id, WithSpec(spec))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer container.Delete(context.Background())
|
defer container.Delete(ctx)
|
||||||
if container.ID() != id {
|
if container.ID() != id {
|
||||||
t.Errorf("expected container id %q but received %q", id, container.ID())
|
t.Errorf("expected container id %q but received %q", id, container.ID())
|
||||||
}
|
}
|
||||||
@ -66,7 +72,7 @@ func TestNewContainer(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := container.Delete(context.Background()); err != nil {
|
if err := container.Delete(ctx); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -83,9 +89,11 @@ func TestContainerStart(t *testing.T) {
|
|||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx = context.Background()
|
ctx, cancel = testContext()
|
||||||
id = "ContainerStart"
|
id = "ContainerStart"
|
||||||
)
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
image, err := client.GetImage(ctx, testImage)
|
image, err := client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -151,10 +159,12 @@ func TestContainerOutput(t *testing.T) {
|
|||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx = context.Background()
|
ctx, cancel = testContext()
|
||||||
id = "ContainerOutput"
|
id = "ContainerOutput"
|
||||||
expected = "kingkoye"
|
expected = "kingkoye"
|
||||||
)
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
image, err := client.GetImage(ctx, testImage)
|
image, err := client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -222,9 +232,11 @@ func TestContainerExec(t *testing.T) {
|
|||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx = context.Background()
|
ctx, cancel = testContext()
|
||||||
id = "ContainerExec"
|
id = "ContainerExec"
|
||||||
)
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
image, err := client.GetImage(ctx, testImage)
|
image, err := client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -307,9 +319,11 @@ func TestContainerProcesses(t *testing.T) {
|
|||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx = context.Background()
|
ctx, cancel = testContext()
|
||||||
id = "ContainerProcesses"
|
id = "ContainerProcesses"
|
||||||
)
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
image, err := client.GetImage(ctx, testImage)
|
image, err := client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -378,9 +392,11 @@ func TestContainerCloseStdin(t *testing.T) {
|
|||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx = context.Background()
|
ctx, cancel = testContext()
|
||||||
id = "ContainerCloseStdin"
|
id = "ContainerCloseStdin"
|
||||||
)
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
image, err := client.GetImage(ctx, testImage)
|
image, err := client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -460,9 +476,11 @@ func TestContainerAttach(t *testing.T) {
|
|||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx = context.Background()
|
ctx, cancel = testContext()
|
||||||
id = "ContainerAttach"
|
id = "ContainerAttach"
|
||||||
)
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
image, err := client.GetImage(ctx, testImage)
|
image, err := client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
@ -2,13 +2,36 @@ package metadata
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/boltdb/bolt"
|
"github.com/boltdb/bolt"
|
||||||
"github.com/containerd/containerd/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The layout where a "/" delineates a bucket is desribed in the following
|
||||||
|
// section. Please try to follow this as closely as possible when adding
|
||||||
|
// functionality. We can bolster this with helpers and more structure if that
|
||||||
|
// becomes an issue.
|
||||||
|
//
|
||||||
|
// Generically, we try to do the following:
|
||||||
|
//
|
||||||
|
// <version>/<namespace>/<object>/<key> -> <field>
|
||||||
|
//
|
||||||
|
// version: Currently, this is "v1". Additions can be made to v1 in a backwards
|
||||||
|
// compatible way. If the layout changes, a new version must be made, along
|
||||||
|
// with a migration.
|
||||||
|
//
|
||||||
|
// namespace: the namespace to which this object belongs.
|
||||||
|
//
|
||||||
|
// object: defines which object set is stored in the bucket. There are two
|
||||||
|
// special objects, "labels" and "indexes". The "labels" bucket stores the
|
||||||
|
// labels for the parent namespace. The "indexes" object is reserved for
|
||||||
|
// indexing objects, if we require in the future.
|
||||||
|
//
|
||||||
|
// key: object-specific key identifying the storage bucket for the objects
|
||||||
|
// contents.
|
||||||
var (
|
var (
|
||||||
bucketKeyStorageVersion = []byte("v1")
|
bucketKeyVersion = []byte("v1")
|
||||||
bucketKeyImages = []byte("images")
|
bucketKeyObjectLabels = []byte("labels") // stores the labels for a namespace.
|
||||||
bucketKeyContainers = []byte("containers")
|
bucketKeyObjectIndexes = []byte("indexes") // reserved
|
||||||
|
bucketKeyObjectImages = []byte("images") // stores image objects
|
||||||
|
bucketKeyObjectContainers = []byte("containers") // stores container objects
|
||||||
|
|
||||||
bucketKeyDigest = []byte("digest")
|
bucketKeyDigest = []byte("digest")
|
||||||
bucketKeyMediaType = []byte("mediatype")
|
bucketKeyMediaType = []byte("mediatype")
|
||||||
@ -22,21 +45,6 @@ var (
|
|||||||
bucketKeyUpdatedAt = []byte("updatedat")
|
bucketKeyUpdatedAt = []byte("updatedat")
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitDB will initialize the database for use. The database must be opened for
|
|
||||||
// write and the caller must not be holding an open transaction.
|
|
||||||
func InitDB(db *bolt.DB) error {
|
|
||||||
log.L.Debug("init db")
|
|
||||||
return db.Update(func(tx *bolt.Tx) error {
|
|
||||||
if _, err := createBucketIfNotExists(tx, bucketKeyStorageVersion, bucketKeyImages); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := createBucketIfNotExists(tx, bucketKeyStorageVersion, bucketKeyContainers); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBucket(tx *bolt.Tx, keys ...[]byte) *bolt.Bucket {
|
func getBucket(tx *bolt.Tx, keys ...[]byte) *bolt.Bucket {
|
||||||
bkt := tx.Bucket(keys[0])
|
bkt := tx.Bucket(keys[0])
|
||||||
|
|
||||||
@ -66,44 +74,52 @@ func createBucketIfNotExists(tx *bolt.Tx, keys ...[]byte) (*bolt.Bucket, error)
|
|||||||
return bkt, nil
|
return bkt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func withImagesBucket(tx *bolt.Tx, fn func(bkt *bolt.Bucket) error) error {
|
func namespaceLabelsBucketPath(namespace string) [][]byte {
|
||||||
bkt := getImagesBucket(tx)
|
return [][]byte{bucketKeyVersion, []byte(namespace), bucketKeyObjectLabels}
|
||||||
if bkt == nil {
|
}
|
||||||
return ErrNotFound
|
|
||||||
|
func withNamespacesLabelsBucket(tx *bolt.Tx, namespace string, fn func(bkt *bolt.Bucket) error) error {
|
||||||
|
bkt, err := createBucketIfNotExists(tx, namespaceLabelsBucketPath(namespace)...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return fn(bkt)
|
return fn(bkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func withImageBucket(tx *bolt.Tx, name string, fn func(bkt *bolt.Bucket) error) error {
|
func getNamespaceLabelsBucket(tx *bolt.Tx, namespace string) *bolt.Bucket {
|
||||||
bkt := getImageBucket(tx, name)
|
return getBucket(tx, namespaceLabelsBucketPath(namespace)...)
|
||||||
if bkt == nil {
|
}
|
||||||
return ErrNotFound
|
|
||||||
|
func imagesBucketPath(namespace string) [][]byte {
|
||||||
|
return [][]byte{bucketKeyVersion, []byte(namespace), bucketKeyObjectImages}
|
||||||
|
}
|
||||||
|
|
||||||
|
func withImagesBucket(tx *bolt.Tx, namespace string, fn func(bkt *bolt.Bucket) error) error {
|
||||||
|
bkt, err := createBucketIfNotExists(tx, imagesBucketPath(namespace)...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return fn(bkt)
|
return fn(bkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getImagesBucket(tx *bolt.Tx) *bolt.Bucket {
|
func getImagesBucket(tx *bolt.Tx, namespace string) *bolt.Bucket {
|
||||||
return getBucket(tx, bucketKeyStorageVersion, bucketKeyImages)
|
return getBucket(tx, imagesBucketPath(namespace)...)
|
||||||
}
|
|
||||||
|
|
||||||
func getImageBucket(tx *bolt.Tx, name string) *bolt.Bucket {
|
|
||||||
return getBucket(tx, bucketKeyStorageVersion, bucketKeyImages, []byte(name))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createContainersBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
|
func createContainersBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
|
||||||
bkt, err := tx.CreateBucketIfNotExists(bucketKeyStorageVersion)
|
bkt, err := createBucketIfNotExists(tx, bucketKeyVersion, bucketKeyObjectContainers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return bkt.CreateBucketIfNotExists(bucketKeyContainers)
|
return bkt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getContainersBucket(tx *bolt.Tx) *bolt.Bucket {
|
func getContainersBucket(tx *bolt.Tx) *bolt.Bucket {
|
||||||
return getBucket(tx, bucketKeyStorageVersion, bucketKeyContainers)
|
return getBucket(tx, bucketKeyVersion, bucketKeyObjectContainers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getContainerBucket(tx *bolt.Tx, id string) *bolt.Bucket {
|
func getContainerBucket(tx *bolt.Tx, id string) *bolt.Bucket {
|
||||||
return getBucket(tx, bucketKeyStorageVersion, bucketKeyContainers, []byte(id))
|
return getBucket(tx, bucketKeyVersion, bucketKeyObjectContainers, []byte(id))
|
||||||
}
|
}
|
||||||
|
@ -35,13 +35,13 @@ func (s *containerStore) Get(ctx context.Context, id string) (containers.Contain
|
|||||||
|
|
||||||
func (s *containerStore) List(ctx context.Context, filter string) ([]containers.Container, error) {
|
func (s *containerStore) List(ctx context.Context, filter string) ([]containers.Container, error) {
|
||||||
var (
|
var (
|
||||||
m = []containers.Container{}
|
m []containers.Container
|
||||||
bkt = getContainersBucket(s.tx)
|
bkt = getContainersBucket(s.tx)
|
||||||
)
|
)
|
||||||
if bkt == nil {
|
if bkt == nil {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
err := bkt.ForEach(func(k, v []byte) error {
|
if err := bkt.ForEach(func(k, v []byte) error {
|
||||||
cbkt := bkt.Bucket(k)
|
cbkt := bkt.Bucket(k)
|
||||||
if cbkt == nil {
|
if cbkt == nil {
|
||||||
return nil
|
return nil
|
||||||
@ -53,8 +53,7 @@ func (s *containerStore) List(ctx context.Context, filter string) ([]containers.
|
|||||||
}
|
}
|
||||||
m = append(m, container)
|
m = append(m, container)
|
||||||
return nil
|
return nil
|
||||||
})
|
}); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import "github.com/pkg/errors"
|
|||||||
var (
|
var (
|
||||||
ErrExists = errors.New("metadata: exists")
|
ErrExists = errors.New("metadata: exists")
|
||||||
ErrNotFound = errors.New("metadata: not found")
|
ErrNotFound = errors.New("metadata: not found")
|
||||||
|
ErrNotEmpty = errors.New("metadata: namespace not empty")
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsNotFound returns true if the error is due to a missing image.
|
// IsNotFound returns true if the error is due to a missing image.
|
||||||
@ -15,3 +16,7 @@ func IsNotFound(err error) bool {
|
|||||||
func IsExists(err error) bool {
|
func IsExists(err error) bool {
|
||||||
return errors.Cause(err) == ErrExists
|
return errors.Cause(err) == ErrExists
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsNotEmpty(err error) bool {
|
||||||
|
return errors.Cause(err) == ErrNotEmpty
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/boltdb/bolt"
|
"github.com/boltdb/bolt"
|
||||||
"github.com/containerd/containerd/images"
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/namespaces"
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
)
|
)
|
||||||
@ -21,10 +22,24 @@ func NewImageStore(tx *bolt.Tx) images.Store {
|
|||||||
|
|
||||||
func (s *imageStore) Get(ctx context.Context, name string) (images.Image, error) {
|
func (s *imageStore) Get(ctx context.Context, name string) (images.Image, error) {
|
||||||
var image images.Image
|
var image images.Image
|
||||||
if err := withImageBucket(s.tx, name, func(bkt *bolt.Bucket) error {
|
|
||||||
image.Name = name
|
namespace, err := namespaces.NamespaceRequired(ctx)
|
||||||
return readImage(&image, bkt)
|
if err != nil {
|
||||||
}); err != nil {
|
return images.Image{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bkt := getImagesBucket(s.tx, namespace)
|
||||||
|
if bkt == nil {
|
||||||
|
return images.Image{}, ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
ibkt := bkt.Bucket([]byte(name))
|
||||||
|
if ibkt == nil {
|
||||||
|
return images.Image{}, ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
image.Name = name
|
||||||
|
if err := readImage(&image, ibkt); err != nil {
|
||||||
return images.Image{}, err
|
return images.Image{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +47,12 @@ func (s *imageStore) Get(ctx context.Context, name string) (images.Image, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *imageStore) Put(ctx context.Context, name string, desc ocispec.Descriptor) error {
|
func (s *imageStore) Put(ctx context.Context, name string, desc ocispec.Descriptor) error {
|
||||||
return withImagesBucket(s.tx, func(bkt *bolt.Bucket) error {
|
namespace, err := namespaces.NamespaceRequired(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return withImagesBucket(s.tx, namespace, func(bkt *bolt.Bucket) error {
|
||||||
ibkt, err := bkt.CreateBucketIfNotExists([]byte(name))
|
ibkt, err := bkt.CreateBucketIfNotExists([]byte(name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -64,23 +84,30 @@ func (s *imageStore) Put(ctx context.Context, name string, desc ocispec.Descript
|
|||||||
|
|
||||||
func (s *imageStore) List(ctx context.Context) ([]images.Image, error) {
|
func (s *imageStore) List(ctx context.Context) ([]images.Image, error) {
|
||||||
var m []images.Image
|
var m []images.Image
|
||||||
|
namespace, err := namespaces.NamespaceRequired(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err := withImagesBucket(s.tx, func(bkt *bolt.Bucket) error {
|
bkt := getImagesBucket(s.tx, namespace)
|
||||||
return bkt.ForEach(func(k, v []byte) error {
|
if bkt == nil {
|
||||||
var (
|
return nil, nil // empty store
|
||||||
image = images.Image{
|
}
|
||||||
Name: string(k),
|
|
||||||
}
|
|
||||||
kbkt = bkt.Bucket(k)
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := readImage(&image, kbkt); err != nil {
|
if err := bkt.ForEach(func(k, v []byte) error {
|
||||||
return err
|
var (
|
||||||
|
image = images.Image{
|
||||||
|
Name: string(k),
|
||||||
}
|
}
|
||||||
|
kbkt = bkt.Bucket(k)
|
||||||
|
)
|
||||||
|
|
||||||
m = append(m, image)
|
if err := readImage(&image, kbkt); err != nil {
|
||||||
return nil
|
return err
|
||||||
})
|
}
|
||||||
|
|
||||||
|
m = append(m, image)
|
||||||
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -89,7 +116,12 @@ func (s *imageStore) List(ctx context.Context) ([]images.Image, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *imageStore) Delete(ctx context.Context, name string) error {
|
func (s *imageStore) Delete(ctx context.Context, name string) error {
|
||||||
return withImagesBucket(s.tx, func(bkt *bolt.Bucket) error {
|
namespace, err := namespaces.NamespaceRequired(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return withImagesBucket(s.tx, namespace, func(bkt *bolt.Bucket) error {
|
||||||
err := bkt.DeleteBucket([]byte(name))
|
err := bkt.DeleteBucket([]byte(name))
|
||||||
if err == bolt.ErrBucketNotFound {
|
if err == bolt.ErrBucketNotFound {
|
||||||
return ErrNotFound
|
return ErrNotFound
|
||||||
|
145
metadata/namespaces.go
Normal file
145
metadata/namespaces.go
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/boltdb/bolt"
|
||||||
|
"github.com/containerd/containerd/namespaces"
|
||||||
|
)
|
||||||
|
|
||||||
|
type namespaceStore struct {
|
||||||
|
tx *bolt.Tx
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNamespaceStore(tx *bolt.Tx) namespaces.Store {
|
||||||
|
return &namespaceStore{tx: tx}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *namespaceStore) Create(ctx context.Context, namespace string, labels map[string]string) error {
|
||||||
|
topbkt, err := createBucketIfNotExists(s.tx, bucketKeyVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// provides the already exists error.
|
||||||
|
bkt, err := topbkt.CreateBucket([]byte(namespace))
|
||||||
|
if err != nil {
|
||||||
|
if err == bolt.ErrBucketExists {
|
||||||
|
return ErrExists
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
lbkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectLabels)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range labels {
|
||||||
|
if err := lbkt.Put([]byte(k), []byte(v)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *namespaceStore) Labels(ctx context.Context, namespace string) (map[string]string, error) {
|
||||||
|
labels := map[string]string{}
|
||||||
|
|
||||||
|
bkt := getNamespaceLabelsBucket(s.tx, namespace)
|
||||||
|
if bkt == nil {
|
||||||
|
return labels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bkt.ForEach(func(k, v []byte) error {
|
||||||
|
labels[string(k)] = string(v)
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return labels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *namespaceStore) SetLabel(ctx context.Context, namespace, key, value string) error {
|
||||||
|
return withNamespacesLabelsBucket(s.tx, namespace, func(bkt *bolt.Bucket) error {
|
||||||
|
if value == "" {
|
||||||
|
return bkt.Delete([]byte(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
return bkt.Put([]byte(key), []byte(value))
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *namespaceStore) List(ctx context.Context) ([]string, error) {
|
||||||
|
bkt := getBucket(s.tx, bucketKeyVersion)
|
||||||
|
if bkt == nil {
|
||||||
|
return nil, nil // no namespaces!
|
||||||
|
}
|
||||||
|
|
||||||
|
var namespaces []string
|
||||||
|
if err := bkt.ForEach(func(k, v []byte) error {
|
||||||
|
if v != nil {
|
||||||
|
return nil // not a bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
namespaces = append(namespaces, string(k))
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return namespaces, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *namespaceStore) Delete(ctx context.Context, namespace string) error {
|
||||||
|
bkt := getBucket(s.tx, bucketKeyVersion)
|
||||||
|
if empty, err := s.namespaceEmpty(ctx, namespace); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !empty {
|
||||||
|
return ErrNotEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bkt.DeleteBucket([]byte(namespace)); err != nil {
|
||||||
|
if err == bolt.ErrBucketNotFound {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *namespaceStore) namespaceEmpty(ctx context.Context, namespace string) (bool, error) {
|
||||||
|
ctx = namespaces.WithNamespace(ctx, namespace)
|
||||||
|
|
||||||
|
// need to check the various object stores.
|
||||||
|
|
||||||
|
imageStore := NewImageStore(s.tx)
|
||||||
|
images, err := imageStore.List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if len(images) > 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
containerStore := NewContainerStore(s.tx)
|
||||||
|
containers, err := containerStore.List(ctx, "")
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(containers) > 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(stevvooe): Need to add check for content store, as well. Still need
|
||||||
|
// to make content store namespace aware.
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
42
namespaces/context.go
Normal file
42
namespaces/context.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNamespaceRequired = errors.New("namespace is required")
|
||||||
|
)
|
||||||
|
|
||||||
|
type namespaceKey struct{}
|
||||||
|
|
||||||
|
func WithNamespace(ctx context.Context, namespace string) context.Context {
|
||||||
|
ctx = context.WithValue(ctx, namespaceKey{}, namespace) // set our key for namespace
|
||||||
|
|
||||||
|
// also store on the grpc headers so it gets picked up by any clients that
|
||||||
|
// are using this.
|
||||||
|
return withGRPCNamespaceHeader(ctx, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Namespace(ctx context.Context) (string, bool) {
|
||||||
|
namespace, ok := ctx.Value(namespaceKey{}).(string)
|
||||||
|
if !ok {
|
||||||
|
return fromGRPCHeader(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return namespace, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsNamespaceRequired(err error) bool {
|
||||||
|
return errors.Cause(err) == errNamespaceRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
func NamespaceRequired(ctx context.Context) (string, error) {
|
||||||
|
namespace, ok := Namespace(ctx)
|
||||||
|
if !ok || namespace == "" {
|
||||||
|
return "", errNamespaceRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
return namespace, nil
|
||||||
|
}
|
30
namespaces/context_test.go
Normal file
30
namespaces/context_test.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestContext(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
namespace, ok := Namespace(ctx)
|
||||||
|
if ok {
|
||||||
|
t.Fatal("namespace should not be present")
|
||||||
|
}
|
||||||
|
|
||||||
|
if namespace != "" {
|
||||||
|
t.Fatalf("namespace should not be defined: got %q", namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := "test"
|
||||||
|
nctx := WithNamespace(ctx, expected)
|
||||||
|
|
||||||
|
namespace, ok = Namespace(nctx)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("expected to find a namespace")
|
||||||
|
}
|
||||||
|
|
||||||
|
if namespace != expected {
|
||||||
|
t.Fatalf("unexpected namespace: %q != %q", namespace, expected)
|
||||||
|
}
|
||||||
|
}
|
44
namespaces/grpc.go
Normal file
44
namespaces/grpc.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GRPCHeader defines the header name for specifying a containerd namespace.
|
||||||
|
GRPCHeader = "containerd-namespace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE(stevvooe): We can stub this file out if we don't want a grpc dependency here.
|
||||||
|
|
||||||
|
func withGRPCNamespaceHeader(ctx context.Context, namespace string) context.Context {
|
||||||
|
// also store on the grpc headers so it gets picked up by any clients that
|
||||||
|
// are using this.
|
||||||
|
nsheader := metadata.Pairs(GRPCHeader, namespace)
|
||||||
|
md, ok := metadata.FromOutgoingContext(ctx) // merge with outgoing context.
|
||||||
|
if !ok {
|
||||||
|
md = nsheader
|
||||||
|
} else {
|
||||||
|
// order ensures the latest is first in this list.
|
||||||
|
md = metadata.Join(nsheader, md)
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.NewOutgoingContext(ctx, md)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromGRPCHeader(ctx context.Context) (string, bool) {
|
||||||
|
// try to extract for use in grpc servers.
|
||||||
|
md, ok := metadata.FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
// TODO(stevvooe): Check outgoing context?
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
values := md[GRPCHeader]
|
||||||
|
if len(values) == 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return values[0], true
|
||||||
|
}
|
21
namespaces/store.go
Normal file
21
namespaces/store.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package namespaces
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// Store provides introspection about namespaces.
|
||||||
|
//
|
||||||
|
// Note that these are slightly different than other objects, which are record
|
||||||
|
// oriented. A namespace is really just a name and a set of labels. Objects
|
||||||
|
// that belong to a namespace are returned when the namespace is assigned to a
|
||||||
|
// given context.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
type Store interface {
|
||||||
|
Create(ctx context.Context, namespace string, labels map[string]string) error
|
||||||
|
Labels(ctx context.Context, namespace string) (map[string]string, error)
|
||||||
|
SetLabel(ctx context.Context, namespace, key, value string) error
|
||||||
|
List(ctx context.Context) ([]string, error)
|
||||||
|
|
||||||
|
// Delete removes the namespace. The namespace must be empty to be deleted.
|
||||||
|
Delete(ctx context.Context, namespace string) error
|
||||||
|
}
|
@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/containerd/containerd/api/types/descriptor"
|
"github.com/containerd/containerd/api/types/descriptor"
|
||||||
"github.com/containerd/containerd/images"
|
"github.com/containerd/containerd/images"
|
||||||
"github.com/containerd/containerd/metadata"
|
"github.com/containerd/containerd/metadata"
|
||||||
|
"github.com/containerd/containerd/namespaces"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
@ -82,6 +83,8 @@ func mapGRPCError(err error, id string) error {
|
|||||||
return grpc.Errorf(codes.NotFound, "image %v not found", id)
|
return grpc.Errorf(codes.NotFound, "image %v not found", id)
|
||||||
case metadata.IsExists(err):
|
case metadata.IsExists(err):
|
||||||
return grpc.Errorf(codes.AlreadyExists, "image %v already exists", id)
|
return grpc.Errorf(codes.AlreadyExists, "image %v already exists", id)
|
||||||
|
case namespaces.IsNamespaceRequired(err):
|
||||||
|
return grpc.Errorf(codes.InvalidArgument, "namespace required, please set %q header", namespaces.GRPCHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
95
services/namespaces/client.go
Normal file
95
services/namespaces/client.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
api "github.com/containerd/containerd/api/services/namespaces"
|
||||||
|
"github.com/containerd/containerd/namespaces"
|
||||||
|
"github.com/gogo/protobuf/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewStoreFromClient(client api.NamespacesClient) namespaces.Store {
|
||||||
|
return &remote{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
type remote struct {
|
||||||
|
client api.NamespacesClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *remote) Create(ctx context.Context, namespace string, labels map[string]string) error {
|
||||||
|
var req api.CreateNamespaceRequest
|
||||||
|
|
||||||
|
req.Namespace = api.Namespace{
|
||||||
|
Name: namespace,
|
||||||
|
Labels: labels,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := r.client.Create(ctx, &req)
|
||||||
|
if err != nil {
|
||||||
|
return rewriteGRPCError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *remote) Labels(ctx context.Context, namespace string) (map[string]string, error) {
|
||||||
|
var req api.GetNamespaceRequest
|
||||||
|
req.Name = namespace
|
||||||
|
|
||||||
|
resp, err := r.client.Get(ctx, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, rewriteGRPCError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Namespace.Labels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *remote) SetLabel(ctx context.Context, namespace, key, value string) error {
|
||||||
|
var req api.UpdateNamespaceRequest
|
||||||
|
|
||||||
|
req.Namespace = api.Namespace{
|
||||||
|
Name: namespace,
|
||||||
|
Labels: map[string]string{key: value},
|
||||||
|
}
|
||||||
|
|
||||||
|
req.UpdateMask = &types.FieldMask{
|
||||||
|
Paths: []string{strings.Join([]string{"labels", key}, ".")},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := r.client.Update(ctx, &req)
|
||||||
|
if err != nil {
|
||||||
|
return rewriteGRPCError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *remote) List(ctx context.Context) ([]string, error) {
|
||||||
|
var req api.ListNamespacesRequest
|
||||||
|
|
||||||
|
resp, err := r.client.List(ctx, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, rewriteGRPCError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var namespaces []string
|
||||||
|
|
||||||
|
for _, ns := range resp.Namespaces {
|
||||||
|
namespaces = append(namespaces, ns.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return namespaces, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *remote) Delete(ctx context.Context, namespace string) error {
|
||||||
|
var req api.DeleteNamespaceRequest
|
||||||
|
|
||||||
|
req.Name = namespace
|
||||||
|
_, err := r.client.Delete(ctx, &req)
|
||||||
|
if err != nil {
|
||||||
|
return rewriteGRPCError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
36
services/namespaces/helpers.go
Normal file
36
services/namespaces/helpers.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containerd/containerd/metadata"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mapGRPCError(err error, id string) error {
|
||||||
|
switch {
|
||||||
|
case metadata.IsNotFound(err):
|
||||||
|
return grpc.Errorf(codes.NotFound, "namespace %v not found", id)
|
||||||
|
case metadata.IsExists(err):
|
||||||
|
return grpc.Errorf(codes.AlreadyExists, "namespace %v already exists", id)
|
||||||
|
case metadata.IsNotEmpty(err):
|
||||||
|
return grpc.Errorf(codes.FailedPrecondition, "namespace %v must be empty", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func rewriteGRPCError(err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch grpc.Code(errors.Cause(err)) {
|
||||||
|
case codes.AlreadyExists:
|
||||||
|
return metadata.ErrExists
|
||||||
|
case codes.NotFound:
|
||||||
|
return metadata.ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
164
services/namespaces/service.go
Normal file
164
services/namespaces/service.go
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
package namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/boltdb/bolt"
|
||||||
|
api "github.com/containerd/containerd/api/services/namespaces"
|
||||||
|
"github.com/containerd/containerd/metadata"
|
||||||
|
"github.com/containerd/containerd/namespaces"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/golang/protobuf/ptypes/empty"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register("namespaces-grpc", &plugin.Registration{
|
||||||
|
Type: plugin.GRPCPlugin,
|
||||||
|
Init: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
return NewService(ic.Meta), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
db *bolt.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ api.NamespacesServer = &Service{}
|
||||||
|
|
||||||
|
func NewService(db *bolt.DB) api.NamespacesServer {
|
||||||
|
return &Service{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Register(server *grpc.Server) error {
|
||||||
|
api.RegisterNamespacesServer(server, s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Get(ctx context.Context, req *api.GetNamespaceRequest) (*api.GetNamespaceResponse, error) {
|
||||||
|
var resp api.GetNamespaceResponse
|
||||||
|
|
||||||
|
return &resp, s.withStoreView(ctx, func(ctx context.Context, store namespaces.Store) error {
|
||||||
|
labels, err := store.Labels(ctx, req.Name)
|
||||||
|
if err != nil {
|
||||||
|
return mapGRPCError(err, req.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Namespace = api.Namespace{
|
||||||
|
Name: req.Name,
|
||||||
|
Labels: labels,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) List(ctx context.Context, req *api.ListNamespacesRequest) (*api.ListNamespacesResponse, error) {
|
||||||
|
var resp api.ListNamespacesResponse
|
||||||
|
|
||||||
|
return &resp, s.withStoreView(ctx, func(ctx context.Context, store namespaces.Store) error {
|
||||||
|
namespaces, err := store.List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, namespace := range namespaces {
|
||||||
|
labels, err := store.Labels(ctx, namespace)
|
||||||
|
if err != nil {
|
||||||
|
// In general, this should be unlikely, since we are holding a
|
||||||
|
// transaction to service this request.
|
||||||
|
return mapGRPCError(err, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Namespaces = append(resp.Namespaces, api.Namespace{
|
||||||
|
Name: namespace,
|
||||||
|
Labels: labels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Create(ctx context.Context, req *api.CreateNamespaceRequest) (*api.CreateNamespaceResponse, error) {
|
||||||
|
var resp api.CreateNamespaceResponse
|
||||||
|
|
||||||
|
return &resp, s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error {
|
||||||
|
if err := store.Create(ctx, req.Namespace.Name, req.Namespace.Labels); err != nil {
|
||||||
|
return mapGRPCError(err, req.Namespace.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range req.Namespace.Labels {
|
||||||
|
if err := store.SetLabel(ctx, req.Namespace.Name, k, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Namespace = req.Namespace
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Update(ctx context.Context, req *api.UpdateNamespaceRequest) (*api.UpdateNamespaceResponse, error) {
|
||||||
|
var resp api.UpdateNamespaceResponse
|
||||||
|
return &resp, s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error {
|
||||||
|
|
||||||
|
if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 {
|
||||||
|
for _, path := range req.UpdateMask.Paths {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(path, "labels."):
|
||||||
|
key := strings.TrimPrefix(path, "labels.")
|
||||||
|
if err := store.SetLabel(ctx, req.Namespace.Name, key, req.Namespace.Labels[key]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return grpc.Errorf(codes.InvalidArgument, "cannot update %q field", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// clear out the existing labels and then set them to the incoming request.
|
||||||
|
// get current set of labels
|
||||||
|
labels, err := store.Labels(ctx, req.Namespace.Name)
|
||||||
|
if err != nil {
|
||||||
|
return mapGRPCError(err, req.Namespace.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := range labels {
|
||||||
|
if err := store.SetLabel(ctx, req.Namespace.Name, k, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range req.Namespace.Labels {
|
||||||
|
if err := store.SetLabel(ctx, req.Namespace.Name, k, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Delete(ctx context.Context, req *api.DeleteNamespaceRequest) (*empty.Empty, error) {
|
||||||
|
return &empty.Empty{}, s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error {
|
||||||
|
return mapGRPCError(store.Delete(ctx, req.Name), req.Name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) withStore(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) func(tx *bolt.Tx) error {
|
||||||
|
return func(tx *bolt.Tx) error { return fn(ctx, metadata.NewNamespaceStore(tx)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) withStoreView(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) error {
|
||||||
|
return s.db.View(s.withStore(ctx, fn))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) withStoreUpdate(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) error {
|
||||||
|
return s.db.Update(s.withStore(ctx, fn))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user