Merge pull request #106 from tonistiigi/dockerfile-frontend
base of dockerfile frontenddocker-18.09
commit
e1e5a8bfac
|
@ -171,6 +171,8 @@ type SolveRequest struct {
|
|||
Exporter string `protobuf:"bytes,3,opt,name=Exporter,proto3" json:"Exporter,omitempty"`
|
||||
ExporterAttrs map[string]string `protobuf:"bytes,4,rep,name=ExporterAttrs" json:"ExporterAttrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
Session string `protobuf:"bytes,5,opt,name=Session,proto3" json:"Session,omitempty"`
|
||||
Frontend string `protobuf:"bytes,6,opt,name=Frontend,proto3" json:"Frontend,omitempty"`
|
||||
FrontendAttrs map[string]string `protobuf:"bytes,7,rep,name=FrontendAttrs" json:"FrontendAttrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
}
|
||||
|
||||
func (m *SolveRequest) Reset() { *m = SolveRequest{} }
|
||||
|
@ -213,6 +215,20 @@ func (m *SolveRequest) GetSession() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (m *SolveRequest) GetFrontend() string {
|
||||
if m != nil {
|
||||
return m.Frontend
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *SolveRequest) GetFrontendAttrs() map[string]string {
|
||||
if m != nil {
|
||||
return m.FrontendAttrs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SolveResponse struct {
|
||||
Vtx []*Vertex `protobuf:"bytes,1,rep,name=vtx" json:"vtx,omitempty"`
|
||||
}
|
||||
|
@ -884,6 +900,29 @@ func (m *SolveRequest) MarshalTo(dAtA []byte) (int, error) {
|
|||
i = encodeVarintControl(dAtA, i, uint64(len(m.Session)))
|
||||
i += copy(dAtA[i:], m.Session)
|
||||
}
|
||||
if len(m.Frontend) > 0 {
|
||||
dAtA[i] = 0x32
|
||||
i++
|
||||
i = encodeVarintControl(dAtA, i, uint64(len(m.Frontend)))
|
||||
i += copy(dAtA[i:], m.Frontend)
|
||||
}
|
||||
if len(m.FrontendAttrs) > 0 {
|
||||
for k, _ := range m.FrontendAttrs {
|
||||
dAtA[i] = 0x3a
|
||||
i++
|
||||
v := m.FrontendAttrs[k]
|
||||
mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v)))
|
||||
i = encodeVarintControl(dAtA, i, uint64(mapSize))
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintControl(dAtA, i, uint64(len(k)))
|
||||
i += copy(dAtA[i:], k)
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintControl(dAtA, i, uint64(len(v)))
|
||||
i += copy(dAtA[i:], v)
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
|
@ -1337,6 +1376,18 @@ func (m *SolveRequest) Size() (n int) {
|
|||
if l > 0 {
|
||||
n += 1 + l + sovControl(uint64(l))
|
||||
}
|
||||
l = len(m.Frontend)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovControl(uint64(l))
|
||||
}
|
||||
if len(m.FrontendAttrs) > 0 {
|
||||
for k, v := range m.FrontendAttrs {
|
||||
_ = k
|
||||
_ = v
|
||||
mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v)))
|
||||
n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
|
@ -2200,6 +2251,151 @@ func (m *SolveRequest) Unmarshal(dAtA []byte) error {
|
|||
}
|
||||
m.Session = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 6:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Frontend", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowControl
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthControl
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Frontend = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 7:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field FrontendAttrs", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowControl
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthControl
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
var keykey uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowControl
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
keykey |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
var stringLenmapkey uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowControl
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLenmapkey |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLenmapkey := int(stringLenmapkey)
|
||||
if intStringLenmapkey < 0 {
|
||||
return ErrInvalidLengthControl
|
||||
}
|
||||
postStringIndexmapkey := iNdEx + intStringLenmapkey
|
||||
if postStringIndexmapkey > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
mapkey := string(dAtA[iNdEx:postStringIndexmapkey])
|
||||
iNdEx = postStringIndexmapkey
|
||||
if m.FrontendAttrs == nil {
|
||||
m.FrontendAttrs = make(map[string]string)
|
||||
}
|
||||
if iNdEx < postIndex {
|
||||
var valuekey uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowControl
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
valuekey |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
var stringLenmapvalue uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowControl
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLenmapvalue |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLenmapvalue := int(stringLenmapvalue)
|
||||
if intStringLenmapvalue < 0 {
|
||||
return ErrInvalidLengthControl
|
||||
}
|
||||
postStringIndexmapvalue := iNdEx + intStringLenmapvalue
|
||||
if postStringIndexmapvalue > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
mapvalue := string(dAtA[iNdEx:postStringIndexmapvalue])
|
||||
iNdEx = postStringIndexmapvalue
|
||||
m.FrontendAttrs[mapkey] = mapvalue
|
||||
} else {
|
||||
var mapvalue string
|
||||
m.FrontendAttrs[mapkey] = mapvalue
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipControl(dAtA[iNdEx:])
|
||||
|
@ -3424,64 +3620,66 @@ var (
|
|||
func init() { proto.RegisterFile("control.proto", fileDescriptorControl) }
|
||||
|
||||
var fileDescriptorControl = []byte{
|
||||
// 929 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x56, 0xc1, 0x6e, 0xdb, 0x46,
|
||||
0x10, 0x2d, 0x49, 0x99, 0x92, 0x46, 0x72, 0xe0, 0x2e, 0x8a, 0x80, 0x50, 0x51, 0x49, 0x65, 0x2f,
|
||||
0x82, 0x81, 0xd0, 0x89, 0xda, 0x02, 0x85, 0x0b, 0x14, 0x89, 0xac, 0x00, 0xb5, 0x11, 0x03, 0xc5,
|
||||
0x3a, 0x6e, 0xcf, 0x94, 0x34, 0x66, 0x08, 0x53, 0x5c, 0x75, 0x77, 0x29, 0x58, 0xfd, 0x8a, 0xf6,
|
||||
0x37, 0x7a, 0xed, 0x17, 0xf4, 0x50, 0x34, 0xc7, 0x9e, 0x7b, 0x48, 0x0b, 0x7f, 0x40, 0xbf, 0xa1,
|
||||
0xd8, 0x5d, 0x92, 0xa6, 0x23, 0x2b, 0x4e, 0x9c, 0x93, 0x76, 0x56, 0x33, 0x6f, 0x77, 0xde, 0x9b,
|
||||
0x99, 0x25, 0x6c, 0x4f, 0x59, 0x2a, 0x39, 0x4b, 0x82, 0x05, 0x67, 0x92, 0x91, 0x9d, 0x39, 0x9b,
|
||||
0xac, 0x82, 0x49, 0x16, 0x27, 0xb3, 0xf3, 0x58, 0x06, 0xcb, 0x47, 0x9d, 0x07, 0x51, 0x2c, 0x5f,
|
||||
0x64, 0x93, 0x60, 0xca, 0xe6, 0x7b, 0x11, 0x8b, 0xd8, 0x9e, 0x76, 0x9c, 0x64, 0x67, 0xda, 0xd2,
|
||||
0x86, 0x5e, 0x19, 0x80, 0x4e, 0x2f, 0x62, 0x2c, 0x4a, 0xf0, 0xca, 0x4b, 0xc6, 0x73, 0x14, 0x32,
|
||||
0x9c, 0x2f, 0x8c, 0x83, 0xbf, 0x0b, 0x3b, 0xe3, 0x58, 0x9c, 0x9f, 0x8a, 0x30, 0x42, 0x8a, 0x3f,
|
||||
0x66, 0x28, 0x24, 0xb9, 0x0f, 0xee, 0x59, 0x9c, 0x48, 0xe4, 0x9e, 0xd5, 0xb7, 0x06, 0x4d, 0x9a,
|
||||
0x5b, 0xfe, 0x11, 0x7c, 0x58, 0xf1, 0x15, 0x0b, 0x96, 0x0a, 0x24, 0x5f, 0x82, 0xcb, 0x71, 0xca,
|
||||
0xf8, 0xcc, 0xb3, 0xfa, 0xce, 0xa0, 0x35, 0xfc, 0x24, 0x78, 0xfd, 0xce, 0x41, 0x1e, 0xa0, 0x9c,
|
||||
0x68, 0xee, 0xec, 0xff, 0x6e, 0x43, 0xab, 0xb2, 0x4f, 0xee, 0x81, 0x7d, 0x38, 0xce, 0xcf, 0xb3,
|
||||
0x0f, 0xc7, 0xc4, 0x83, 0xfa, 0x71, 0x26, 0xc3, 0x49, 0x82, 0x9e, 0xdd, 0xb7, 0x06, 0x0d, 0x5a,
|
||||
0x98, 0xe4, 0x23, 0xd8, 0x3a, 0x4c, 0x4f, 0x05, 0x7a, 0x8e, 0xde, 0x37, 0x06, 0x21, 0x50, 0x3b,
|
||||
0x89, 0x7f, 0x42, 0xaf, 0xd6, 0xb7, 0x06, 0x0e, 0xd5, 0x6b, 0x95, 0xc7, 0x77, 0x21, 0xc7, 0x54,
|
||||
0x7a, 0x5b, 0x26, 0x0f, 0x63, 0x91, 0x11, 0x34, 0x0f, 0x38, 0x86, 0x12, 0x67, 0x4f, 0xa4, 0xe7,
|
||||
0xf6, 0xad, 0x41, 0x6b, 0xd8, 0x09, 0x0c, 0x51, 0x41, 0x41, 0x54, 0xf0, 0xbc, 0x20, 0x6a, 0xd4,
|
||||
0x78, 0xf9, 0xaa, 0xf7, 0xc1, 0xcf, 0xff, 0xf4, 0x2c, 0x7a, 0x15, 0x46, 0x1e, 0x03, 0x3c, 0x0b,
|
||||
0x85, 0x3c, 0x15, 0x1a, 0xa4, 0x7e, 0x2b, 0x48, 0x4d, 0x03, 0x54, 0x62, 0x48, 0x17, 0x40, 0x13,
|
||||
0x70, 0xc0, 0xb2, 0x54, 0x7a, 0x0d, 0x7d, 0xef, 0xca, 0x0e, 0xe9, 0x43, 0x6b, 0x8c, 0x62, 0xca,
|
||||
0xe3, 0x85, 0x8c, 0x59, 0xea, 0x35, 0x75, 0x0a, 0xd5, 0x2d, 0xff, 0x17, 0x1b, 0xda, 0x27, 0x2c,
|
||||
0x59, 0x96, 0xc2, 0xed, 0x80, 0x43, 0xf1, 0x2c, 0x67, 0x51, 0x2d, 0xd5, 0x21, 0x63, 0x3c, 0x8b,
|
||||
0xd3, 0x58, 0x63, 0xd8, 0x7d, 0x67, 0xd0, 0xa6, 0x95, 0x1d, 0xd2, 0x81, 0xc6, 0xd3, 0x8b, 0x05,
|
||||
0xe3, 0x4a, 0x6c, 0x47, 0x87, 0x95, 0x36, 0xf9, 0x01, 0xb6, 0x8b, 0xf5, 0x13, 0x29, 0xb9, 0xf0,
|
||||
0x6a, 0x5a, 0xe0, 0x47, 0xeb, 0x02, 0x57, 0x2f, 0x11, 0x5c, 0x8b, 0x79, 0x9a, 0x4a, 0xbe, 0xa2,
|
||||
0xd7, 0x71, 0x94, 0xb6, 0x27, 0x28, 0x84, 0xba, 0x91, 0x11, 0xa6, 0x30, 0x3b, 0x8f, 0x81, 0xac,
|
||||
0x87, 0xab, 0xb4, 0xce, 0x71, 0x55, 0xa4, 0x75, 0x8e, 0x2b, 0x55, 0x03, 0xcb, 0x30, 0xc9, 0x4c,
|
||||
0x6d, 0x34, 0xa9, 0x31, 0xf6, 0xed, 0xaf, 0x2c, 0xff, 0x6b, 0xd8, 0xce, 0x6f, 0x93, 0xd7, 0xe7,
|
||||
0x2e, 0x38, 0x4b, 0x79, 0x91, 0x17, 0xa7, 0xb7, 0x7e, 0xf7, 0xef, 0x91, 0x4b, 0xbc, 0xa0, 0xca,
|
||||
0xc9, 0xff, 0x14, 0xb6, 0x4f, 0x64, 0x28, 0x33, 0xb1, 0x91, 0x50, 0xff, 0x37, 0x0b, 0xee, 0x15,
|
||||
0x3e, 0xf9, 0x09, 0x5f, 0x40, 0x63, 0xa9, 0x41, 0x50, 0xdc, 0x7a, 0x4c, 0xe9, 0x49, 0xf6, 0xa1,
|
||||
0x21, 0x34, 0x0e, 0x0a, 0xad, 0x4b, 0x6b, 0xd8, 0xdd, 0x14, 0x95, 0x9f, 0x57, 0xfa, 0x93, 0x3d,
|
||||
0xa8, 0x25, 0x2c, 0x12, 0x9e, 0xa3, 0xe3, 0x3e, 0xde, 0x14, 0xf7, 0x8c, 0x45, 0x54, 0x3b, 0xfa,
|
||||
0xbf, 0x3a, 0xe0, 0x9a, 0x3d, 0x72, 0x04, 0xee, 0x2c, 0x8e, 0x50, 0x48, 0x93, 0xd5, 0x68, 0xa8,
|
||||
0xaa, 0xfb, 0xef, 0x57, 0xbd, 0xdd, 0xca, 0x60, 0x61, 0x0b, 0x4c, 0xd5, 0x20, 0x0a, 0xe3, 0x14,
|
||||
0xb9, 0xd8, 0x8b, 0xd8, 0x03, 0x13, 0x12, 0x8c, 0xf5, 0x0f, 0xcd, 0x11, 0x14, 0x56, 0x9c, 0x2e,
|
||||
0x32, 0x69, 0x32, 0xb8, 0x23, 0x96, 0x41, 0x50, 0x0d, 0x9c, 0x86, 0x73, 0xcc, 0xab, 0x50, 0xaf,
|
||||
0x55, 0x03, 0x4f, 0xc3, 0xe9, 0x0b, 0x9c, 0xe9, 0xb6, 0x6e, 0xd0, 0xdc, 0x22, 0xfb, 0x50, 0x17,
|
||||
0x32, 0xe4, 0x12, 0x67, 0xba, 0x80, 0xde, 0xa6, 0xf3, 0x8a, 0x00, 0xf2, 0x0d, 0x34, 0xa7, 0x6c,
|
||||
0xbe, 0x48, 0x50, 0x45, 0xbb, 0x6f, 0x19, 0x7d, 0x15, 0xa2, 0x4a, 0x0f, 0x39, 0x67, 0x5c, 0xf7,
|
||||
0x7c, 0x93, 0x1a, 0x43, 0x31, 0xb1, 0x30, 0xa3, 0xa6, 0x71, 0x77, 0x56, 0x0d, 0x82, 0xff, 0x9f,
|
||||
0x0d, 0xed, 0xaa, 0xf0, 0x6b, 0xb3, 0xf1, 0x08, 0x5c, 0x53, 0x46, 0xa6, 0xfc, 0xef, 0x76, 0x98,
|
||||
0x41, 0xb8, 0x91, 0x76, 0x0f, 0xea, 0xd3, 0x8c, 0xeb, 0x6c, 0xcc, 0x38, 0x2d, 0x4c, 0x95, 0xbc,
|
||||
0x64, 0x32, 0x4c, 0x34, 0xed, 0x0e, 0x35, 0x86, 0x9a, 0xa7, 0xe5, 0xb3, 0xf2, 0x6e, 0xf3, 0xb4,
|
||||
0x0c, 0xab, 0x4a, 0x5a, 0x7f, 0x2f, 0x49, 0x1b, 0xef, 0x2c, 0xa9, 0xff, 0x87, 0x05, 0xcd, 0xb2,
|
||||
0x63, 0x2a, 0xec, 0x5a, 0xef, 0xcd, 0xee, 0x35, 0x66, 0xec, 0xbb, 0x31, 0x73, 0x1f, 0x5c, 0x21,
|
||||
0x39, 0x86, 0x73, 0xad, 0x91, 0x43, 0x73, 0x4b, 0xcd, 0xa6, 0xb9, 0x88, 0xb4, 0x42, 0x6d, 0xaa,
|
||||
0x96, 0xbe, 0x0f, 0xed, 0xd1, 0x4a, 0xa2, 0x38, 0x46, 0xa1, 0x9e, 0x11, 0xa5, 0xed, 0x2c, 0x94,
|
||||
0xa1, 0xce, 0xa3, 0x4d, 0xf5, 0x7a, 0xf8, 0xa7, 0x0d, 0xf5, 0x03, 0xf3, 0x8d, 0x41, 0x9e, 0x43,
|
||||
0xb3, 0x7c, 0xcf, 0x89, 0xbf, 0x3e, 0x45, 0x5e, 0xff, 0x30, 0xe8, 0x7c, 0xf6, 0x46, 0x9f, 0x7c,
|
||||
0x1c, 0x7e, 0x0b, 0x5b, 0x7a, 0x02, 0x93, 0xee, 0x9b, 0x1f, 0x8a, 0x4e, 0x6f, 0xe3, 0xff, 0x39,
|
||||
0xd2, 0x31, 0xb8, 0x79, 0x07, 0xdc, 0xe4, 0x5a, 0x1d, 0xd4, 0x9d, 0xfe, 0x66, 0x07, 0x03, 0xf6,
|
||||
0xd0, 0x22, 0xc7, 0xe5, 0xb3, 0x73, 0xd3, 0xd5, 0xaa, 0xcc, 0x75, 0x6e, 0xf9, 0x7f, 0x60, 0x3d,
|
||||
0xb4, 0x46, 0xed, 0x97, 0x97, 0x5d, 0xeb, 0xaf, 0xcb, 0xae, 0xf5, 0xef, 0x65, 0xd7, 0x9a, 0xb8,
|
||||
0x5a, 0xce, 0xcf, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xae, 0x93, 0xb8, 0x49, 0xc1, 0x09, 0x00,
|
||||
0x00,
|
||||
// 965 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x56, 0xcd, 0x6e, 0xdb, 0x46,
|
||||
0x10, 0x2e, 0x45, 0x85, 0x92, 0x46, 0x72, 0xe0, 0x2e, 0x8a, 0x80, 0x50, 0x51, 0x49, 0x65, 0x2f,
|
||||
0x82, 0x81, 0xd0, 0x89, 0xda, 0x02, 0x85, 0x0b, 0x14, 0x89, 0xac, 0x14, 0xb5, 0x11, 0x03, 0xc5,
|
||||
0x3a, 0x6e, 0xcf, 0x94, 0x34, 0x66, 0x08, 0x53, 0x5c, 0x75, 0x77, 0x29, 0x58, 0x7d, 0x8a, 0x3e,
|
||||
0x47, 0xaf, 0x7d, 0x82, 0x1e, 0x8a, 0xfa, 0xd8, 0x73, 0x0f, 0x69, 0xe1, 0x07, 0xe8, 0x33, 0x14,
|
||||
0xbb, 0x4b, 0xd2, 0x54, 0x64, 0xc5, 0x3f, 0x39, 0x69, 0x67, 0x35, 0xf3, 0xed, 0xcc, 0xf7, 0xcd,
|
||||
0xce, 0x12, 0xb6, 0x26, 0x2c, 0x91, 0x9c, 0xc5, 0xfe, 0x9c, 0x33, 0xc9, 0xc8, 0xf6, 0x8c, 0x8d,
|
||||
0x97, 0xfe, 0x38, 0x8d, 0xe2, 0xe9, 0x59, 0x24, 0xfd, 0xc5, 0xd3, 0xf6, 0xe3, 0x30, 0x92, 0xaf,
|
||||
0xd3, 0xb1, 0x3f, 0x61, 0xb3, 0xdd, 0x90, 0x85, 0x6c, 0x57, 0x3b, 0x8e, 0xd3, 0x53, 0x6d, 0x69,
|
||||
0x43, 0xaf, 0x0c, 0x40, 0xbb, 0x1b, 0x32, 0x16, 0xc6, 0x78, 0xe5, 0x25, 0xa3, 0x19, 0x0a, 0x19,
|
||||
0xcc, 0xe6, 0xc6, 0xc1, 0xdb, 0x81, 0xed, 0x51, 0x24, 0xce, 0x4e, 0x44, 0x10, 0x22, 0xc5, 0x9f,
|
||||
0x52, 0x14, 0x92, 0x3c, 0x02, 0xe7, 0x34, 0x8a, 0x25, 0x72, 0xd7, 0xea, 0x59, 0xfd, 0x06, 0xcd,
|
||||
0x2c, 0xef, 0x10, 0x3e, 0x2c, 0xf9, 0x8a, 0x39, 0x4b, 0x04, 0x92, 0x2f, 0xc1, 0xe1, 0x38, 0x61,
|
||||
0x7c, 0xea, 0x5a, 0x3d, 0xbb, 0xdf, 0x1c, 0x7c, 0xe2, 0xbf, 0x9d, 0xb3, 0x9f, 0x05, 0x28, 0x27,
|
||||
0x9a, 0x39, 0x7b, 0xbf, 0x57, 0xa0, 0x59, 0xda, 0x27, 0x0f, 0xa1, 0x72, 0x30, 0xca, 0xce, 0xab,
|
||||
0x1c, 0x8c, 0x88, 0x0b, 0xb5, 0xa3, 0x54, 0x06, 0xe3, 0x18, 0xdd, 0x4a, 0xcf, 0xea, 0xd7, 0x69,
|
||||
0x6e, 0x92, 0x8f, 0xe0, 0xc1, 0x41, 0x72, 0x22, 0xd0, 0xb5, 0xf5, 0xbe, 0x31, 0x08, 0x81, 0xea,
|
||||
0x71, 0xf4, 0x33, 0xba, 0xd5, 0x9e, 0xd5, 0xb7, 0xa9, 0x5e, 0xab, 0x3a, 0xbe, 0x0f, 0x38, 0x26,
|
||||
0xd2, 0x7d, 0x60, 0xea, 0x30, 0x16, 0x19, 0x42, 0x63, 0x9f, 0x63, 0x20, 0x71, 0xfa, 0x5c, 0xba,
|
||||
0x4e, 0xcf, 0xea, 0x37, 0x07, 0x6d, 0xdf, 0x10, 0xe5, 0xe7, 0x44, 0xf9, 0xaf, 0x72, 0xa2, 0x86,
|
||||
0xf5, 0x8b, 0x37, 0xdd, 0x0f, 0x7e, 0xf9, 0xa7, 0x6b, 0xd1, 0xab, 0x30, 0xf2, 0x0c, 0xe0, 0x65,
|
||||
0x20, 0xe4, 0x89, 0xd0, 0x20, 0xb5, 0x1b, 0x41, 0xaa, 0x1a, 0xa0, 0x14, 0x43, 0x3a, 0x00, 0x9a,
|
||||
0x80, 0x7d, 0x96, 0x26, 0xd2, 0xad, 0xeb, 0xbc, 0x4b, 0x3b, 0xa4, 0x07, 0xcd, 0x11, 0x8a, 0x09,
|
||||
0x8f, 0xe6, 0x32, 0x62, 0x89, 0xdb, 0xd0, 0x25, 0x94, 0xb7, 0xbc, 0x0b, 0x1b, 0x5a, 0xc7, 0x2c,
|
||||
0x5e, 0x14, 0xc2, 0x6d, 0x83, 0x4d, 0xf1, 0x34, 0x63, 0x51, 0x2d, 0xd5, 0x21, 0x23, 0x3c, 0x8d,
|
||||
0x92, 0x48, 0x63, 0x54, 0x7a, 0x76, 0xbf, 0x45, 0x4b, 0x3b, 0xa4, 0x0d, 0xf5, 0x17, 0xe7, 0x73,
|
||||
0xc6, 0x95, 0xd8, 0xb6, 0x0e, 0x2b, 0x6c, 0xf2, 0x23, 0x6c, 0xe5, 0xeb, 0xe7, 0x52, 0x72, 0xe1,
|
||||
0x56, 0xb5, 0xc0, 0x4f, 0xd7, 0x05, 0x2e, 0x27, 0xe1, 0xaf, 0xc4, 0xbc, 0x48, 0x24, 0x5f, 0xd2,
|
||||
0x55, 0x1c, 0xa5, 0xed, 0x31, 0x0a, 0xa1, 0x32, 0x32, 0xc2, 0xe4, 0xa6, 0x4a, 0xe7, 0x5b, 0xce,
|
||||
0x12, 0x89, 0xc9, 0x54, 0x0b, 0xd3, 0xa0, 0x85, 0xad, 0xd2, 0xc9, 0xd7, 0x26, 0x9d, 0xda, 0xad,
|
||||
0xd2, 0x59, 0x89, 0xc9, 0xd2, 0x59, 0xd9, 0x6b, 0x3f, 0x03, 0xb2, 0x9e, 0xb3, 0xe2, 0xf2, 0x0c,
|
||||
0x97, 0x39, 0x97, 0x67, 0xb8, 0x54, 0x8d, 0xb7, 0x08, 0xe2, 0xd4, 0x34, 0x64, 0x83, 0x1a, 0x63,
|
||||
0xaf, 0xf2, 0x95, 0xa5, 0x10, 0xd6, 0x8f, 0xb9, 0x0b, 0x82, 0xf7, 0x35, 0x6c, 0x65, 0x59, 0x67,
|
||||
0xd7, 0x6a, 0x07, 0xec, 0x85, 0x3c, 0xcf, 0xee, 0x94, 0xbb, 0x5e, 0xe3, 0x0f, 0xc8, 0x25, 0x9e,
|
||||
0x53, 0xe5, 0xe4, 0x7d, 0x0a, 0x5b, 0xc7, 0x32, 0x90, 0xa9, 0xd8, 0xd8, 0x07, 0xde, 0x6f, 0x16,
|
||||
0x3c, 0xcc, 0x7d, 0xb2, 0x13, 0xbe, 0x80, 0xfa, 0x42, 0x83, 0xa0, 0xb8, 0xf1, 0x98, 0xc2, 0x93,
|
||||
0xec, 0x41, 0x5d, 0x68, 0x1c, 0x14, 0xba, 0x9d, 0x9a, 0x83, 0xce, 0xa6, 0xa8, 0xec, 0xbc, 0xc2,
|
||||
0x9f, 0xec, 0x42, 0x35, 0x66, 0xa1, 0x70, 0x6d, 0x1d, 0xf7, 0xf1, 0xa6, 0xb8, 0x97, 0x2c, 0xa4,
|
||||
0xda, 0xd1, 0xfb, 0xd5, 0x06, 0xc7, 0xec, 0x91, 0x43, 0x70, 0xa6, 0x51, 0x88, 0x42, 0x9a, 0xaa,
|
||||
0x86, 0x03, 0x75, 0x29, 0xff, 0x7e, 0xd3, 0xdd, 0x29, 0xcd, 0x43, 0x36, 0xc7, 0x44, 0xcd, 0xcf,
|
||||
0x20, 0x4a, 0x90, 0x8b, 0xdd, 0x90, 0x3d, 0x36, 0x21, 0xfe, 0x48, 0xff, 0xd0, 0x0c, 0x41, 0x61,
|
||||
0x45, 0xc9, 0x3c, 0x95, 0xa6, 0x82, 0x7b, 0x62, 0x19, 0x04, 0x35, 0x77, 0x92, 0x60, 0x86, 0xd9,
|
||||
0xe5, 0xd1, 0x6b, 0x35, 0x77, 0x26, 0xc1, 0xe4, 0x35, 0x4e, 0xf5, 0x34, 0xaa, 0xd3, 0xcc, 0x22,
|
||||
0x7b, 0x50, 0x13, 0x32, 0xe0, 0x12, 0xa7, 0xba, 0xef, 0x6f, 0x33, 0x30, 0xf2, 0x00, 0xf2, 0x0d,
|
||||
0x34, 0x26, 0x6c, 0x36, 0x8f, 0x51, 0x45, 0x3b, 0xb7, 0x8c, 0xbe, 0x0a, 0x51, 0xad, 0x87, 0x9c,
|
||||
0x33, 0xae, 0x47, 0x55, 0x83, 0x1a, 0x43, 0x31, 0x31, 0x37, 0x13, 0xb2, 0x7e, 0x7f, 0x56, 0x0d,
|
||||
0x82, 0xf7, 0x5f, 0x05, 0x5a, 0x65, 0xe1, 0xd7, 0x46, 0xfa, 0x21, 0x38, 0xa6, 0x8d, 0x4c, 0xfb,
|
||||
0xdf, 0xef, 0x30, 0x83, 0x70, 0x2d, 0xed, 0x2e, 0xd4, 0x26, 0x29, 0xd7, 0xd5, 0x98, 0x57, 0x20,
|
||||
0x37, 0x55, 0xf1, 0x92, 0xc9, 0x20, 0xd6, 0xb4, 0xdb, 0xd4, 0x18, 0xea, 0x19, 0x28, 0x5e, 0xc3,
|
||||
0xbb, 0x3d, 0x03, 0x45, 0x58, 0x59, 0xd2, 0xda, 0x7b, 0x49, 0x5a, 0xbf, 0xb3, 0xa4, 0xde, 0x1f,
|
||||
0x16, 0x34, 0x8a, 0x1b, 0x53, 0x62, 0xd7, 0x7a, 0x6f, 0x76, 0x57, 0x98, 0xa9, 0xdc, 0x8f, 0x99,
|
||||
0x47, 0xe0, 0x08, 0xc9, 0x31, 0x98, 0x69, 0x8d, 0x6c, 0x9a, 0x59, 0x6a, 0x36, 0xcd, 0x44, 0xa8,
|
||||
0x15, 0x6a, 0x51, 0xb5, 0xf4, 0x3c, 0x68, 0x0d, 0x97, 0x12, 0xc5, 0x11, 0x0a, 0xf5, 0xfa, 0x29,
|
||||
0x6d, 0xa7, 0x81, 0x0c, 0x74, 0x1d, 0x2d, 0xaa, 0xd7, 0x83, 0x3f, 0x2b, 0x50, 0xdb, 0x37, 0x9f,
|
||||
0x46, 0xe4, 0x15, 0x34, 0x8a, 0xcf, 0x10, 0xe2, 0xad, 0x4f, 0x91, 0xb7, 0xbf, 0x67, 0xda, 0x9f,
|
||||
0xbd, 0xd3, 0x27, 0x1b, 0x87, 0xdf, 0xc1, 0x03, 0x3d, 0x81, 0x49, 0xe7, 0xdd, 0x0f, 0x4a, 0xbb,
|
||||
0xbb, 0xf1, 0xff, 0x0c, 0xe9, 0x08, 0x9c, 0xec, 0x06, 0x5c, 0xe7, 0x5a, 0x1e, 0xd4, 0xed, 0xde,
|
||||
0x66, 0x07, 0x03, 0xf6, 0xc4, 0x22, 0x47, 0xc5, 0x6b, 0x79, 0x5d, 0x6a, 0x65, 0xe6, 0xda, 0x37,
|
||||
0xfc, 0xdf, 0xb7, 0x9e, 0x58, 0xc3, 0xd6, 0xc5, 0x65, 0xc7, 0xfa, 0xeb, 0xb2, 0x63, 0xfd, 0x7b,
|
||||
0xd9, 0xb1, 0xc6, 0x8e, 0x96, 0xf3, 0xf3, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0xf4, 0x6d, 0x75,
|
||||
0x39, 0x78, 0x0a, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ message SolveRequest {
|
|||
string Exporter = 3;
|
||||
map<string, string> ExporterAttrs = 4;
|
||||
string Session = 5;
|
||||
string Frontend = 6;
|
||||
map<string, string> FrontendAttrs = 7;
|
||||
}
|
||||
|
||||
message SolveResponse {
|
||||
|
|
|
@ -212,7 +212,7 @@ type ExecState struct {
|
|||
}
|
||||
|
||||
func (e ExecState) AddMount(target string, source State, opt ...MountOption) State {
|
||||
return NewState(e.exec.AddMount(target, source.Output(), opt...))
|
||||
return source.WithOutput(e.exec.AddMount(target, source.Output(), opt...))
|
||||
}
|
||||
|
||||
func (e ExecState) GetMount(target string) State {
|
||||
|
@ -247,6 +247,13 @@ func Shlexf(str string, v ...interface{}) RunOption {
|
|||
}
|
||||
}
|
||||
|
||||
func Args(a []string) RunOption {
|
||||
return func(ei ExecInfo) ExecInfo {
|
||||
ei.State = args(a...)(ei.State)
|
||||
return ei
|
||||
}
|
||||
}
|
||||
|
||||
func AddEnv(key, value string) RunOption {
|
||||
return AddEnvf(key, value)
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@ package llb
|
|||
import (
|
||||
"context"
|
||||
_ "crypto/sha256"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -70,7 +72,14 @@ func Source(id string) State {
|
|||
}
|
||||
|
||||
func Image(ref string, opts ...ImageOption) State {
|
||||
r, err := reference.ParseNormalizedNamed(ref)
|
||||
if err == nil {
|
||||
ref = reference.TagNameOnly(r).String()
|
||||
}
|
||||
src := NewSource("docker-image://"+ref, nil) // controversial
|
||||
if err != nil {
|
||||
src.err = err
|
||||
}
|
||||
var info ImageInfo
|
||||
for _, opt := range opts {
|
||||
opt(&info)
|
||||
|
@ -149,6 +158,9 @@ func Local(name string, opts ...LocalOption) State {
|
|||
if gi.SessionID != "" {
|
||||
attrs[pb.AttrLocalSessionID] = gi.SessionID
|
||||
}
|
||||
if gi.IncludePatterns != "" {
|
||||
attrs[pb.AttrIncludePatterns] = gi.IncludePatterns
|
||||
}
|
||||
|
||||
source := NewSource("local://"+name, attrs)
|
||||
return NewState(source.Output())
|
||||
|
@ -162,6 +174,14 @@ func SessionID(id string) LocalOption {
|
|||
}
|
||||
}
|
||||
|
||||
type LocalInfo struct {
|
||||
SessionID string
|
||||
func IncludePatterns(p []string) LocalOption {
|
||||
return func(li *LocalInfo) {
|
||||
dt, _ := json.Marshal(p) // empty on error
|
||||
li.IncludePatterns = string(dt)
|
||||
}
|
||||
}
|
||||
|
||||
type LocalInfo struct {
|
||||
SessionID string
|
||||
IncludePatterns string
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ type SolveOpt struct {
|
|||
ExporterAttrs map[string]string
|
||||
LocalDirs map[string]string
|
||||
SharedKey string
|
||||
Frontend string
|
||||
FrontendAttrs map[string]string
|
||||
// Session string
|
||||
}
|
||||
|
||||
|
@ -36,13 +38,17 @@ func (c *Client) Solve(ctx context.Context, r io.Reader, opt SolveOpt, statusCha
|
|||
}
|
||||
}()
|
||||
|
||||
def, err := llb.ReadFrom(r)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse input")
|
||||
}
|
||||
var def [][]byte
|
||||
var err error
|
||||
if opt.Frontend == "" {
|
||||
def, err = llb.ReadFrom(r)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse input")
|
||||
}
|
||||
|
||||
if len(def) == 0 {
|
||||
return errors.New("invalid empty definition")
|
||||
if len(def) == 0 {
|
||||
return errors.New("invalid empty definition")
|
||||
}
|
||||
}
|
||||
|
||||
syncedDirs, err := prepareSyncedDirs(def, opt.LocalDirs)
|
||||
|
@ -92,6 +98,8 @@ func (c *Client) Solve(ctx context.Context, r io.Reader, opt SolveOpt, statusCha
|
|||
Exporter: opt.Exporter,
|
||||
ExporterAttrs: opt.ExporterAttrs,
|
||||
Session: s.ID(),
|
||||
Frontend: opt.Frontend,
|
||||
FrontendAttrs: opt.FrontendAttrs,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to solve")
|
||||
|
@ -175,6 +183,11 @@ func prepareSyncedDirs(defs [][]byte, localDirs map[string]string) ([]filesync.S
|
|||
}
|
||||
}
|
||||
dirs := make([]filesync.SyncedDir, 0, len(localDirs))
|
||||
if len(defs) == 0 {
|
||||
for name, d := range localDirs {
|
||||
dirs = append(dirs, filesync.SyncedDir{Name: name, Dir: d})
|
||||
}
|
||||
}
|
||||
for _, dt := range defs {
|
||||
var op pb.Op
|
||||
if err := (&op).Unmarshal(dt); err != nil {
|
||||
|
|
|
@ -37,6 +37,14 @@ var buildCommand = cli.Command{
|
|||
Name: "local",
|
||||
Usage: "Allow build access to the local directory",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "frontend",
|
||||
Usage: "Define frontend used for build",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "frontend-opt",
|
||||
Usage: "Define custom options for frontend",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -64,6 +72,11 @@ func build(clicontext *cli.Context) error {
|
|||
return errors.Wrap(err, "invalid exporter-opt")
|
||||
}
|
||||
|
||||
frontendAttrs, err := attrMap(clicontext.StringSlice("frontend-opt"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid frontend-opt")
|
||||
}
|
||||
|
||||
localDirs, err := attrMap(clicontext.StringSlice("local"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid local")
|
||||
|
@ -74,6 +87,8 @@ func build(clicontext *cli.Context) error {
|
|||
Exporter: clicontext.String("exporter"),
|
||||
ExporterAttrs: exporterAttrs,
|
||||
LocalDirs: localDirs,
|
||||
Frontend: clicontext.String("frontend"),
|
||||
FrontendAttrs: frontendAttrs,
|
||||
}, ch)
|
||||
})
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/moby/buildkit/cache"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/exporter"
|
||||
"github.com/moby/buildkit/frontend"
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/moby/buildkit/session/grpchijack"
|
||||
"github.com/moby/buildkit/solver"
|
||||
|
@ -26,6 +27,7 @@ type Opt struct {
|
|||
InstructionCache solver.InstructionCache
|
||||
Exporters map[string]exporter.Exporter
|
||||
SessionManager *session.Manager
|
||||
Frontends map[string]frontend.Frontend
|
||||
}
|
||||
|
||||
type Controller struct { // TODO: ControlService
|
||||
|
@ -77,14 +79,28 @@ func (c *Controller) DiskUsage(ctx context.Context, r *controlapi.DiskUsageReque
|
|||
}
|
||||
|
||||
func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (*controlapi.SolveResponse, error) {
|
||||
v, err := solver.LoadLLB(req.Definition)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load")
|
||||
var frontend frontend.Frontend
|
||||
if req.Frontend != "" {
|
||||
var ok bool
|
||||
frontend, ok = c.opt.Frontends[req.Frontend]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("frontend %s not found", req.Frontend)
|
||||
}
|
||||
}
|
||||
|
||||
var vertex solver.Vertex
|
||||
if req.Frontend == "" {
|
||||
v, err := solver.LoadLLB(req.Definition)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load llb definition")
|
||||
}
|
||||
vertex = v
|
||||
}
|
||||
|
||||
ctx = session.NewContext(ctx, req.Session)
|
||||
|
||||
var expi exporter.ExporterInstance
|
||||
var err error
|
||||
if req.Exporter != "" {
|
||||
exp, ok := c.opt.Exporters[req.Exporter]
|
||||
if !ok {
|
||||
|
@ -96,7 +112,7 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (*
|
|||
}
|
||||
}
|
||||
|
||||
if err := c.solver.Solve(ctx, req.Ref, v, expi); err != nil {
|
||||
if err := c.solver.Solve(ctx, req.Ref, frontend, vertex, expi, req.FrontendAttrs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &controlapi.SolveResponse{}, nil
|
||||
|
|
|
@ -16,6 +16,8 @@ import (
|
|||
"github.com/moby/buildkit/exporter"
|
||||
imageexporter "github.com/moby/buildkit/exporter/containerimage"
|
||||
localexporter "github.com/moby/buildkit/exporter/local"
|
||||
"github.com/moby/buildkit/frontend"
|
||||
"github.com/moby/buildkit/frontend/dockerfile"
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/moby/buildkit/snapshot/blobmapping"
|
||||
"github.com/moby/buildkit/source"
|
||||
|
@ -124,6 +126,9 @@ func defaultControllerOpts(root string, pd pullDeps) (*Opt, error) {
|
|||
}
|
||||
exporters[client.ExporterLocal] = localExporter
|
||||
|
||||
frontends := map[string]frontend.Frontend{}
|
||||
frontends["dockerfile.v0"] = dockerfile.NewDockerfileFrontend()
|
||||
|
||||
return &Opt{
|
||||
Snapshotter: snapshotter,
|
||||
CacheManager: cm,
|
||||
|
@ -131,5 +136,6 @@ func defaultControllerOpts(root string, pd pullDeps) (*Opt, error) {
|
|||
InstructionCache: ic,
|
||||
Exporters: exporters,
|
||||
SessionManager: sessm,
|
||||
Frontends: frontends,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
package dockerfile
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/moby/buildkit/cache"
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
|
||||
"github.com/moby/buildkit/snapshot"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const (
|
||||
keyTarget = "target"
|
||||
keyFilename = "filename"
|
||||
)
|
||||
|
||||
type dfFrontend struct{}
|
||||
|
||||
func NewDockerfileFrontend() frontend.Frontend {
|
||||
return &dfFrontend{}
|
||||
}
|
||||
|
||||
func (f *dfFrontend) Solve(ctx context.Context, llbBridge frontend.FrontendLLBBridge, opts map[string]string) (retRef cache.ImmutableRef, retErr error) {
|
||||
|
||||
filename := opts[keyFilename]
|
||||
if filename == "" {
|
||||
filename = "Dockerfile"
|
||||
}
|
||||
if path.Base(filename) != filename {
|
||||
return nil, errors.Errorf("invalid filename %s", filename)
|
||||
}
|
||||
|
||||
src := llb.Local("dockerfile", llb.IncludePatterns([]string{filename}))
|
||||
dt, err := src.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ref, err := llbBridge.Solve(ctx, dt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if ref != nil {
|
||||
ref.Release(context.TODO())
|
||||
}
|
||||
}()
|
||||
|
||||
mount, err := ref.Mount(ctx, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lm := snapshot.LocalMounter(mount)
|
||||
|
||||
root, err := lm.Mount()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if lm != nil {
|
||||
lm.Unmount()
|
||||
}
|
||||
}()
|
||||
|
||||
dtDockerfile, err := ioutil.ReadFile(filepath.Join(root, filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := lm.Unmount(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lm = nil
|
||||
|
||||
if err := ref.Release(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref = nil
|
||||
|
||||
st, err := dockerfile2llb.Dockerfile2LLB(ctx, dtDockerfile, dockerfile2llb.ConvertOpt{
|
||||
Target: opts[keyTarget],
|
||||
MetaResolver: llb.DefaultImageMetaResolver(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dt, err = st.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
retRef, err = llbBridge.Solve(ctx, dt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return retRef, nil
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/builder/dockerfile/instructions"
|
||||
"github.com/docker/docker/builder/dockerfile/parser"
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
emptyImageName = "scratch"
|
||||
)
|
||||
|
||||
type ConvertOpt struct {
|
||||
Target string
|
||||
MetaResolver llb.ImageMetaResolver
|
||||
}
|
||||
|
||||
func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, error) {
|
||||
if len(dt) == 0 {
|
||||
return nil, errors.Errorf("the Dockerfile cannot be empty")
|
||||
}
|
||||
|
||||
dockerfile, err := parser.Parse(bytes.NewReader(dt))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stages, metaArgs, err := instructions.Parse(dockerfile.AST)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_ = metaArgs
|
||||
|
||||
var allStages []*dispatchState
|
||||
stagesByName := map[string]*dispatchState{}
|
||||
|
||||
for _, st := range stages {
|
||||
state := llb.Scratch()
|
||||
if st.BaseName != emptyImageName {
|
||||
state = llb.Image(st.BaseName)
|
||||
}
|
||||
|
||||
ds := &dispatchState{
|
||||
state: state,
|
||||
stage: st,
|
||||
}
|
||||
if d, ok := stagesByName[st.BaseName]; ok {
|
||||
ds.base = d
|
||||
}
|
||||
|
||||
allStages = append(allStages, ds)
|
||||
if st.Name != "" {
|
||||
stagesByName[strings.ToLower(st.Name)] = ds
|
||||
}
|
||||
}
|
||||
|
||||
metaResolver := llb.DefaultImageMetaResolver()
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
for i, d := range allStages {
|
||||
if d.base == nil && d.stage.BaseName != emptyImageName {
|
||||
func(i int, d *dispatchState) {
|
||||
eg.Go(func() error {
|
||||
ref, err := reference.ParseNormalizedNamed(d.stage.BaseName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.stage.BaseName = reference.TagNameOnly(ref).String()
|
||||
if opt.MetaResolver != nil {
|
||||
img, err := metaResolver.Resolve(ctx, d.stage.BaseName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allStages[i].image = *img
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}(i, d)
|
||||
}
|
||||
}
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, d := range allStages {
|
||||
if d.base != nil {
|
||||
d.state = d.base.state
|
||||
}
|
||||
|
||||
for _, env := range d.image.Config.Env {
|
||||
parts := strings.SplitN(env, "=", 2)
|
||||
v := ""
|
||||
if len(parts) > 1 {
|
||||
v = parts[1]
|
||||
}
|
||||
if err := dispatchEnv(d, &instructions.EnvCommand{Env: []instructions.KeyValuePair{{Key: parts[0], Value: v}}}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if d.image.Config.WorkingDir != "" {
|
||||
if err = dispatchWorkdir(d, &instructions.WorkdirCommand{Path: d.image.Config.WorkingDir}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, cmd := range d.stage.Commands {
|
||||
switch c := cmd.(type) {
|
||||
case *instructions.EnvCommand:
|
||||
err = dispatchEnv(d, c)
|
||||
case *instructions.RunCommand:
|
||||
err = dispatchRun(d, c)
|
||||
case *instructions.WorkdirCommand:
|
||||
err = dispatchWorkdir(d, c)
|
||||
case *instructions.AddCommand:
|
||||
err = dispatchCopy(d, c.SourcesAndDest, llb.Local("context"))
|
||||
case *instructions.CopyCommand:
|
||||
l := llb.Local("context")
|
||||
if c.From != "" {
|
||||
index, err := strconv.Atoi(c.From)
|
||||
if err != nil {
|
||||
stn, ok := stagesByName[strings.ToLower(c.From)]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("stage %s not found", c.From)
|
||||
}
|
||||
l = stn.state
|
||||
} else {
|
||||
if index >= len(allStages) {
|
||||
return nil, errors.Errorf("invalid stage index %d", index)
|
||||
}
|
||||
l = allStages[index].state
|
||||
}
|
||||
}
|
||||
err = dispatchCopy(d, c.SourcesAndDest, l)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if opt.Target == "" {
|
||||
return &allStages[len(allStages)-1].state, nil
|
||||
}
|
||||
|
||||
state, ok := stagesByName[strings.ToLower(opt.Target)]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("target stage %s could not be found", opt.Target)
|
||||
}
|
||||
return &state.state, nil
|
||||
}
|
||||
|
||||
type dispatchState struct {
|
||||
state llb.State
|
||||
image ocispec.Image
|
||||
stage instructions.Stage
|
||||
base *dispatchState
|
||||
}
|
||||
|
||||
func dispatchEnv(d *dispatchState, c *instructions.EnvCommand) error {
|
||||
for _, e := range c.Env {
|
||||
d.state = d.state.AddEnv(e.Key, e.Value)
|
||||
// addEnv(d.image, e.Key, e.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dispatchRun(d *dispatchState, c *instructions.RunCommand) error {
|
||||
var args []string = c.CmdLine
|
||||
if c.PrependShell {
|
||||
args = append(defaultShell(), strings.Join(args, " "))
|
||||
}
|
||||
d.state = d.state.Run(llb.Args(args)).Root()
|
||||
return nil
|
||||
}
|
||||
|
||||
func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand) error {
|
||||
d.state = d.state.Dir(c.Path)
|
||||
wd := c.Path
|
||||
if !path.IsAbs(c.Path) {
|
||||
wd = path.Join("/", d.image.Config.WorkingDir, wd)
|
||||
}
|
||||
d.image.Config.WorkingDir = wd
|
||||
return nil
|
||||
}
|
||||
|
||||
func dispatchCopy(d *dispatchState, c instructions.SourcesAndDest, sourceState llb.State) error {
|
||||
// TODO: this should use CopyOp instead. Current implementation is inefficient and doesn't match Dockerfile path suffixes rules
|
||||
img := llb.Image("docker.io/library/alpine@sha256:1072e499f3f655a032e88542330cf75b02e7bdf673278f701d7ba61629ee3ebe")
|
||||
|
||||
destDir := filepath.Join("/dest", toWorkingDir(d.state, c.Dest())) // TODO: detect file source + no dest path case
|
||||
srcs := make([]string, 0, len(c.Sources()))
|
||||
for _, src := range c.Sources() {
|
||||
src = path.Join("/src", toWorkingDir(sourceState, src))
|
||||
if src == "/src" {
|
||||
src += "/."
|
||||
}
|
||||
srcs = append(srcs, src)
|
||||
}
|
||||
|
||||
run := img.Run(llb.Shlexf("sh -c \"mkdir -p %s && cp -a %s %s\"", destDir, strings.Join(srcs, " "), destDir))
|
||||
run.AddMount("/src", sourceState, llb.Readonly)
|
||||
d.state = run.AddMount("/dest", d.state)
|
||||
return nil
|
||||
}
|
||||
|
||||
func toWorkingDir(s llb.State, p string) string {
|
||||
if path.IsAbs(p) {
|
||||
return p
|
||||
}
|
||||
return path.Join(s.GetDir(), p)
|
||||
}
|
||||
|
||||
func dispatchMaintainer(d *dispatchState, c instructions.MaintainerCommand) error {
|
||||
d.image.Author = c.Maintainer
|
||||
return nil
|
||||
}
|
||||
|
||||
func dispatchLabel(d *dispatchState, c instructions.LabelCommand) error {
|
||||
if d.image.Config.Labels == nil {
|
||||
d.image.Config.Labels = make(map[string]string)
|
||||
}
|
||||
for _, v := range c.Labels {
|
||||
d.image.Config.Labels[v.Key] = v.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// func dispatchOnbuild(d *dispatchState, c *instructions.OnbuildCommand) error {
|
||||
// d.image.Config.OnBuild = append(d.image.Config.OnBuild, c.Expression)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func dispatchCmd(d *dispatchState, c *instructions.CmdCommand) error {
|
||||
var args []string = c.CmdLine
|
||||
if c.PrependShell {
|
||||
args = append(defaultShell(), strings.Join(args, " "))
|
||||
}
|
||||
d.image.Config.Cmd = args
|
||||
// d.image.Config.ArgsEscaped = true
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/moby/buildkit/util/appcontext"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDockerfileParsing(t *testing.T) {
|
||||
df := `FROM busybox
|
||||
ENV FOO bar
|
||||
COPY f1 f2 /sub/
|
||||
RUN ls -l
|
||||
`
|
||||
_, err := Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
df = `FROM busybox AS foo
|
||||
ENV FOO bar
|
||||
FROM foo
|
||||
COPY --from=foo f1 /
|
||||
COPY --from=0 f2 /
|
||||
`
|
||||
_, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
df = `FROM busybox AS foo
|
||||
ENV FOO bar
|
||||
FROM foo
|
||||
COPY --from=foo f1 /
|
||||
COPY --from=0 f2 /
|
||||
`
|
||||
_, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{
|
||||
Target: "Foo",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{
|
||||
Target: "nosuch",
|
||||
})
|
||||
assert.Error(t, err)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
// +build !windows
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
func defaultShell() []string {
|
||||
return []string{"/bin/sh", "-c"}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
// +build windows
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
func defaultShell() []string {
|
||||
return []string{"cmd", "/S", "/C"}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package frontend
|
||||
|
||||
import (
|
||||
"github.com/moby/buildkit/cache"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type Frontend interface {
|
||||
Solve(ctx context.Context, llb FrontendLLBBridge, opt map[string]string) (cache.ImmutableRef, error)
|
||||
// TODO: return exporter data
|
||||
}
|
||||
|
||||
type FrontendLLBBridge interface {
|
||||
Solve(ctx context.Context, vtx [][]byte) (cache.ImmutableRef, error)
|
||||
// ImageConfigResolver
|
||||
}
|
|
@ -85,15 +85,17 @@ type job struct {
|
|||
|
||||
func (j *job) pipe(ctx context.Context, ch chan *client.SolveStatus) error {
|
||||
pr := j.pr.Reader(ctx)
|
||||
for v := range walk(j.g) {
|
||||
vv := v.(*vertex)
|
||||
ss := &client.SolveStatus{
|
||||
Vertexes: []*client.Vertex{&vv.clientVertex},
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case ch <- ss:
|
||||
if j.g != nil {
|
||||
for v := range walk(j.g) {
|
||||
vv := v.(*vertex)
|
||||
ss := &client.SolveStatus{
|
||||
Vertexes: []*client.Vertex{&vv.clientVertex},
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case ch <- ss:
|
||||
}
|
||||
}
|
||||
}
|
||||
for {
|
||||
|
|
|
@ -2,4 +2,5 @@ package pb
|
|||
|
||||
const AttrKeepGitDir = "git.keepgitdir"
|
||||
const AttrLocalSessionID = "local.session"
|
||||
const AttrIncludePatterns = "local.includepattern"
|
||||
const AttrLLBDefinitionFilename = "llbbuild.filename"
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/moby/buildkit/cache"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/exporter"
|
||||
"github.com/moby/buildkit/frontend"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/source"
|
||||
"github.com/moby/buildkit/util/progress"
|
||||
|
@ -76,30 +77,64 @@ func New(resolve ResolveOpFunc, cache InstructionCache) *Solver {
|
|||
return &Solver{resolve: resolve, jobs: newJobList(), cache: cache}
|
||||
}
|
||||
|
||||
func (s *Solver) Solve(ctx context.Context, id string, v Vertex, exp exporter.ExporterInstance) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
pr, ctx, closeProgressWriter := progress.NewContext(ctx)
|
||||
|
||||
origVertex := v
|
||||
|
||||
defer closeProgressWriter()
|
||||
type llbBridge struct {
|
||||
solver func(ctx context.Context, v *vertex, i Index) (Reference, error)
|
||||
}
|
||||
|
||||
func (s *llbBridge) Solve(ctx context.Context, dt [][]byte) (cache.ImmutableRef, error) {
|
||||
v, err := LoadLLB(dt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(v.Inputs()) == 0 {
|
||||
return errors.New("required vertex needs to have inputs")
|
||||
return nil, errors.New("required vertex needs to have inputs")
|
||||
}
|
||||
|
||||
index := v.Inputs()[0].Index
|
||||
v = v.Inputs()[0].Vertex
|
||||
|
||||
vv := toInternalVertex(v)
|
||||
solveVertex := vv
|
||||
ref, err := s.solver(ctx, vv, index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
immutable, ok := toImmutableRef(ref)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid reference for exporting: %T", ref)
|
||||
}
|
||||
|
||||
if exp != nil {
|
||||
vv = &vertex{digest: origVertex.Digest(), name: exp.Name()}
|
||||
vv.inputs = []*input{{index: 0, vertex: solveVertex}}
|
||||
vv.initClientVertex()
|
||||
return immutable, nil
|
||||
}
|
||||
|
||||
func (s *Solver) Solve(ctx context.Context, id string, f frontend.Frontend, v Vertex, exp exporter.ExporterInstance, frontendOpt map[string]string) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
pr, ctx, closeProgressWriter := progress.NewContext(ctx)
|
||||
|
||||
defer closeProgressWriter()
|
||||
|
||||
var vv *vertex
|
||||
var solveVertex *vertex
|
||||
var index Index
|
||||
if v != nil {
|
||||
origVertex := v
|
||||
|
||||
if len(v.Inputs()) == 0 {
|
||||
return errors.New("required vertex needs to have inputs")
|
||||
}
|
||||
|
||||
index = v.Inputs()[0].Index
|
||||
v = v.Inputs()[0].Vertex
|
||||
|
||||
vv = toInternalVertex(v)
|
||||
solveVertex = vv
|
||||
|
||||
if exp != nil {
|
||||
vv = &vertex{digest: origVertex.Digest(), name: exp.Name()}
|
||||
vv.inputs = []*input{{index: 0, vertex: solveVertex}}
|
||||
vv.initClientVertex()
|
||||
}
|
||||
}
|
||||
|
||||
ctx, j, err := s.jobs.new(ctx, id, vv, pr)
|
||||
|
@ -107,7 +142,14 @@ func (s *Solver) Solve(ctx context.Context, id string, v Vertex, exp exporter.Ex
|
|||
return err
|
||||
}
|
||||
|
||||
ref, err := s.getRef(ctx, solveVertex, index)
|
||||
var ref Reference
|
||||
if solveVertex != nil {
|
||||
ref, err = s.getRef(ctx, solveVertex, index)
|
||||
} else {
|
||||
ref, err = f.Solve(ctx, &llbBridge{
|
||||
solver: s.getRef,
|
||||
}, frontendOpt)
|
||||
}
|
||||
s.activeState.cancel(j)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package solver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
|
@ -50,6 +51,12 @@ func (s *sourceOp) instance(ctx context.Context) (source.SourceInstance, error)
|
|||
switch k {
|
||||
case pb.AttrLocalSessionID:
|
||||
id.SessionID = v
|
||||
case pb.AttrIncludePatterns:
|
||||
var patterns []string
|
||||
if err := json.Unmarshal([]byte(v), &patterns); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id.IncludePatterns = patterns
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,8 +62,9 @@ func (_ *ImageIdentifier) ID() string {
|
|||
}
|
||||
|
||||
type LocalIdentifier struct {
|
||||
Name string
|
||||
SessionID string
|
||||
Name string
|
||||
SessionID string
|
||||
IncludePatterns []string
|
||||
}
|
||||
|
||||
func NewLocalIdentifier(str string) (*LocalIdentifier, error) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package local
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
|
@ -13,6 +14,7 @@ import (
|
|||
"github.com/moby/buildkit/snapshot"
|
||||
"github.com/moby/buildkit/source"
|
||||
"github.com/moby/buildkit/util/progress"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/tonistiigi/fsutil"
|
||||
|
@ -74,8 +76,14 @@ func (ls *localSourceHandler) CacheKey(ctx context.Context) (string, error) {
|
|||
}
|
||||
sessionID = id
|
||||
}
|
||||
|
||||
return "session:" + ls.src.Name + ":" + sessionID, nil
|
||||
dt, err := json.Marshal(struct {
|
||||
SessionID string
|
||||
IncludePatterns []string
|
||||
}{SessionID: sessionID, IncludePatterns: ls.src.IncludePatterns})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "session:" + ls.src.Name + ":" + digest.FromBytes(dt).String(), nil
|
||||
}
|
||||
|
||||
func (ls *localSourceHandler) Snapshot(ctx context.Context) (out cache.ImmutableRef, retErr error) {
|
||||
|
@ -148,7 +156,7 @@ func (ls *localSourceHandler) Snapshot(ctx context.Context) (out cache.Immutable
|
|||
|
||||
opt := filesync.FSSendRequestOpt{
|
||||
Name: ls.src.Name,
|
||||
IncludePatterns: nil,
|
||||
IncludePatterns: ls.src.IncludePatterns,
|
||||
OverrideExcludes: false,
|
||||
DestDir: dest,
|
||||
CacheUpdater: &cacheUpdater{cc},
|
||||
|
|
|
@ -33,7 +33,7 @@ github.com/google/shlex 6f45313302b9c56850fc17f99e40caebce98c716
|
|||
golang.org/x/time 8be79e1e0910c292df4e79c241bb7e8f7e725959
|
||||
|
||||
github.com/BurntSushi/locker 392720b78f44e9d0249fcac6c43b111b47a370b8
|
||||
github.com/docker/docker 6301ac0c27aef52a96820482a01869457ae416f7 https://github.com/mcandre/moby.git
|
||||
github.com/docker/docker 6f723db8c6f0c7f0b252674a9673a25b5978db04 https://github.com/simonferquel/docker.git
|
||||
github.com/pkg/profile 5b67d428864e92711fcbd2f8629456121a56d91f
|
||||
|
||||
github.com/tonistiigi/fsutil d49833a9a6fa5b41f63e7e338038633d10276b57
|
||||
|
@ -42,3 +42,5 @@ github.com/dmcgowan/go-tar 2e2c51242e8993c50445dab7c03c8e7febddd0cf
|
|||
github.com/hashicorp/go-immutable-radix 826af9ccf0feeee615d546d69b11f8e98da8c8f1 git://github.com/tonistiigi/go-immutable-radix.git
|
||||
github.com/hashicorp/golang-lru a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
|
||||
github.com/mitchellh/hashstructure 2bca23e0e452137f789efbc8610126fd8b94f73b
|
||||
github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
|
||||
github.com/docker/distribution 30578ca32960a4d368bf6db67b0a33c2a1f3dc6f
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
# Distribution
|
||||
|
||||
The Docker toolset to pack, ship, store, and deliver content.
|
||||
|
||||
This repository's main product is the Docker Registry 2.0 implementation
|
||||
for storing and distributing Docker images. It supersedes the
|
||||
[docker/docker-registry](https://github.com/docker/docker-registry)
|
||||
project with a new API design, focused around security and performance.
|
||||
|
||||
<img src="https://www.docker.com/sites/default/files/oyster-registry-3.png" width=200px/>
|
||||
|
||||
[![Circle CI](https://circleci.com/gh/docker/distribution/tree/master.svg?style=svg)](https://circleci.com/gh/docker/distribution/tree/master)
|
||||
[![GoDoc](https://godoc.org/github.com/docker/distribution?status.svg)](https://godoc.org/github.com/docker/distribution)
|
||||
|
||||
This repository contains the following components:
|
||||
|
||||
|**Component** |Description |
|
||||
|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **registry** | An implementation of the [Docker Registry HTTP API V2](docs/spec/api.md) for use with docker 1.6+. |
|
||||
| **libraries** | A rich set of libraries for interacting with distribution components. Please see [godoc](https://godoc.org/github.com/docker/distribution) for details. **Note**: These libraries are **unstable**. |
|
||||
| **specifications** | _Distribution_ related specifications are available in [docs/spec](docs/spec) |
|
||||
| **documentation** | Docker's full documentation set is available at [docs.docker.com](https://docs.docker.com). This repository [contains the subset](docs/) related just to the registry. |
|
||||
|
||||
### How does this integrate with Docker engine?
|
||||
|
||||
This project should provide an implementation to a V2 API for use in the [Docker
|
||||
core project](https://github.com/docker/docker). The API should be embeddable
|
||||
and simplify the process of securely pulling and pushing content from `docker`
|
||||
daemons.
|
||||
|
||||
### What are the long term goals of the Distribution project?
|
||||
|
||||
The _Distribution_ project has the further long term goal of providing a
|
||||
secure tool chain for distributing content. The specifications, APIs and tools
|
||||
should be as useful with Docker as they are without.
|
||||
|
||||
Our goal is to design a professional grade and extensible content distribution
|
||||
system that allow users to:
|
||||
|
||||
* Enjoy an efficient, secured and reliable way to store, manage, package and
|
||||
exchange content
|
||||
* Hack/roll their own on top of healthy open-source components
|
||||
* Implement their own home made solution through good specs, and solid
|
||||
extensions mechanism.
|
||||
|
||||
## More about Registry 2.0
|
||||
|
||||
The new registry implementation provides the following benefits:
|
||||
|
||||
- faster push and pull
|
||||
- new, more efficient implementation
|
||||
- simplified deployment
|
||||
- pluggable storage backend
|
||||
- webhook notifications
|
||||
|
||||
For information on upcoming functionality, please see [ROADMAP.md](ROADMAP.md).
|
||||
|
||||
### Who needs to deploy a registry?
|
||||
|
||||
By default, Docker users pull images from Docker's public registry instance.
|
||||
[Installing Docker](https://docs.docker.com/engine/installation/) gives users this
|
||||
ability. Users can also push images to a repository on Docker's public registry,
|
||||
if they have a [Docker Hub](https://hub.docker.com/) account.
|
||||
|
||||
For some users and even companies, this default behavior is sufficient. For
|
||||
others, it is not.
|
||||
|
||||
For example, users with their own software products may want to maintain a
|
||||
registry for private, company images. Also, you may wish to deploy your own
|
||||
image repository for images used to test or in continuous integration. For these
|
||||
use cases and others, [deploying your own registry instance](https://github.com/docker/docker.github.io/blob/master/registry/deploying.md)
|
||||
may be the better choice.
|
||||
|
||||
### Migration to Registry 2.0
|
||||
|
||||
For those who have previously deployed their own registry based on the Registry
|
||||
1.0 implementation and wish to deploy a Registry 2.0 while retaining images,
|
||||
data migration is required. A tool to assist with migration efforts has been
|
||||
created. For more information see [docker/migrator](https://github.com/docker/migrator).
|
||||
|
||||
## Contribute
|
||||
|
||||
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute
|
||||
issues, fixes, and patches to this project. If you are contributing code, see
|
||||
the instructions for [building a development environment](BUILDING.md).
|
||||
|
||||
## Support
|
||||
|
||||
If any issues are encountered while using the _Distribution_ project, several
|
||||
avenues are available for support:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th align="left">
|
||||
IRC
|
||||
</th>
|
||||
<td>
|
||||
#docker-distribution on FreeNode
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="left">
|
||||
Issue Tracker
|
||||
</th>
|
||||
<td>
|
||||
github.com/docker/distribution/issues
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="left">
|
||||
Google Groups
|
||||
</th>
|
||||
<td>
|
||||
https://groups.google.com/a/dockerproject.org/forum/#!forum/distribution
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th align="left">
|
||||
Mailing List
|
||||
</th>
|
||||
<td>
|
||||
docker@dockerproject.org
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This project is distributed under [Apache License, Version 2.0](LICENSE).
|
|
@ -0,0 +1,247 @@
|
|||
package digestset
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrDigestNotFound is used when a matching digest
|
||||
// could not be found in a set.
|
||||
ErrDigestNotFound = errors.New("digest not found")
|
||||
|
||||
// ErrDigestAmbiguous is used when multiple digests
|
||||
// are found in a set. None of the matching digests
|
||||
// should be considered valid matches.
|
||||
ErrDigestAmbiguous = errors.New("ambiguous digest string")
|
||||
)
|
||||
|
||||
// Set is used to hold a unique set of digests which
|
||||
// may be easily referenced by easily referenced by a string
|
||||
// representation of the digest as well as short representation.
|
||||
// The uniqueness of the short representation is based on other
|
||||
// digests in the set. If digests are omitted from this set,
|
||||
// collisions in a larger set may not be detected, therefore it
|
||||
// is important to always do short representation lookups on
|
||||
// the complete set of digests. To mitigate collisions, an
|
||||
// appropriately long short code should be used.
|
||||
type Set struct {
|
||||
mutex sync.RWMutex
|
||||
entries digestEntries
|
||||
}
|
||||
|
||||
// NewSet creates an empty set of digests
|
||||
// which may have digests added.
|
||||
func NewSet() *Set {
|
||||
return &Set{
|
||||
entries: digestEntries{},
|
||||
}
|
||||
}
|
||||
|
||||
// checkShortMatch checks whether two digests match as either whole
|
||||
// values or short values. This function does not test equality,
|
||||
// rather whether the second value could match against the first
|
||||
// value.
|
||||
func checkShortMatch(alg digest.Algorithm, hex, shortAlg, shortHex string) bool {
|
||||
if len(hex) == len(shortHex) {
|
||||
if hex != shortHex {
|
||||
return false
|
||||
}
|
||||
if len(shortAlg) > 0 && string(alg) != shortAlg {
|
||||
return false
|
||||
}
|
||||
} else if !strings.HasPrefix(hex, shortHex) {
|
||||
return false
|
||||
} else if len(shortAlg) > 0 && string(alg) != shortAlg {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Lookup looks for a digest matching the given string representation.
|
||||
// If no digests could be found ErrDigestNotFound will be returned
|
||||
// with an empty digest value. If multiple matches are found
|
||||
// ErrDigestAmbiguous will be returned with an empty digest value.
|
||||
func (dst *Set) Lookup(d string) (digest.Digest, error) {
|
||||
dst.mutex.RLock()
|
||||
defer dst.mutex.RUnlock()
|
||||
if len(dst.entries) == 0 {
|
||||
return "", ErrDigestNotFound
|
||||
}
|
||||
var (
|
||||
searchFunc func(int) bool
|
||||
alg digest.Algorithm
|
||||
hex string
|
||||
)
|
||||
dgst, err := digest.Parse(d)
|
||||
if err == digest.ErrDigestInvalidFormat {
|
||||
hex = d
|
||||
searchFunc = func(i int) bool {
|
||||
return dst.entries[i].val >= d
|
||||
}
|
||||
} else {
|
||||
hex = dgst.Hex()
|
||||
alg = dgst.Algorithm()
|
||||
searchFunc = func(i int) bool {
|
||||
if dst.entries[i].val == hex {
|
||||
return dst.entries[i].alg >= alg
|
||||
}
|
||||
return dst.entries[i].val >= hex
|
||||
}
|
||||
}
|
||||
idx := sort.Search(len(dst.entries), searchFunc)
|
||||
if idx == len(dst.entries) || !checkShortMatch(dst.entries[idx].alg, dst.entries[idx].val, string(alg), hex) {
|
||||
return "", ErrDigestNotFound
|
||||
}
|
||||
if dst.entries[idx].alg == alg && dst.entries[idx].val == hex {
|
||||
return dst.entries[idx].digest, nil
|
||||
}
|
||||
if idx+1 < len(dst.entries) && checkShortMatch(dst.entries[idx+1].alg, dst.entries[idx+1].val, string(alg), hex) {
|
||||
return "", ErrDigestAmbiguous
|
||||
}
|
||||
|
||||
return dst.entries[idx].digest, nil
|
||||
}
|
||||
|
||||
// Add adds the given digest to the set. An error will be returned
|
||||
// if the given digest is invalid. If the digest already exists in the
|
||||
// set, this operation will be a no-op.
|
||||
func (dst *Set) Add(d digest.Digest) error {
|
||||
if err := d.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
dst.mutex.Lock()
|
||||
defer dst.mutex.Unlock()
|
||||
entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d}
|
||||
searchFunc := func(i int) bool {
|
||||
if dst.entries[i].val == entry.val {
|
||||
return dst.entries[i].alg >= entry.alg
|
||||
}
|
||||
return dst.entries[i].val >= entry.val
|
||||
}
|
||||
idx := sort.Search(len(dst.entries), searchFunc)
|
||||
if idx == len(dst.entries) {
|
||||
dst.entries = append(dst.entries, entry)
|
||||
return nil
|
||||
} else if dst.entries[idx].digest == d {
|
||||
return nil
|
||||
}
|
||||
|
||||
entries := append(dst.entries, nil)
|
||||
copy(entries[idx+1:], entries[idx:len(entries)-1])
|
||||
entries[idx] = entry
|
||||
dst.entries = entries
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes the given digest from the set. An err will be
|
||||
// returned if the given digest is invalid. If the digest does
|
||||
// not exist in the set, this operation will be a no-op.
|
||||
func (dst *Set) Remove(d digest.Digest) error {
|
||||
if err := d.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
dst.mutex.Lock()
|
||||
defer dst.mutex.Unlock()
|
||||
entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d}
|
||||
searchFunc := func(i int) bool {
|
||||
if dst.entries[i].val == entry.val {
|
||||
return dst.entries[i].alg >= entry.alg
|
||||
}
|
||||
return dst.entries[i].val >= entry.val
|
||||
}
|
||||
idx := sort.Search(len(dst.entries), searchFunc)
|
||||
// Not found if idx is after or value at idx is not digest
|
||||
if idx == len(dst.entries) || dst.entries[idx].digest != d {
|
||||
return nil
|
||||
}
|
||||
|
||||
entries := dst.entries
|
||||
copy(entries[idx:], entries[idx+1:])
|
||||
entries = entries[:len(entries)-1]
|
||||
dst.entries = entries
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// All returns all the digests in the set
|
||||
func (dst *Set) All() []digest.Digest {
|
||||
dst.mutex.RLock()
|
||||
defer dst.mutex.RUnlock()
|
||||
retValues := make([]digest.Digest, len(dst.entries))
|
||||
for i := range dst.entries {
|
||||
retValues[i] = dst.entries[i].digest
|
||||
}
|
||||
|
||||
return retValues
|
||||
}
|
||||
|
||||
// ShortCodeTable returns a map of Digest to unique short codes. The
|
||||
// length represents the minimum value, the maximum length may be the
|
||||
// entire value of digest if uniqueness cannot be achieved without the
|
||||
// full value. This function will attempt to make short codes as short
|
||||
// as possible to be unique.
|
||||
func ShortCodeTable(dst *Set, length int) map[digest.Digest]string {
|
||||
dst.mutex.RLock()
|
||||
defer dst.mutex.RUnlock()
|
||||
m := make(map[digest.Digest]string, len(dst.entries))
|
||||
l := length
|
||||
resetIdx := 0
|
||||
for i := 0; i < len(dst.entries); i++ {
|
||||
var short string
|
||||
extended := true
|
||||
for extended {
|
||||
extended = false
|
||||
if len(dst.entries[i].val) <= l {
|
||||
short = dst.entries[i].digest.String()
|
||||
} else {
|
||||
short = dst.entries[i].val[:l]
|
||||
for j := i + 1; j < len(dst.entries); j++ {
|
||||
if checkShortMatch(dst.entries[j].alg, dst.entries[j].val, "", short) {
|
||||
if j > resetIdx {
|
||||
resetIdx = j
|
||||
}
|
||||
extended = true
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if extended {
|
||||
l++
|
||||
}
|
||||
}
|
||||
}
|
||||
m[dst.entries[i].digest] = short
|
||||
if i >= resetIdx {
|
||||
l = length
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
type digestEntry struct {
|
||||
alg digest.Algorithm
|
||||
val string
|
||||
digest digest.Digest
|
||||
}
|
||||
|
||||
type digestEntries []*digestEntry
|
||||
|
||||
func (d digestEntries) Len() int {
|
||||
return len(d)
|
||||
}
|
||||
|
||||
func (d digestEntries) Less(i, j int) bool {
|
||||
if d[i].val != d[j].val {
|
||||
return d[i].val < d[j].val
|
||||
}
|
||||
return d[i].alg < d[j].alg
|
||||
}
|
||||
|
||||
func (d digestEntries) Swap(i, j int) {
|
||||
d[i], d[j] = d[j], d[i]
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package reference
|
||||
|
||||
import "path"
|
||||
|
||||
// IsNameOnly returns true if reference only contains a repo name.
|
||||
func IsNameOnly(ref Named) bool {
|
||||
if _, ok := ref.(NamedTagged); ok {
|
||||
return false
|
||||
}
|
||||
if _, ok := ref.(Canonical); ok {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// FamiliarName returns the familiar name string
|
||||
// for the given named, familiarizing if needed.
|
||||
func FamiliarName(ref Named) string {
|
||||
if nn, ok := ref.(normalizedNamed); ok {
|
||||
return nn.Familiar().Name()
|
||||
}
|
||||
return ref.Name()
|
||||
}
|
||||
|
||||
// FamiliarString returns the familiar string representation
|
||||
// for the given reference, familiarizing if needed.
|
||||
func FamiliarString(ref Reference) string {
|
||||
if nn, ok := ref.(normalizedNamed); ok {
|
||||
return nn.Familiar().String()
|
||||
}
|
||||
return ref.String()
|
||||
}
|
||||
|
||||
// FamiliarMatch reports whether ref matches the specified pattern.
|
||||
// See https://godoc.org/path#Match for supported patterns.
|
||||
func FamiliarMatch(pattern string, ref Reference) (bool, error) {
|
||||
matched, err := path.Match(pattern, FamiliarString(ref))
|
||||
if namedRef, isNamed := ref.(Named); isNamed && !matched {
|
||||
matched, _ = path.Match(pattern, FamiliarName(namedRef))
|
||||
}
|
||||
return matched, err
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package reference
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/digestset"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var (
|
||||
legacyDefaultDomain = "index.docker.io"
|
||||
defaultDomain = "docker.io"
|
||||
officialRepoName = "library"
|
||||
defaultTag = "latest"
|
||||
)
|
||||
|
||||
// normalizedNamed represents a name which has been
|
||||
// normalized and has a familiar form. A familiar name
|
||||
// is what is used in Docker UI. An example normalized
|
||||
// name is "docker.io/library/ubuntu" and corresponding
|
||||
// familiar name of "ubuntu".
|
||||
type normalizedNamed interface {
|
||||
Named
|
||||
Familiar() Named
|
||||
}
|
||||
|
||||
// ParseNormalizedNamed parses a string into a named reference
|
||||
// transforming a familiar name from Docker UI to a fully
|
||||
// qualified reference. If the value may be an identifier
|
||||
// use ParseAnyReference.
|
||||
func ParseNormalizedNamed(s string) (Named, error) {
|
||||
if ok := anchoredIdentifierRegexp.MatchString(s); ok {
|
||||
return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s)
|
||||
}
|
||||
domain, remainder := splitDockerDomain(s)
|
||||
var remoteName string
|
||||
if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 {
|
||||
remoteName = remainder[:tagSep]
|
||||
} else {
|
||||
remoteName = remainder
|
||||
}
|
||||
if strings.ToLower(remoteName) != remoteName {
|
||||
return nil, errors.New("invalid reference format: repository name must be lowercase")
|
||||
}
|
||||
|
||||
ref, err := Parse(domain + "/" + remainder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
named, isNamed := ref.(Named)
|
||||
if !isNamed {
|
||||
return nil, fmt.Errorf("reference %s has no name", ref.String())
|
||||
}
|
||||
return named, nil
|
||||
}
|
||||
|
||||
// splitDockerDomain splits a repository name to domain and remotename string.
|
||||
// If no valid domain is found, the default domain is used. Repository name
|
||||
// needs to be already validated before.
|
||||
func splitDockerDomain(name string) (domain, remainder string) {
|
||||
i := strings.IndexRune(name, '/')
|
||||
if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
|
||||
domain, remainder = defaultDomain, name
|
||||
} else {
|
||||
domain, remainder = name[:i], name[i+1:]
|
||||
}
|
||||
if domain == legacyDefaultDomain {
|
||||
domain = defaultDomain
|
||||
}
|
||||
if domain == defaultDomain && !strings.ContainsRune(remainder, '/') {
|
||||
remainder = officialRepoName + "/" + remainder
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// familiarizeName returns a shortened version of the name familiar
|
||||
// to to the Docker UI. Familiar names have the default domain
|
||||
// "docker.io" and "library/" repository prefix removed.
|
||||
// For example, "docker.io/library/redis" will have the familiar
|
||||
// name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp".
|
||||
// Returns a familiarized named only reference.
|
||||
func familiarizeName(named namedRepository) repository {
|
||||
repo := repository{
|
||||
domain: named.Domain(),
|
||||
path: named.Path(),
|
||||
}
|
||||
|
||||
if repo.domain == defaultDomain {
|
||||
repo.domain = ""
|
||||
// Handle official repositories which have the pattern "library/<official repo name>"
|
||||
if split := strings.Split(repo.path, "/"); len(split) == 2 && split[0] == officialRepoName {
|
||||
repo.path = split[1]
|
||||
}
|
||||
}
|
||||
return repo
|
||||
}
|
||||
|
||||
func (r reference) Familiar() Named {
|
||||
return reference{
|
||||
namedRepository: familiarizeName(r.namedRepository),
|
||||
tag: r.tag,
|
||||
digest: r.digest,
|
||||
}
|
||||
}
|
||||
|
||||
func (r repository) Familiar() Named {
|
||||
return familiarizeName(r)
|
||||
}
|
||||
|
||||
func (t taggedReference) Familiar() Named {
|
||||
return taggedReference{
|
||||
namedRepository: familiarizeName(t.namedRepository),
|
||||
tag: t.tag,
|
||||
}
|
||||
}
|
||||
|
||||
func (c canonicalReference) Familiar() Named {
|
||||
return canonicalReference{
|
||||
namedRepository: familiarizeName(c.namedRepository),
|
||||
digest: c.digest,
|
||||
}
|
||||
}
|
||||
|
||||
// TagNameOnly adds the default tag "latest" to a reference if it only has
|
||||
// a repo name.
|
||||
func TagNameOnly(ref Named) Named {
|
||||
if IsNameOnly(ref) {
|
||||
namedTagged, err := WithTag(ref, defaultTag)
|
||||
if err != nil {
|
||||
// Default tag must be valid, to create a NamedTagged
|
||||
// type with non-validated input the WithTag function
|
||||
// should be used instead
|
||||
panic(err)
|
||||
}
|
||||
return namedTagged
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
// ParseAnyReference parses a reference string as a possible identifier,
|
||||
// full digest, or familiar name.
|
||||
func ParseAnyReference(ref string) (Reference, error) {
|
||||
if ok := anchoredIdentifierRegexp.MatchString(ref); ok {
|
||||
return digestReference("sha256:" + ref), nil
|
||||
}
|
||||
if dgst, err := digest.Parse(ref); err == nil {
|
||||
return digestReference(dgst), nil
|
||||
}
|
||||
|
||||
return ParseNormalizedNamed(ref)
|
||||
}
|
||||
|
||||
// ParseAnyReferenceWithSet parses a reference string as a possible short
|
||||
// identifier to be matched in a digest set, a full digest, or familiar name.
|
||||
func ParseAnyReferenceWithSet(ref string, ds *digestset.Set) (Reference, error) {
|
||||
if ok := anchoredShortIdentifierRegexp.MatchString(ref); ok {
|
||||
dgst, err := ds.Lookup(ref)
|
||||
if err == nil {
|
||||
return digestReference(dgst), nil
|
||||
}
|
||||
} else {
|
||||
if dgst, err := digest.Parse(ref); err == nil {
|
||||
return digestReference(dgst), nil
|
||||
}
|
||||
}
|
||||
|
||||
return ParseNormalizedNamed(ref)
|
||||
}
|
|
@ -0,0 +1,433 @@
|
|||
// Package reference provides a general type to represent any way of referencing images within the registry.
|
||||
// Its main purpose is to abstract tags and digests (content-addressable hash).
|
||||
//
|
||||
// Grammar
|
||||
//
|
||||
// reference := name [ ":" tag ] [ "@" digest ]
|
||||
// name := [domain '/'] path-component ['/' path-component]*
|
||||
// domain := domain-component ['.' domain-component]* [':' port-number]
|
||||
// domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
|
||||
// port-number := /[0-9]+/
|
||||
// path-component := alpha-numeric [separator alpha-numeric]*
|
||||
// alpha-numeric := /[a-z0-9]+/
|
||||
// separator := /[_.]|__|[-]*/
|
||||
//
|
||||
// tag := /[\w][\w.-]{0,127}/
|
||||
//
|
||||
// digest := digest-algorithm ":" digest-hex
|
||||
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]*
|
||||
// digest-algorithm-separator := /[+.-_]/
|
||||
// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/
|
||||
// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
|
||||
//
|
||||
// identifier := /[a-f0-9]{64}/
|
||||
// short-identifier := /[a-f0-9]{6,64}/
|
||||
package reference
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
const (
|
||||
// NameTotalLengthMax is the maximum total number of characters in a repository name.
|
||||
NameTotalLengthMax = 255
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference.
|
||||
ErrReferenceInvalidFormat = errors.New("invalid reference format")
|
||||
|
||||
// ErrTagInvalidFormat represents an error while trying to parse a string as a tag.
|
||||
ErrTagInvalidFormat = errors.New("invalid tag format")
|
||||
|
||||
// ErrDigestInvalidFormat represents an error while trying to parse a string as a tag.
|
||||
ErrDigestInvalidFormat = errors.New("invalid digest format")
|
||||
|
||||
// ErrNameContainsUppercase is returned for invalid repository names that contain uppercase characters.
|
||||
ErrNameContainsUppercase = errors.New("repository name must be lowercase")
|
||||
|
||||
// ErrNameEmpty is returned for empty, invalid repository names.
|
||||
ErrNameEmpty = errors.New("repository name must have at least one component")
|
||||
|
||||
// ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax.
|
||||
ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax)
|
||||
|
||||
// ErrNameNotCanonical is returned when a name is not canonical.
|
||||
ErrNameNotCanonical = errors.New("repository name must be canonical")
|
||||
)
|
||||
|
||||
// Reference is an opaque object reference identifier that may include
|
||||
// modifiers such as a hostname, name, tag, and digest.
|
||||
type Reference interface {
|
||||
// String returns the full reference
|
||||
String() string
|
||||
}
|
||||
|
||||
// Field provides a wrapper type for resolving correct reference types when
|
||||
// working with encoding.
|
||||
type Field struct {
|
||||
reference Reference
|
||||
}
|
||||
|
||||
// AsField wraps a reference in a Field for encoding.
|
||||
func AsField(reference Reference) Field {
|
||||
return Field{reference}
|
||||
}
|
||||
|
||||
// Reference unwraps the reference type from the field to
|
||||
// return the Reference object. This object should be
|
||||
// of the appropriate type to further check for different
|
||||
// reference types.
|
||||
func (f Field) Reference() Reference {
|
||||
return f.reference
|
||||
}
|
||||
|
||||
// MarshalText serializes the field to byte text which
|
||||
// is the string of the reference.
|
||||
func (f Field) MarshalText() (p []byte, err error) {
|
||||
return []byte(f.reference.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText parses text bytes by invoking the
|
||||
// reference parser to ensure the appropriately
|
||||
// typed reference object is wrapped by field.
|
||||
func (f *Field) UnmarshalText(p []byte) error {
|
||||
r, err := Parse(string(p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.reference = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// Named is an object with a full name
|
||||
type Named interface {
|
||||
Reference
|
||||
Name() string
|
||||
}
|
||||
|
||||
// Tagged is an object which has a tag
|
||||
type Tagged interface {
|
||||
Reference
|
||||
Tag() string
|
||||
}
|
||||
|
||||
// NamedTagged is an object including a name and tag.
|
||||
type NamedTagged interface {
|
||||
Named
|
||||
Tag() string
|
||||
}
|
||||
|
||||
// Digested is an object which has a digest
|
||||
// in which it can be referenced by
|
||||
type Digested interface {
|
||||
Reference
|
||||
Digest() digest.Digest
|
||||
}
|
||||
|
||||
// Canonical reference is an object with a fully unique
|
||||
// name including a name with domain and digest
|
||||
type Canonical interface {
|
||||
Named
|
||||
Digest() digest.Digest
|
||||
}
|
||||
|
||||
// namedRepository is a reference to a repository with a name.
|
||||
// A namedRepository has both domain and path components.
|
||||
type namedRepository interface {
|
||||
Named
|
||||
Domain() string
|
||||
Path() string
|
||||
}
|
||||
|
||||
// Domain returns the domain part of the Named reference
|
||||
func Domain(named Named) string {
|
||||
if r, ok := named.(namedRepository); ok {
|
||||
return r.Domain()
|
||||
}
|
||||
domain, _ := splitDomain(named.Name())
|
||||
return domain
|
||||
}
|
||||
|
||||
// Path returns the name without the domain part of the Named reference
|
||||
func Path(named Named) (name string) {
|
||||
if r, ok := named.(namedRepository); ok {
|
||||
return r.Path()
|
||||
}
|
||||
_, path := splitDomain(named.Name())
|
||||
return path
|
||||
}
|
||||
|
||||
func splitDomain(name string) (string, string) {
|
||||
match := anchoredNameRegexp.FindStringSubmatch(name)
|
||||
if len(match) != 3 {
|
||||
return "", name
|
||||
}
|
||||
return match[1], match[2]
|
||||
}
|
||||
|
||||
// SplitHostname splits a named reference into a
|
||||
// hostname and name string. If no valid hostname is
|
||||
// found, the hostname is empty and the full value
|
||||
// is returned as name
|
||||
// DEPRECATED: Use Domain or Path
|
||||
func SplitHostname(named Named) (string, string) {
|
||||
if r, ok := named.(namedRepository); ok {
|
||||
return r.Domain(), r.Path()
|
||||
}
|
||||
return splitDomain(named.Name())
|
||||
}
|
||||
|
||||
// Parse parses s and returns a syntactically valid Reference.
|
||||
// If an error was encountered it is returned, along with a nil Reference.
|
||||
// NOTE: Parse will not handle short digests.
|
||||
func Parse(s string) (Reference, error) {
|
||||
matches := ReferenceRegexp.FindStringSubmatch(s)
|
||||
if matches == nil {
|
||||
if s == "" {
|
||||
return nil, ErrNameEmpty
|
||||
}
|
||||
if ReferenceRegexp.FindStringSubmatch(strings.ToLower(s)) != nil {
|
||||
return nil, ErrNameContainsUppercase
|
||||
}
|
||||
return nil, ErrReferenceInvalidFormat
|
||||
}
|
||||
|
||||
if len(matches[1]) > NameTotalLengthMax {
|
||||
return nil, ErrNameTooLong
|
||||
}
|
||||
|
||||
var repo repository
|
||||
|
||||
nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
|
||||
if nameMatch != nil && len(nameMatch) == 3 {
|
||||
repo.domain = nameMatch[1]
|
||||
repo.path = nameMatch[2]
|
||||
} else {
|
||||
repo.domain = ""
|
||||
repo.path = matches[1]
|
||||
}
|
||||
|
||||
ref := reference{
|
||||
namedRepository: repo,
|
||||
tag: matches[2],
|
||||
}
|
||||
if matches[3] != "" {
|
||||
var err error
|
||||
ref.digest, err = digest.Parse(matches[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
r := getBestReferenceType(ref)
|
||||
if r == nil {
|
||||
return nil, ErrNameEmpty
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// ParseNamed parses s and returns a syntactically valid reference implementing
|
||||
// the Named interface. The reference must have a name and be in the canonical
|
||||
// form, otherwise an error is returned.
|
||||
// If an error was encountered it is returned, along with a nil Reference.
|
||||
// NOTE: ParseNamed will not handle short digests.
|
||||
func ParseNamed(s string) (Named, error) {
|
||||
named, err := ParseNormalizedNamed(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if named.String() != s {
|
||||
return nil, ErrNameNotCanonical
|
||||
}
|
||||
return named, nil
|
||||
}
|
||||
|
||||
// WithName returns a named object representing the given string. If the input
|
||||
// is invalid ErrReferenceInvalidFormat will be returned.
|
||||
func WithName(name string) (Named, error) {
|
||||
if len(name) > NameTotalLengthMax {
|
||||
return nil, ErrNameTooLong
|
||||
}
|
||||
|
||||
match := anchoredNameRegexp.FindStringSubmatch(name)
|
||||
if match == nil || len(match) != 3 {
|
||||
return nil, ErrReferenceInvalidFormat
|
||||
}
|
||||
return repository{
|
||||
domain: match[1],
|
||||
path: match[2],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithTag combines the name from "name" and the tag from "tag" to form a
|
||||
// reference incorporating both the name and the tag.
|
||||
func WithTag(name Named, tag string) (NamedTagged, error) {
|
||||
if !anchoredTagRegexp.MatchString(tag) {
|
||||
return nil, ErrTagInvalidFormat
|
||||
}
|
||||
var repo repository
|
||||
if r, ok := name.(namedRepository); ok {
|
||||
repo.domain = r.Domain()
|
||||
repo.path = r.Path()
|
||||
} else {
|
||||
repo.path = name.Name()
|
||||
}
|
||||
if canonical, ok := name.(Canonical); ok {
|
||||
return reference{
|
||||
namedRepository: repo,
|
||||
tag: tag,
|
||||
digest: canonical.Digest(),
|
||||
}, nil
|
||||
}
|
||||
return taggedReference{
|
||||
namedRepository: repo,
|
||||
tag: tag,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithDigest combines the name from "name" and the digest from "digest" to form
|
||||
// a reference incorporating both the name and the digest.
|
||||
func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
|
||||
if !anchoredDigestRegexp.MatchString(digest.String()) {
|
||||
return nil, ErrDigestInvalidFormat
|
||||
}
|
||||
var repo repository
|
||||
if r, ok := name.(namedRepository); ok {
|
||||
repo.domain = r.Domain()
|
||||
repo.path = r.Path()
|
||||
} else {
|
||||
repo.path = name.Name()
|
||||
}
|
||||
if tagged, ok := name.(Tagged); ok {
|
||||
return reference{
|
||||
namedRepository: repo,
|
||||
tag: tagged.Tag(),
|
||||
digest: digest,
|
||||
}, nil
|
||||
}
|
||||
return canonicalReference{
|
||||
namedRepository: repo,
|
||||
digest: digest,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TrimNamed removes any tag or digest from the named reference.
|
||||
func TrimNamed(ref Named) Named {
|
||||
domain, path := SplitHostname(ref)
|
||||
return repository{
|
||||
domain: domain,
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
|
||||
func getBestReferenceType(ref reference) Reference {
|
||||
if ref.Name() == "" {
|
||||
// Allow digest only references
|
||||
if ref.digest != "" {
|
||||
return digestReference(ref.digest)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if ref.tag == "" {
|
||||
if ref.digest != "" {
|
||||
return canonicalReference{
|
||||
namedRepository: ref.namedRepository,
|
||||
digest: ref.digest,
|
||||
}
|
||||
}
|
||||
return ref.namedRepository
|
||||
}
|
||||
if ref.digest == "" {
|
||||
return taggedReference{
|
||||
namedRepository: ref.namedRepository,
|
||||
tag: ref.tag,
|
||||
}
|
||||
}
|
||||
|
||||
return ref
|
||||
}
|
||||
|
||||
type reference struct {
|
||||
namedRepository
|
||||
tag string
|
||||
digest digest.Digest
|
||||
}
|
||||
|
||||
func (r reference) String() string {
|
||||
return r.Name() + ":" + r.tag + "@" + r.digest.String()
|
||||
}
|
||||
|
||||
func (r reference) Tag() string {
|
||||
return r.tag
|
||||
}
|
||||
|
||||
func (r reference) Digest() digest.Digest {
|
||||
return r.digest
|
||||
}
|
||||
|
||||
type repository struct {
|
||||
domain string
|
||||
path string
|
||||
}
|
||||
|
||||
func (r repository) String() string {
|
||||
return r.Name()
|
||||
}
|
||||
|
||||
func (r repository) Name() string {
|
||||
if r.domain == "" {
|
||||
return r.path
|
||||
}
|
||||
return r.domain + "/" + r.path
|
||||
}
|
||||
|
||||
func (r repository) Domain() string {
|
||||
return r.domain
|
||||
}
|
||||
|
||||
func (r repository) Path() string {
|
||||
return r.path
|
||||
}
|
||||
|
||||
type digestReference digest.Digest
|
||||
|
||||
func (d digestReference) String() string {
|
||||
return digest.Digest(d).String()
|
||||
}
|
||||
|
||||
func (d digestReference) Digest() digest.Digest {
|
||||
return digest.Digest(d)
|
||||
}
|
||||
|
||||
type taggedReference struct {
|
||||
namedRepository
|
||||
tag string
|
||||
}
|
||||
|
||||
func (t taggedReference) String() string {
|
||||
return t.Name() + ":" + t.tag
|
||||
}
|
||||
|
||||
func (t taggedReference) Tag() string {
|
||||
return t.tag
|
||||
}
|
||||
|
||||
type canonicalReference struct {
|
||||
namedRepository
|
||||
digest digest.Digest
|
||||
}
|
||||
|
||||
func (c canonicalReference) String() string {
|
||||
return c.Name() + "@" + c.digest.String()
|
||||
}
|
||||
|
||||
func (c canonicalReference) Digest() digest.Digest {
|
||||
return c.digest
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
package reference
|
||||
|
||||
import "regexp"
|
||||
|
||||
var (
|
||||
// alphaNumericRegexp defines the alpha numeric atom, typically a
|
||||
// component of names. This only allows lower case characters and digits.
|
||||
alphaNumericRegexp = match(`[a-z0-9]+`)
|
||||
|
||||
// separatorRegexp defines the separators allowed to be embedded in name
|
||||
// components. This allow one period, one or two underscore and multiple
|
||||
// dashes.
|
||||
separatorRegexp = match(`(?:[._]|__|[-]*)`)
|
||||
|
||||
// nameComponentRegexp restricts registry path component names to start
|
||||
// with at least one letter or number, with following parts able to be
|
||||
// separated by one period, one or two underscore and multiple dashes.
|
||||
nameComponentRegexp = expression(
|
||||
alphaNumericRegexp,
|
||||
optional(repeated(separatorRegexp, alphaNumericRegexp)))
|
||||
|
||||
// domainComponentRegexp restricts the registry domain component of a
|
||||
// repository name to start with a component as defined by DomainRegexp
|
||||
// and followed by an optional port.
|
||||
domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
||||
|
||||
// DomainRegexp defines the structure of potential domain components
|
||||
// that may be part of image names. This is purposely a subset of what is
|
||||
// allowed by DNS to ensure backwards compatibility with Docker image
|
||||
// names.
|
||||
DomainRegexp = expression(
|
||||
domainComponentRegexp,
|
||||
optional(repeated(literal(`.`), domainComponentRegexp)),
|
||||
optional(literal(`:`), match(`[0-9]+`)))
|
||||
|
||||
// TagRegexp matches valid tag names. From docker/docker:graph/tags.go.
|
||||
TagRegexp = match(`[\w][\w.-]{0,127}`)
|
||||
|
||||
// anchoredTagRegexp matches valid tag names, anchored at the start and
|
||||
// end of the matched string.
|
||||
anchoredTagRegexp = anchored(TagRegexp)
|
||||
|
||||
// DigestRegexp matches valid digests.
|
||||
DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`)
|
||||
|
||||
// anchoredDigestRegexp matches valid digests, anchored at the start and
|
||||
// end of the matched string.
|
||||
anchoredDigestRegexp = anchored(DigestRegexp)
|
||||
|
||||
// NameRegexp is the format for the name component of references. The
|
||||
// regexp has capturing groups for the domain and name part omitting
|
||||
// the separating forward slash from either.
|
||||
NameRegexp = expression(
|
||||
optional(DomainRegexp, literal(`/`)),
|
||||
nameComponentRegexp,
|
||||
optional(repeated(literal(`/`), nameComponentRegexp)))
|
||||
|
||||
// anchoredNameRegexp is used to parse a name value, capturing the
|
||||
// domain and trailing components.
|
||||
anchoredNameRegexp = anchored(
|
||||
optional(capture(DomainRegexp), literal(`/`)),
|
||||
capture(nameComponentRegexp,
|
||||
optional(repeated(literal(`/`), nameComponentRegexp))))
|
||||
|
||||
// ReferenceRegexp is the full supported format of a reference. The regexp
|
||||
// is anchored and has capturing groups for name, tag, and digest
|
||||
// components.
|
||||
ReferenceRegexp = anchored(capture(NameRegexp),
|
||||
optional(literal(":"), capture(TagRegexp)),
|
||||
optional(literal("@"), capture(DigestRegexp)))
|
||||
|
||||
// IdentifierRegexp is the format for string identifier used as a
|
||||
// content addressable identifier using sha256. These identifiers
|
||||
// are like digests without the algorithm, since sha256 is used.
|
||||
IdentifierRegexp = match(`([a-f0-9]{64})`)
|
||||
|
||||
// ShortIdentifierRegexp is the format used to represent a prefix
|
||||
// of an identifier. A prefix may be used to match a sha256 identifier
|
||||
// within a list of trusted identifiers.
|
||||
ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`)
|
||||
|
||||
// anchoredIdentifierRegexp is used to check or match an
|
||||
// identifier value, anchored at start and end of string.
|
||||
anchoredIdentifierRegexp = anchored(IdentifierRegexp)
|
||||
|
||||
// anchoredShortIdentifierRegexp is used to check if a value
|
||||
// is a possible identifier prefix, anchored at start and end
|
||||
// of string.
|
||||
anchoredShortIdentifierRegexp = anchored(ShortIdentifierRegexp)
|
||||
)
|
||||
|
||||
// match compiles the string to a regular expression.
|
||||
var match = regexp.MustCompile
|
||||
|
||||
// literal compiles s into a literal regular expression, escaping any regexp
|
||||
// reserved characters.
|
||||
func literal(s string) *regexp.Regexp {
|
||||
re := match(regexp.QuoteMeta(s))
|
||||
|
||||
if _, complete := re.LiteralPrefix(); !complete {
|
||||
panic("must be a literal")
|
||||
}
|
||||
|
||||
return re
|
||||
}
|
||||
|
||||
// expression defines a full expression, where each regular expression must
|
||||
// follow the previous.
|
||||
func expression(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
var s string
|
||||
for _, re := range res {
|
||||
s += re.String()
|
||||
}
|
||||
|
||||
return match(s)
|
||||
}
|
||||
|
||||
// optional wraps the expression in a non-capturing group and makes the
|
||||
// production optional.
|
||||
func optional(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(group(expression(res...)).String() + `?`)
|
||||
}
|
||||
|
||||
// repeated wraps the regexp in a non-capturing group to get one or more
|
||||
// matches.
|
||||
func repeated(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(group(expression(res...)).String() + `+`)
|
||||
}
|
||||
|
||||
// group wraps the regexp in a non-capturing group.
|
||||
func group(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(`(?:` + expression(res...).String() + `)`)
|
||||
}
|
||||
|
||||
// capture wraps the expression in a capturing group.
|
||||
func capture(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(`(` + expression(res...).String() + `)`)
|
||||
}
|
||||
|
||||
// anchored anchors the regular expression by adding start and end delimiters.
|
||||
func anchored(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(`^` + expression(res...).String() + `$`)
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
github.com/Azure/azure-sdk-for-go 088007b3b08cc02b27f2eadfdcd870958460ce7e
|
||||
github.com/Azure/go-autorest ec5f4903f77ed9927ac95b19ab8e44ada64c1356
|
||||
github.com/sirupsen/logrus 3d4380f53a34dcdc95f0c1db702615992b38d9a4
|
||||
github.com/aws/aws-sdk-go c6fc52983ea2375810aa38ddb5370e9cdf611716
|
||||
github.com/bshuster-repo/logrus-logstash-hook d2c0ecc1836d91814e15e23bb5dc309c3ef51f4a
|
||||
github.com/bugsnag/bugsnag-go b1d153021fcd90ca3f080db36bec96dc690fb274
|
||||
github.com/bugsnag/osext 0dd3f918b21bec95ace9dc86c7e70266cfc5c702
|
||||
github.com/bugsnag/panicwrap e2c28503fcd0675329da73bf48b33404db873782
|
||||
github.com/denverdino/aliyungo afedced274aa9a7fcdd47ac97018f0f8db4e5de2
|
||||
github.com/dgrijalva/jwt-go a601269ab70c205d26370c16f7c81e9017c14e04
|
||||
github.com/docker/goamz f0a21f5b2e12f83a505ecf79b633bb2035cf6f85
|
||||
github.com/docker/libtrust fa567046d9b14f6aa788882a950d69651d230b21
|
||||
github.com/garyburd/redigo 535138d7bcd717d6531c701ef5933d98b1866257
|
||||
github.com/go-ini/ini 2ba15ac2dc9cdf88c110ec2dc0ced7fa45f5678c
|
||||
github.com/golang/protobuf 8d92cf5fc15a4382f8964b08e1f42a75c0591aa3
|
||||
github.com/gorilla/context 14f550f51af52180c2eefed15e5fd18d63c0a64a
|
||||
github.com/gorilla/handlers 60c7bfde3e33c201519a200a4507a158cc03a17b
|
||||
github.com/gorilla/mux 599cba5e7b6137d46ddf58fb1765f5d928e69604
|
||||
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
||||
github.com/miekg/dns 271c58e0c14f552178ea321a545ff9af38930f39
|
||||
github.com/mitchellh/mapstructure 482a9fd5fa83e8c4e7817413b80f3eb8feec03ef
|
||||
github.com/ncw/swift b964f2ca856aac39885e258ad25aec08d5f64ee6
|
||||
github.com/spf13/cobra 312092086bed4968099259622145a0c9ae280064
|
||||
github.com/spf13/pflag 5644820622454e71517561946e3d94b9f9db6842
|
||||
github.com/stevvooe/resumable 2aaf90b2ceea5072cb503ef2a620b08ff3119870
|
||||
github.com/xenolf/lego a9d8cec0e6563575e5868a005359ac97911b5985
|
||||
github.com/yvasiyarov/go-metrics 57bccd1ccd43f94bb17fdd8bf3007059b802f85e
|
||||
github.com/yvasiyarov/gorelic a9bba5b9ab508a086f9a12b8c51fab68478e2128
|
||||
github.com/yvasiyarov/newrelic_platform_go b21fdbd4370f3717f3bbd2bf41c223bc273068e6
|
||||
golang.org/x/crypto c10c31b5e94b6f7a0283272dc2bb27163dcea24b
|
||||
golang.org/x/net 4876518f9e71663000c348837735820161a42df7
|
||||
golang.org/x/oauth2 045497edb6234273d67dbc25da3f2ddbc4c4cacf
|
||||
golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb
|
||||
google.golang.org/api 9bf6e6e569ff057f75d9604a46c52928f17d2b54
|
||||
google.golang.org/appengine 12d5545dc1cfa6047a286d5e853841b6471f4c19
|
||||
google.golang.org/cloud 975617b05ea8a58727e6c1a06b6161ff4185a9f2
|
||||
google.golang.org/grpc d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994
|
||||
gopkg.in/check.v1 64131543e7896d5bcc6bd5a76287eb75ea96c673
|
||||
gopkg.in/square/go-jose.v1 40d457b439244b546f023d056628e5184136899b
|
||||
gopkg.in/yaml.v2 bef53efd0c76e49e6de55ead051f886bea7e9420
|
||||
rsc.io/letsencrypt e770c10b0f1a64775ae91d240407ce00d1a5bdeb https://github.com/dmcgowan/letsencrypt.git
|
||||
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
|
@ -51,7 +51,7 @@ Moby is NOT recommended for:
|
|||
|
||||
- Application developers looking for an easy way to run their applications in containers. We recommend Docker CE instead.
|
||||
- Enterprise IT and development teams looking for a ready-to-use, commercially supported container platform. We recommend Docker EE instead.
|
||||
- Anyone curious about containers and looking for an easy way to learn. We recommend the docker.com website instead.
|
||||
- Anyone curious about containers and looking for an easy way to learn. We recommend the [docker.com](https://www.docker.com/) website instead.
|
||||
|
||||
# Transitioning to Moby
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
# Working on the Engine API
|
||||
|
||||
The Engine API is an HTTP API used by the command-line client to communicate with the daemon. It can also be used by third-party software to control the daemon.
|
||||
|
||||
It consists of various components in this repository:
|
||||
|
||||
- `api/swagger.yaml` A Swagger definition of the API.
|
||||
- `api/types/` Types shared by both the client and server, representing various objects, options, responses, etc. Most are written manually, but some are automatically generated from the Swagger definition. See [#27919](https://github.com/docker/docker/issues/27919) for progress on this.
|
||||
- `cli/` The command-line client.
|
||||
- `client/` The Go client used by the command-line client. It can also be used by third-party Go programs.
|
||||
- `daemon/` The daemon, which serves the API.
|
||||
|
||||
## Swagger definition
|
||||
|
||||
The API is defined by the [Swagger](http://swagger.io/specification/) definition in `api/swagger.yaml`. This definition can be used to:
|
||||
|
||||
1. Automatically generate documentation.
|
||||
2. Automatically generate the Go server and client. (A work-in-progress.)
|
||||
3. Provide a machine readable version of the API for introspecting what it can do, automatically generating clients for other languages, etc.
|
||||
|
||||
## Updating the API documentation
|
||||
|
||||
The API documentation is generated entirely from `api/swagger.yaml`. If you make updates to the API, you'll need to edit this file to represent the change in the documentation.
|
||||
|
||||
The file is split into two main sections:
|
||||
|
||||
- `definitions`, which defines re-usable objects used in requests and responses
|
||||
- `paths`, which defines the API endpoints (and some inline objects which don't need to be reusable)
|
||||
|
||||
To make an edit, first look for the endpoint you want to edit under `paths`, then make the required edits. Endpoints may reference reusable objects with `$ref`, which can be found in the `definitions` section.
|
||||
|
||||
There is hopefully enough example material in the file for you to copy a similar pattern from elsewhere in the file (e.g. adding new fields or endpoints), but for the full reference, see the [Swagger specification](https://github.com/docker/docker/issues/27919)
|
||||
|
||||
`swagger.yaml` is validated by `hack/validate/swagger` to ensure it is a valid Swagger definition. This is useful for when you are making edits to ensure you are doing the right thing.
|
||||
|
||||
## Viewing the API documentation
|
||||
|
||||
When you make edits to `swagger.yaml`, you may want to check the generated API documentation to ensure it renders correctly.
|
||||
|
||||
Run `make swagger-docs` and a preview will be running at `http://localhost`. Some of the styling may be incorrect, but you'll be able to ensure that it is generating the correct documentation.
|
||||
|
||||
The production documentation is generated by vendoring `swagger.yaml` into [docker/docker.github.io](https://github.com/docker/docker.github.io).
|
|
@ -0,0 +1,23 @@
|
|||
package blkiodev
|
||||
|
||||
import "fmt"
|
||||
|
||||
// WeightDevice is a structure that holds device:weight pair
|
||||
type WeightDevice struct {
|
||||
Path string
|
||||
Weight uint16
|
||||
}
|
||||
|
||||
func (w *WeightDevice) String() string {
|
||||
return fmt.Sprintf("%s:%d", w.Path, w.Weight)
|
||||
}
|
||||
|
||||
// ThrottleDevice is a structure that holds device:rate_per_second pair
|
||||
type ThrottleDevice struct {
|
||||
Path string
|
||||
Rate uint64
|
||||
}
|
||||
|
||||
func (t *ThrottleDevice) String() string {
|
||||
return fmt.Sprintf("%s:%d", t.Path, t.Rate)
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
"github.com/docker/go-connections/nat"
|
||||
)
|
||||
|
||||
// MinimumDuration puts a minimum on user configured duration.
|
||||
// This is to prevent API error on time unit. For example, API may
|
||||
// set 3 as healthcheck interval with intention of 3 seconds, but
|
||||
// Docker interprets it as 3 nanoseconds.
|
||||
const MinimumDuration = 1 * time.Millisecond
|
||||
|
||||
// HealthConfig holds configuration settings for the HEALTHCHECK feature.
|
||||
type HealthConfig struct {
|
||||
// Test is the test to perform to check that the container is healthy.
|
||||
// An empty slice means to inherit the default.
|
||||
// The options are:
|
||||
// {} : inherit healthcheck
|
||||
// {"NONE"} : disable healthcheck
|
||||
// {"CMD", args...} : exec arguments directly
|
||||
// {"CMD-SHELL", command} : run command with system's default shell
|
||||
Test []string `json:",omitempty"`
|
||||
|
||||
// Zero means to inherit. Durations are expressed as integer nanoseconds.
|
||||
Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks.
|
||||
Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung.
|
||||
StartPeriod time.Duration `json:",omitempty"` // The start period for the container to initialize before the retries starts to count down.
|
||||
|
||||
// Retries is the number of consecutive failures needed to consider a container as unhealthy.
|
||||
// Zero means inherit.
|
||||
Retries int `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Config contains the configuration data about a container.
|
||||
// It should hold only portable information about the container.
|
||||
// Here, "portable" means "independent from the host we are running on".
|
||||
// Non-portable information *should* appear in HostConfig.
|
||||
// All fields added to this struct must be marked `omitempty` to keep getting
|
||||
// predictable hashes from the old `v1Compatibility` configuration.
|
||||
type Config struct {
|
||||
Hostname string // Hostname
|
||||
Domainname string // Domainname
|
||||
User string // User that will run the command(s) inside the container, also support user:group
|
||||
AttachStdin bool // Attach the standard input, makes possible user interaction
|
||||
AttachStdout bool // Attach the standard output
|
||||
AttachStderr bool // Attach the standard error
|
||||
ExposedPorts nat.PortSet `json:",omitempty"` // List of exposed ports
|
||||
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
||||
OpenStdin bool // Open stdin
|
||||
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
|
||||
Env []string // List of environment variable to set in the container
|
||||
Cmd strslice.StrSlice // Command to run when starting the container
|
||||
Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
|
||||
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific)
|
||||
Image string // Name of the image as it was passed by the operator (e.g. could be symbolic)
|
||||
Volumes map[string]struct{} // List of volumes (mounts) used for the container
|
||||
WorkingDir string // Current directory (PWD) in the command will be launched
|
||||
Entrypoint strslice.StrSlice // Entrypoint to run when starting the container
|
||||
NetworkDisabled bool `json:",omitempty"` // Is network disabled
|
||||
MacAddress string `json:",omitempty"` // Mac Address of the container
|
||||
OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile
|
||||
Labels map[string]string // List of labels set to this container
|
||||
StopSignal string `json:",omitempty"` // Signal to stop a container
|
||||
StopTimeout *int `json:",omitempty"` // Timeout (in seconds) to stop a container
|
||||
Shell strslice.StrSlice `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT
|
||||
}
|
21
vendor/github.com/docker/docker/api/types/container/container_changes.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/api/types/container/container_changes.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
package container
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ContainerChangeResponseItem container change response item
|
||||
// swagger:model ContainerChangeResponseItem
|
||||
type ContainerChangeResponseItem struct {
|
||||
|
||||
// Kind of change
|
||||
// Required: true
|
||||
Kind uint8 `json:"Kind"`
|
||||
|
||||
// Path to file that has changed
|
||||
// Required: true
|
||||
Path string `json:"Path"`
|
||||
}
|
21
vendor/github.com/docker/docker/api/types/container/container_create.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/api/types/container/container_create.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
package container
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ContainerCreateCreatedBody container create created body
|
||||
// swagger:model ContainerCreateCreatedBody
|
||||
type ContainerCreateCreatedBody struct {
|
||||
|
||||
// The ID of the created container
|
||||
// Required: true
|
||||
ID string `json:"Id"`
|
||||
|
||||
// Warnings encountered when creating the container
|
||||
// Required: true
|
||||
Warnings []string `json:"Warnings"`
|
||||
}
|
21
vendor/github.com/docker/docker/api/types/container/container_top.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/api/types/container/container_top.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
package container
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ContainerTopOKBody container top o k body
|
||||
// swagger:model ContainerTopOKBody
|
||||
type ContainerTopOKBody struct {
|
||||
|
||||
// Each process running in the container, where each is process is an array of values corresponding to the titles
|
||||
// Required: true
|
||||
Processes [][]string `json:"Processes"`
|
||||
|
||||
// The ps column titles
|
||||
// Required: true
|
||||
Titles []string `json:"Titles"`
|
||||
}
|
17
vendor/github.com/docker/docker/api/types/container/container_update.go
generated
vendored
Normal file
17
vendor/github.com/docker/docker/api/types/container/container_update.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
package container
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ContainerUpdateOKBody container update o k body
|
||||
// swagger:model ContainerUpdateOKBody
|
||||
type ContainerUpdateOKBody struct {
|
||||
|
||||
// warnings
|
||||
// Required: true
|
||||
Warnings []string `json:"Warnings"`
|
||||
}
|
17
vendor/github.com/docker/docker/api/types/container/container_wait.go
generated
vendored
Normal file
17
vendor/github.com/docker/docker/api/types/container/container_wait.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
package container
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ContainerWaitOKBody container wait o k body
|
||||
// swagger:model ContainerWaitOKBody
|
||||
type ContainerWaitOKBody struct {
|
||||
|
||||
// Exit code of the container
|
||||
// Required: true
|
||||
StatusCode int64 `json:"StatusCode"`
|
||||
}
|
385
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
Normal file
385
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
Normal file
|
@ -0,0 +1,385 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/blkiodev"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
// Isolation represents the isolation technology of a container. The supported
|
||||
// values are platform specific
|
||||
type Isolation string
|
||||
|
||||
// IsDefault indicates the default isolation technology of a container. On Linux this
|
||||
// is the native driver. On Windows, this is a Windows Server Container.
|
||||
func (i Isolation) IsDefault() bool {
|
||||
return strings.ToLower(string(i)) == "default" || string(i) == ""
|
||||
}
|
||||
|
||||
// IpcMode represents the container ipc stack.
|
||||
type IpcMode string
|
||||
|
||||
// IsPrivate indicates whether the container uses its own private ipc namespace which can not be shared.
|
||||
func (n IpcMode) IsPrivate() bool {
|
||||
return n == "private"
|
||||
}
|
||||
|
||||
// IsHost indicates whether the container shares the host's ipc namespace.
|
||||
func (n IpcMode) IsHost() bool {
|
||||
return n == "host"
|
||||
}
|
||||
|
||||
// IsShareable indicates whether the container's ipc namespace can be shared with another container.
|
||||
func (n IpcMode) IsShareable() bool {
|
||||
return n == "shareable"
|
||||
}
|
||||
|
||||
// IsContainer indicates whether the container uses another container's ipc namespace.
|
||||
func (n IpcMode) IsContainer() bool {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
return len(parts) > 1 && parts[0] == "container"
|
||||
}
|
||||
|
||||
// IsNone indicates whether container IpcMode is set to "none".
|
||||
func (n IpcMode) IsNone() bool {
|
||||
return n == "none"
|
||||
}
|
||||
|
||||
// IsEmpty indicates whether container IpcMode is empty
|
||||
func (n IpcMode) IsEmpty() bool {
|
||||
return n == ""
|
||||
}
|
||||
|
||||
// Valid indicates whether the ipc mode is valid.
|
||||
func (n IpcMode) Valid() bool {
|
||||
return n.IsEmpty() || n.IsNone() || n.IsPrivate() || n.IsHost() || n.IsShareable() || n.IsContainer()
|
||||
}
|
||||
|
||||
// Container returns the name of the container ipc stack is going to be used.
|
||||
func (n IpcMode) Container() string {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
if len(parts) > 1 && parts[0] == "container" {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// NetworkMode represents the container network stack.
|
||||
type NetworkMode string
|
||||
|
||||
// IsNone indicates whether container isn't using a network stack.
|
||||
func (n NetworkMode) IsNone() bool {
|
||||
return n == "none"
|
||||
}
|
||||
|
||||
// IsDefault indicates whether container uses the default network stack.
|
||||
func (n NetworkMode) IsDefault() bool {
|
||||
return n == "default"
|
||||
}
|
||||
|
||||
// IsPrivate indicates whether container uses its private network stack.
|
||||
func (n NetworkMode) IsPrivate() bool {
|
||||
return !(n.IsHost() || n.IsContainer())
|
||||
}
|
||||
|
||||
// IsContainer indicates whether container uses a container network stack.
|
||||
func (n NetworkMode) IsContainer() bool {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
return len(parts) > 1 && parts[0] == "container"
|
||||
}
|
||||
|
||||
// ConnectedContainer is the id of the container which network this container is connected to.
|
||||
func (n NetworkMode) ConnectedContainer() string {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
//UserDefined indicates user-created network
|
||||
func (n NetworkMode) UserDefined() string {
|
||||
if n.IsUserDefined() {
|
||||
return string(n)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// UsernsMode represents userns mode in the container.
|
||||
type UsernsMode string
|
||||
|
||||
// IsHost indicates whether the container uses the host's userns.
|
||||
func (n UsernsMode) IsHost() bool {
|
||||
return n == "host"
|
||||
}
|
||||
|
||||
// IsPrivate indicates whether the container uses the a private userns.
|
||||
func (n UsernsMode) IsPrivate() bool {
|
||||
return !(n.IsHost())
|
||||
}
|
||||
|
||||
// Valid indicates whether the userns is valid.
|
||||
func (n UsernsMode) Valid() bool {
|
||||
parts := strings.Split(string(n), ":")
|
||||
switch mode := parts[0]; mode {
|
||||
case "", "host":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CgroupSpec represents the cgroup to use for the container.
|
||||
type CgroupSpec string
|
||||
|
||||
// IsContainer indicates whether the container is using another container cgroup
|
||||
func (c CgroupSpec) IsContainer() bool {
|
||||
parts := strings.SplitN(string(c), ":", 2)
|
||||
return len(parts) > 1 && parts[0] == "container"
|
||||
}
|
||||
|
||||
// Valid indicates whether the cgroup spec is valid.
|
||||
func (c CgroupSpec) Valid() bool {
|
||||
return c.IsContainer() || c == ""
|
||||
}
|
||||
|
||||
// Container returns the name of the container whose cgroup will be used.
|
||||
func (c CgroupSpec) Container() string {
|
||||
parts := strings.SplitN(string(c), ":", 2)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// UTSMode represents the UTS namespace of the container.
|
||||
type UTSMode string
|
||||
|
||||
// IsPrivate indicates whether the container uses its private UTS namespace.
|
||||
func (n UTSMode) IsPrivate() bool {
|
||||
return !(n.IsHost())
|
||||
}
|
||||
|
||||
// IsHost indicates whether the container uses the host's UTS namespace.
|
||||
func (n UTSMode) IsHost() bool {
|
||||
return n == "host"
|
||||
}
|
||||
|
||||
// Valid indicates whether the UTS namespace is valid.
|
||||
func (n UTSMode) Valid() bool {
|
||||
parts := strings.Split(string(n), ":")
|
||||
switch mode := parts[0]; mode {
|
||||
case "", "host":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// PidMode represents the pid namespace of the container.
|
||||
type PidMode string
|
||||
|
||||
// IsPrivate indicates whether the container uses its own new pid namespace.
|
||||
func (n PidMode) IsPrivate() bool {
|
||||
return !(n.IsHost() || n.IsContainer())
|
||||
}
|
||||
|
||||
// IsHost indicates whether the container uses the host's pid namespace.
|
||||
func (n PidMode) IsHost() bool {
|
||||
return n == "host"
|
||||
}
|
||||
|
||||
// IsContainer indicates whether the container uses a container's pid namespace.
|
||||
func (n PidMode) IsContainer() bool {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
return len(parts) > 1 && parts[0] == "container"
|
||||
}
|
||||
|
||||
// Valid indicates whether the pid namespace is valid.
|
||||
func (n PidMode) Valid() bool {
|
||||
parts := strings.Split(string(n), ":")
|
||||
switch mode := parts[0]; mode {
|
||||
case "", "host":
|
||||
case "container":
|
||||
if len(parts) != 2 || parts[1] == "" {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Container returns the name of the container whose pid namespace is going to be used.
|
||||
func (n PidMode) Container() string {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// DeviceMapping represents the device mapping between the host and the container.
|
||||
type DeviceMapping struct {
|
||||
PathOnHost string
|
||||
PathInContainer string
|
||||
CgroupPermissions string
|
||||
}
|
||||
|
||||
// RestartPolicy represents the restart policies of the container.
|
||||
type RestartPolicy struct {
|
||||
Name string
|
||||
MaximumRetryCount int
|
||||
}
|
||||
|
||||
// IsNone indicates whether the container has the "no" restart policy.
|
||||
// This means the container will not automatically restart when exiting.
|
||||
func (rp *RestartPolicy) IsNone() bool {
|
||||
return rp.Name == "no" || rp.Name == ""
|
||||
}
|
||||
|
||||
// IsAlways indicates whether the container has the "always" restart policy.
|
||||
// This means the container will automatically restart regardless of the exit status.
|
||||
func (rp *RestartPolicy) IsAlways() bool {
|
||||
return rp.Name == "always"
|
||||
}
|
||||
|
||||
// IsOnFailure indicates whether the container has the "on-failure" restart policy.
|
||||
// This means the container will automatically restart of exiting with a non-zero exit status.
|
||||
func (rp *RestartPolicy) IsOnFailure() bool {
|
||||
return rp.Name == "on-failure"
|
||||
}
|
||||
|
||||
// IsUnlessStopped indicates whether the container has the
|
||||
// "unless-stopped" restart policy. This means the container will
|
||||
// automatically restart unless user has put it to stopped state.
|
||||
func (rp *RestartPolicy) IsUnlessStopped() bool {
|
||||
return rp.Name == "unless-stopped"
|
||||
}
|
||||
|
||||
// IsSame compares two RestartPolicy to see if they are the same
|
||||
func (rp *RestartPolicy) IsSame(tp *RestartPolicy) bool {
|
||||
return rp.Name == tp.Name && rp.MaximumRetryCount == tp.MaximumRetryCount
|
||||
}
|
||||
|
||||
// LogMode is a type to define the available modes for logging
|
||||
// These modes affect how logs are handled when log messages start piling up.
|
||||
type LogMode string
|
||||
|
||||
// Available logging modes
|
||||
const (
|
||||
LogModeUnset = ""
|
||||
LogModeBlocking LogMode = "blocking"
|
||||
LogModeNonBlock LogMode = "non-blocking"
|
||||
)
|
||||
|
||||
// LogConfig represents the logging configuration of the container.
|
||||
type LogConfig struct {
|
||||
Type string
|
||||
Config map[string]string
|
||||
}
|
||||
|
||||
// Resources contains container's resources (cgroups config, ulimits...)
|
||||
type Resources struct {
|
||||
// Applicable to all platforms
|
||||
CPUShares int64 `json:"CpuShares"` // CPU shares (relative weight vs. other containers)
|
||||
Memory int64 // Memory limit (in bytes)
|
||||
NanoCPUs int64 `json:"NanoCpus"` // CPU quota in units of 10<sup>-9</sup> CPUs.
|
||||
|
||||
// Applicable to UNIX platforms
|
||||
CgroupParent string // Parent cgroup.
|
||||
BlkioWeight uint16 // Block IO weight (relative weight vs. other containers)
|
||||
BlkioWeightDevice []*blkiodev.WeightDevice
|
||||
BlkioDeviceReadBps []*blkiodev.ThrottleDevice
|
||||
BlkioDeviceWriteBps []*blkiodev.ThrottleDevice
|
||||
BlkioDeviceReadIOps []*blkiodev.ThrottleDevice
|
||||
BlkioDeviceWriteIOps []*blkiodev.ThrottleDevice
|
||||
CPUPeriod int64 `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period
|
||||
CPUQuota int64 `json:"CpuQuota"` // CPU CFS (Completely Fair Scheduler) quota
|
||||
CPURealtimePeriod int64 `json:"CpuRealtimePeriod"` // CPU real-time period
|
||||
CPURealtimeRuntime int64 `json:"CpuRealtimeRuntime"` // CPU real-time runtime
|
||||
CpusetCpus string // CpusetCpus 0-2, 0,1
|
||||
CpusetMems string // CpusetMems 0-2, 0,1
|
||||
Devices []DeviceMapping // List of devices to map inside the container
|
||||
DeviceCgroupRules []string // List of rule to be added to the device cgroup
|
||||
DiskQuota int64 // Disk limit (in bytes)
|
||||
KernelMemory int64 // Kernel memory limit (in bytes)
|
||||
MemoryReservation int64 // Memory soft limit (in bytes)
|
||||
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
|
||||
MemorySwappiness *int64 // Tuning container memory swappiness behaviour
|
||||
OomKillDisable *bool // Whether to disable OOM Killer or not
|
||||
PidsLimit int64 // Setting pids limit for a container
|
||||
Ulimits []*units.Ulimit // List of ulimits to be set in the container
|
||||
|
||||
// Applicable to Windows
|
||||
CPUCount int64 `json:"CpuCount"` // CPU count
|
||||
CPUPercent int64 `json:"CpuPercent"` // CPU percent
|
||||
IOMaximumIOps uint64 // Maximum IOps for the container system drive
|
||||
IOMaximumBandwidth uint64 // Maximum IO in bytes per second for the container system drive
|
||||
}
|
||||
|
||||
// UpdateConfig holds the mutable attributes of a Container.
|
||||
// Those attributes can be updated at runtime.
|
||||
type UpdateConfig struct {
|
||||
// Contains container's resources (cgroups, ulimits)
|
||||
Resources
|
||||
RestartPolicy RestartPolicy
|
||||
}
|
||||
|
||||
// HostConfig the non-portable Config structure of a container.
|
||||
// Here, "non-portable" means "dependent of the host we are running on".
|
||||
// Portable information *should* appear in Config.
|
||||
type HostConfig struct {
|
||||
// Applicable to all platforms
|
||||
Binds []string // List of volume bindings for this container
|
||||
ContainerIDFile string // File (path) where the containerId is written
|
||||
LogConfig LogConfig // Configuration of the logs for this container
|
||||
NetworkMode NetworkMode // Network mode to use for the container
|
||||
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
|
||||
RestartPolicy RestartPolicy // Restart policy to be used for the container
|
||||
AutoRemove bool // Automatically remove container when it exits
|
||||
VolumeDriver string // Name of the volume driver used to mount volumes
|
||||
VolumesFrom []string // List of volumes to take from other container
|
||||
|
||||
// Applicable to UNIX platforms
|
||||
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
|
||||
CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container
|
||||
DNS []string `json:"Dns"` // List of DNS server to lookup
|
||||
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
|
||||
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
|
||||
ExtraHosts []string // List of extra hosts
|
||||
GroupAdd []string // List of additional groups that the container process will run as
|
||||
IpcMode IpcMode // IPC namespace to use for the container
|
||||
Cgroup CgroupSpec // Cgroup to use for the container
|
||||
Links []string // List of links (in the name:alias form)
|
||||
OomScoreAdj int // Container preference for OOM-killing
|
||||
PidMode PidMode // PID namespace to use for the container
|
||||
Privileged bool // Is the container in privileged mode
|
||||
PublishAllPorts bool // Should docker publish all exposed port for the container
|
||||
ReadonlyRootfs bool // Is the container root filesystem in read-only
|
||||
SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux.
|
||||
StorageOpt map[string]string `json:",omitempty"` // Storage driver options per container.
|
||||
Tmpfs map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container
|
||||
UTSMode UTSMode // UTS namespace to use for the container
|
||||
UsernsMode UsernsMode // The user namespace to use for the container
|
||||
ShmSize int64 // Total shm memory usage
|
||||
Sysctls map[string]string `json:",omitempty"` // List of Namespaced sysctls used for the container
|
||||
Runtime string `json:",omitempty"` // Runtime to use with this container
|
||||
|
||||
// Applicable to Windows
|
||||
ConsoleSize [2]uint // Initial console size (height,width)
|
||||
Isolation Isolation // Isolation technology of the container (e.g. default, hyperv)
|
||||
|
||||
// Contains container's resources (cgroups, ulimits)
|
||||
Resources
|
||||
|
||||
// Mounts specs used by the container
|
||||
Mounts []mount.Mount `json:",omitempty"`
|
||||
|
||||
// Run a custom init inside the container, if null, use the daemon's configured settings
|
||||
Init *bool `json:",omitempty"`
|
||||
}
|
41
vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go
generated
vendored
Normal file
41
vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
// +build !windows
|
||||
|
||||
package container
|
||||
|
||||
// IsValid indicates if an isolation technology is valid
|
||||
func (i Isolation) IsValid() bool {
|
||||
return i.IsDefault()
|
||||
}
|
||||
|
||||
// NetworkName returns the name of the network stack.
|
||||
func (n NetworkMode) NetworkName() string {
|
||||
if n.IsBridge() {
|
||||
return "bridge"
|
||||
} else if n.IsHost() {
|
||||
return "host"
|
||||
} else if n.IsContainer() {
|
||||
return "container"
|
||||
} else if n.IsNone() {
|
||||
return "none"
|
||||
} else if n.IsDefault() {
|
||||
return "default"
|
||||
} else if n.IsUserDefined() {
|
||||
return n.UserDefined()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsBridge indicates whether container uses the bridge network stack
|
||||
func (n NetworkMode) IsBridge() bool {
|
||||
return n == "bridge"
|
||||
}
|
||||
|
||||
// IsHost indicates whether container uses the host network stack.
|
||||
func (n NetworkMode) IsHost() bool {
|
||||
return n == "host"
|
||||
}
|
||||
|
||||
// IsUserDefined indicates user-created network
|
||||
func (n NetworkMode) IsUserDefined() bool {
|
||||
return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer()
|
||||
}
|
54
vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go
generated
vendored
Normal file
54
vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsBridge indicates whether container uses the bridge network stack
|
||||
// in windows it is given the name NAT
|
||||
func (n NetworkMode) IsBridge() bool {
|
||||
return n == "nat"
|
||||
}
|
||||
|
||||
// IsHost indicates whether container uses the host network stack.
|
||||
// returns false as this is not supported by windows
|
||||
func (n NetworkMode) IsHost() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsUserDefined indicates user-created network
|
||||
func (n NetworkMode) IsUserDefined() bool {
|
||||
return !n.IsDefault() && !n.IsNone() && !n.IsBridge() && !n.IsContainer()
|
||||
}
|
||||
|
||||
// IsHyperV indicates the use of a Hyper-V partition for isolation
|
||||
func (i Isolation) IsHyperV() bool {
|
||||
return strings.ToLower(string(i)) == "hyperv"
|
||||
}
|
||||
|
||||
// IsProcess indicates the use of process isolation
|
||||
func (i Isolation) IsProcess() bool {
|
||||
return strings.ToLower(string(i)) == "process"
|
||||
}
|
||||
|
||||
// IsValid indicates if an isolation technology is valid
|
||||
func (i Isolation) IsValid() bool {
|
||||
return i.IsDefault() || i.IsHyperV() || i.IsProcess()
|
||||
}
|
||||
|
||||
// NetworkName returns the name of the network stack.
|
||||
func (n NetworkMode) NetworkName() string {
|
||||
if n.IsDefault() {
|
||||
return "default"
|
||||
} else if n.IsBridge() {
|
||||
return "nat"
|
||||
} else if n.IsNone() {
|
||||
return "none"
|
||||
} else if n.IsContainer() {
|
||||
return "container"
|
||||
} else if n.IsUserDefined() {
|
||||
return n.UserDefined()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
22
vendor/github.com/docker/docker/api/types/container/waitcondition.go
generated
vendored
Normal file
22
vendor/github.com/docker/docker/api/types/container/waitcondition.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
package container
|
||||
|
||||
// WaitCondition is a type used to specify a container state for which
|
||||
// to wait.
|
||||
type WaitCondition string
|
||||
|
||||
// Possible WaitCondition Values.
|
||||
//
|
||||
// WaitConditionNotRunning (default) is used to wait for any of the non-running
|
||||
// states: "created", "exited", "dead", "removing", or "removed".
|
||||
//
|
||||
// WaitConditionNextExit is used to wait for the next time the state changes
|
||||
// to a non-running state. If the state is currently "created" or "exited",
|
||||
// this would cause Wait() to block until either the container runs and exits
|
||||
// or is removed.
|
||||
//
|
||||
// WaitConditionRemoved is used to wait for the container to be removed.
|
||||
const (
|
||||
WaitConditionNotRunning WaitCondition = "not-running"
|
||||
WaitConditionNextExit WaitCondition = "next-exit"
|
||||
WaitConditionRemoved WaitCondition = "removed"
|
||||
)
|
|
@ -0,0 +1,130 @@
|
|||
package mount
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Type represents the type of a mount.
|
||||
type Type string
|
||||
|
||||
// Type constants
|
||||
const (
|
||||
// TypeBind is the type for mounting host dir
|
||||
TypeBind Type = "bind"
|
||||
// TypeVolume is the type for remote storage volumes
|
||||
TypeVolume Type = "volume"
|
||||
// TypeTmpfs is the type for mounting tmpfs
|
||||
TypeTmpfs Type = "tmpfs"
|
||||
// TypeNamedPipe is the type for mounting Windows named pipes
|
||||
TypeNamedPipe Type = "npipe"
|
||||
)
|
||||
|
||||
// Mount represents a mount (volume).
|
||||
type Mount struct {
|
||||
Type Type `json:",omitempty"`
|
||||
// Source specifies the name of the mount. Depending on mount type, this
|
||||
// may be a volume name or a host path, or even ignored.
|
||||
// Source is not supported for tmpfs (must be an empty value)
|
||||
Source string `json:",omitempty"`
|
||||
Target string `json:",omitempty"`
|
||||
ReadOnly bool `json:",omitempty"`
|
||||
Consistency Consistency `json:",omitempty"`
|
||||
|
||||
BindOptions *BindOptions `json:",omitempty"`
|
||||
VolumeOptions *VolumeOptions `json:",omitempty"`
|
||||
TmpfsOptions *TmpfsOptions `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Propagation represents the propagation of a mount.
|
||||
type Propagation string
|
||||
|
||||
const (
|
||||
// PropagationRPrivate RPRIVATE
|
||||
PropagationRPrivate Propagation = "rprivate"
|
||||
// PropagationPrivate PRIVATE
|
||||
PropagationPrivate Propagation = "private"
|
||||
// PropagationRShared RSHARED
|
||||
PropagationRShared Propagation = "rshared"
|
||||
// PropagationShared SHARED
|
||||
PropagationShared Propagation = "shared"
|
||||
// PropagationRSlave RSLAVE
|
||||
PropagationRSlave Propagation = "rslave"
|
||||
// PropagationSlave SLAVE
|
||||
PropagationSlave Propagation = "slave"
|
||||
)
|
||||
|
||||
// Propagations is the list of all valid mount propagations
|
||||
var Propagations = []Propagation{
|
||||
PropagationRPrivate,
|
||||
PropagationPrivate,
|
||||
PropagationRShared,
|
||||
PropagationShared,
|
||||
PropagationRSlave,
|
||||
PropagationSlave,
|
||||
}
|
||||
|
||||
// Consistency represents the consistency requirements of a mount.
|
||||
type Consistency string
|
||||
|
||||
const (
|
||||
// ConsistencyFull guarantees bind mount-like consistency
|
||||
ConsistencyFull Consistency = "consistent"
|
||||
// ConsistencyCached mounts can cache read data and FS structure
|
||||
ConsistencyCached Consistency = "cached"
|
||||
// ConsistencyDelegated mounts can cache read and written data and structure
|
||||
ConsistencyDelegated Consistency = "delegated"
|
||||
// ConsistencyDefault provides "consistent" behavior unless overridden
|
||||
ConsistencyDefault Consistency = "default"
|
||||
)
|
||||
|
||||
// BindOptions defines options specific to mounts of type "bind".
|
||||
type BindOptions struct {
|
||||
Propagation Propagation `json:",omitempty"`
|
||||
}
|
||||
|
||||
// VolumeOptions represents the options for a mount of type volume.
|
||||
type VolumeOptions struct {
|
||||
NoCopy bool `json:",omitempty"`
|
||||
Labels map[string]string `json:",omitempty"`
|
||||
DriverConfig *Driver `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Driver represents a volume driver.
|
||||
type Driver struct {
|
||||
Name string `json:",omitempty"`
|
||||
Options map[string]string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// TmpfsOptions defines options specific to mounts of type "tmpfs".
|
||||
type TmpfsOptions struct {
|
||||
// Size sets the size of the tmpfs, in bytes.
|
||||
//
|
||||
// This will be converted to an operating system specific value
|
||||
// depending on the host. For example, on linux, it will be converted to
|
||||
// use a 'k', 'm' or 'g' syntax. BSD, though not widely supported with
|
||||
// docker, uses a straight byte value.
|
||||
//
|
||||
// Percentages are not supported.
|
||||
SizeBytes int64 `json:",omitempty"`
|
||||
// Mode of the tmpfs upon creation
|
||||
Mode os.FileMode `json:",omitempty"`
|
||||
|
||||
// TODO(stevvooe): There are several more tmpfs flags, specified in the
|
||||
// daemon, that are accepted. Only the most basic are added for now.
|
||||
//
|
||||
// From docker/docker/pkg/mount/flags.go:
|
||||
//
|
||||
// var validFlags = map[string]bool{
|
||||
// "": true,
|
||||
// "size": true, X
|
||||
// "mode": true, X
|
||||
// "uid": true,
|
||||
// "gid": true,
|
||||
// "nr_inodes": true,
|
||||
// "nr_blocks": true,
|
||||
// "mpol": true,
|
||||
// }
|
||||
//
|
||||
// Some of these may be straightforward to add, but others, such as
|
||||
// uid/gid have implications in a clustered system.
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package strslice
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// StrSlice represents a string or an array of strings.
|
||||
// We need to override the json decoder to accept both options.
|
||||
type StrSlice []string
|
||||
|
||||
// UnmarshalJSON decodes the byte slice whether it's a string or an array of
|
||||
// strings. This method is needed to implement json.Unmarshaler.
|
||||
func (e *StrSlice) UnmarshalJSON(b []byte) error {
|
||||
if len(b) == 0 {
|
||||
// With no input, we preserve the existing value by returning nil and
|
||||
// leaving the target alone. This allows defining default values for
|
||||
// the type.
|
||||
return nil
|
||||
}
|
||||
|
||||
p := make([]string, 0, 1)
|
||||
if err := json.Unmarshal(b, &p); err != nil {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
p = append(p, s)
|
||||
}
|
||||
|
||||
*e = p
|
||||
return nil
|
||||
}
|
46
vendor/github.com/docker/docker/builder/dockerfile/command/command.go
generated
vendored
Normal file
46
vendor/github.com/docker/docker/builder/dockerfile/command/command.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Package command contains the set of Dockerfile commands.
|
||||
package command
|
||||
|
||||
// Define constants for the command strings
|
||||
const (
|
||||
Add = "add"
|
||||
Arg = "arg"
|
||||
Cmd = "cmd"
|
||||
Copy = "copy"
|
||||
Entrypoint = "entrypoint"
|
||||
Env = "env"
|
||||
Expose = "expose"
|
||||
From = "from"
|
||||
Healthcheck = "healthcheck"
|
||||
Label = "label"
|
||||
Maintainer = "maintainer"
|
||||
Onbuild = "onbuild"
|
||||
Run = "run"
|
||||
Shell = "shell"
|
||||
StopSignal = "stopsignal"
|
||||
User = "user"
|
||||
Volume = "volume"
|
||||
Workdir = "workdir"
|
||||
)
|
||||
|
||||
// Commands is list of all Dockerfile commands
|
||||
var Commands = map[string]struct{}{
|
||||
Add: {},
|
||||
Arg: {},
|
||||
Cmd: {},
|
||||
Copy: {},
|
||||
Entrypoint: {},
|
||||
Env: {},
|
||||
Expose: {},
|
||||
From: {},
|
||||
Healthcheck: {},
|
||||
Label: {},
|
||||
Maintainer: {},
|
||||
Onbuild: {},
|
||||
Run: {},
|
||||
Shell: {},
|
||||
StopSignal: {},
|
||||
User: {},
|
||||
Volume: {},
|
||||
Workdir: {},
|
||||
}
|
183
vendor/github.com/docker/docker/builder/dockerfile/instructions/bflag.go
generated
vendored
Normal file
183
vendor/github.com/docker/docker/builder/dockerfile/instructions/bflag.go
generated
vendored
Normal file
|
@ -0,0 +1,183 @@
|
|||
package instructions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FlagType is the type of the build flag
|
||||
type FlagType int
|
||||
|
||||
const (
|
||||
boolType FlagType = iota
|
||||
stringType
|
||||
)
|
||||
|
||||
// BFlags contains all flags information for the builder
|
||||
type BFlags struct {
|
||||
Args []string // actual flags/args from cmd line
|
||||
flags map[string]*Flag
|
||||
used map[string]*Flag
|
||||
Err error
|
||||
}
|
||||
|
||||
// Flag contains all information for a flag
|
||||
type Flag struct {
|
||||
bf *BFlags
|
||||
name string
|
||||
flagType FlagType
|
||||
Value string
|
||||
}
|
||||
|
||||
// NewBFlags returns the new BFlags struct
|
||||
func NewBFlags() *BFlags {
|
||||
return &BFlags{
|
||||
flags: make(map[string]*Flag),
|
||||
used: make(map[string]*Flag),
|
||||
}
|
||||
}
|
||||
|
||||
// NewBFlagsWithArgs returns the new BFlags struct with Args set to args
|
||||
func NewBFlagsWithArgs(args []string) *BFlags {
|
||||
flags := NewBFlags()
|
||||
flags.Args = args
|
||||
return flags
|
||||
}
|
||||
|
||||
// AddBool adds a bool flag to BFlags
|
||||
// Note, any error will be generated when Parse() is called (see Parse).
|
||||
func (bf *BFlags) AddBool(name string, def bool) *Flag {
|
||||
flag := bf.addFlag(name, boolType)
|
||||
if flag == nil {
|
||||
return nil
|
||||
}
|
||||
if def {
|
||||
flag.Value = "true"
|
||||
} else {
|
||||
flag.Value = "false"
|
||||
}
|
||||
return flag
|
||||
}
|
||||
|
||||
// AddString adds a string flag to BFlags
|
||||
// Note, any error will be generated when Parse() is called (see Parse).
|
||||
func (bf *BFlags) AddString(name string, def string) *Flag {
|
||||
flag := bf.addFlag(name, stringType)
|
||||
if flag == nil {
|
||||
return nil
|
||||
}
|
||||
flag.Value = def
|
||||
return flag
|
||||
}
|
||||
|
||||
// addFlag is a generic func used by the other AddXXX() func
|
||||
// to add a new flag to the BFlags struct.
|
||||
// Note, any error will be generated when Parse() is called (see Parse).
|
||||
func (bf *BFlags) addFlag(name string, flagType FlagType) *Flag {
|
||||
if _, ok := bf.flags[name]; ok {
|
||||
bf.Err = fmt.Errorf("Duplicate flag defined: %s", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
newFlag := &Flag{
|
||||
bf: bf,
|
||||
name: name,
|
||||
flagType: flagType,
|
||||
}
|
||||
bf.flags[name] = newFlag
|
||||
|
||||
return newFlag
|
||||
}
|
||||
|
||||
// IsUsed checks if the flag is used
|
||||
func (fl *Flag) IsUsed() bool {
|
||||
if _, ok := fl.bf.used[fl.name]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsTrue checks if a bool flag is true
|
||||
func (fl *Flag) IsTrue() bool {
|
||||
if fl.flagType != boolType {
|
||||
// Should never get here
|
||||
panic(fmt.Errorf("Trying to use IsTrue on a non-boolean: %s", fl.name))
|
||||
}
|
||||
return fl.Value == "true"
|
||||
}
|
||||
|
||||
// Parse parses and checks if the BFlags is valid.
|
||||
// Any error noticed during the AddXXX() funcs will be generated/returned
|
||||
// here. We do this because an error during AddXXX() is more like a
|
||||
// compile time error so it doesn't matter too much when we stop our
|
||||
// processing as long as we do stop it, so this allows the code
|
||||
// around AddXXX() to be just:
|
||||
// defFlag := AddString("description", "")
|
||||
// w/o needing to add an if-statement around each one.
|
||||
func (bf *BFlags) Parse() error {
|
||||
// If there was an error while defining the possible flags
|
||||
// go ahead and bubble it back up here since we didn't do it
|
||||
// earlier in the processing
|
||||
if bf.Err != nil {
|
||||
return fmt.Errorf("Error setting up flags: %s", bf.Err)
|
||||
}
|
||||
|
||||
for _, arg := range bf.Args {
|
||||
if !strings.HasPrefix(arg, "--") {
|
||||
return fmt.Errorf("Arg should start with -- : %s", arg)
|
||||
}
|
||||
|
||||
if arg == "--" {
|
||||
return nil
|
||||
}
|
||||
|
||||
arg = arg[2:]
|
||||
value := ""
|
||||
|
||||
index := strings.Index(arg, "=")
|
||||
if index >= 0 {
|
||||
value = arg[index+1:]
|
||||
arg = arg[:index]
|
||||
}
|
||||
|
||||
flag, ok := bf.flags[arg]
|
||||
if !ok {
|
||||
return fmt.Errorf("Unknown flag: %s", arg)
|
||||
}
|
||||
|
||||
if _, ok = bf.used[arg]; ok {
|
||||
return fmt.Errorf("Duplicate flag specified: %s", arg)
|
||||
}
|
||||
|
||||
bf.used[arg] = flag
|
||||
|
||||
switch flag.flagType {
|
||||
case boolType:
|
||||
// value == "" is only ok if no "=" was specified
|
||||
if index >= 0 && value == "" {
|
||||
return fmt.Errorf("Missing a value on flag: %s", arg)
|
||||
}
|
||||
|
||||
lower := strings.ToLower(value)
|
||||
if lower == "" {
|
||||
flag.Value = "true"
|
||||
} else if lower == "true" || lower == "false" {
|
||||
flag.Value = lower
|
||||
} else {
|
||||
return fmt.Errorf("Expecting boolean value for flag %s, not: %s", arg, value)
|
||||
}
|
||||
|
||||
case stringType:
|
||||
if index < 0 {
|
||||
return fmt.Errorf("Missing a value on flag: %s", arg)
|
||||
}
|
||||
flag.Value = value
|
||||
|
||||
default:
|
||||
panic("No idea what kind of flag we have! Should never get here!")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
396
vendor/github.com/docker/docker/builder/dockerfile/instructions/commands.go
generated
vendored
Normal file
396
vendor/github.com/docker/docker/builder/dockerfile/instructions/commands.go
generated
vendored
Normal file
|
@ -0,0 +1,396 @@
|
|||
package instructions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
)
|
||||
|
||||
// KeyValuePair represent an arbitrary named value (usefull in slice insted of map[string] string to preserve ordering)
|
||||
type KeyValuePair struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (kvp *KeyValuePair) String() string {
|
||||
return kvp.Key + "=" + kvp.Value
|
||||
}
|
||||
|
||||
// Command is implemented by every command present in a dockerfile
|
||||
type Command interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
// KeyValuePairs is a slice of KeyValuePair
|
||||
type KeyValuePairs []KeyValuePair
|
||||
|
||||
// withNameAndCode is the base of every command in a Dockerfile (String() returns its source code)
|
||||
type withNameAndCode struct {
|
||||
code string
|
||||
name string
|
||||
}
|
||||
|
||||
func (c *withNameAndCode) String() string {
|
||||
return c.code
|
||||
}
|
||||
|
||||
// Name of the command
|
||||
func (c *withNameAndCode) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func newWithNameAndCode(req parseRequest) withNameAndCode {
|
||||
return withNameAndCode{code: strings.TrimSpace(req.original), name: req.command}
|
||||
}
|
||||
|
||||
// SingleWordExpander is a provider for variable expansion where 1 word => 1 output
|
||||
type SingleWordExpander func(word string) (string, error)
|
||||
|
||||
// SupportsSingleWordExpansion interface marks a command as supporting variable expansion
|
||||
type SupportsSingleWordExpansion interface {
|
||||
Expand(expander SingleWordExpander) error
|
||||
}
|
||||
|
||||
// PlatformSpecific adds platform checks to a command
|
||||
type PlatformSpecific interface {
|
||||
CheckPlatform(platform string) error
|
||||
}
|
||||
|
||||
func expandKvp(kvp KeyValuePair, expander SingleWordExpander) (KeyValuePair, error) {
|
||||
key, err := expander(kvp.Key)
|
||||
if err != nil {
|
||||
return KeyValuePair{}, err
|
||||
}
|
||||
value, err := expander(kvp.Value)
|
||||
if err != nil {
|
||||
return KeyValuePair{}, err
|
||||
}
|
||||
return KeyValuePair{Key: key, Value: value}, nil
|
||||
}
|
||||
func expandKvpsInPlace(kvps KeyValuePairs, expander SingleWordExpander) error {
|
||||
for i, kvp := range kvps {
|
||||
newKvp, err := expandKvp(kvp, expander)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kvps[i] = newKvp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func expandSliceInPlace(values []string, expander SingleWordExpander) error {
|
||||
for i, v := range values {
|
||||
newValue, err := expander(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
values[i] = newValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnvCommand : ENV key1 value1 [keyN valueN...]
|
||||
type EnvCommand struct {
|
||||
withNameAndCode
|
||||
Env KeyValuePairs // kvp slice instead of map to preserve ordering
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *EnvCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandKvpsInPlace(c.Env, expander)
|
||||
}
|
||||
|
||||
// MaintainerCommand : MAINTAINER maintainer_name
|
||||
type MaintainerCommand struct {
|
||||
withNameAndCode
|
||||
Maintainer string
|
||||
}
|
||||
|
||||
// LabelCommand : LABEL some json data describing the image
|
||||
//
|
||||
// Sets the Label variable foo to bar,
|
||||
//
|
||||
type LabelCommand struct {
|
||||
withNameAndCode
|
||||
Labels KeyValuePairs // kvp slice instead of map to preserve ordering
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *LabelCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandKvpsInPlace(c.Labels, expander)
|
||||
}
|
||||
|
||||
// SourcesAndDest represent a list of source files and a destination
|
||||
type SourcesAndDest []string
|
||||
|
||||
// Sources list the source paths
|
||||
func (s SourcesAndDest) Sources() []string {
|
||||
res := make([]string, len(s)-1)
|
||||
copy(res, s[:len(s)-1])
|
||||
return res
|
||||
}
|
||||
|
||||
// Dest path of the operation
|
||||
func (s SourcesAndDest) Dest() string {
|
||||
return s[len(s)-1]
|
||||
}
|
||||
|
||||
// AddCommand : ADD foo /path
|
||||
//
|
||||
// Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling
|
||||
// exist here. If you do not wish to have this automatic handling, use COPY.
|
||||
//
|
||||
type AddCommand struct {
|
||||
withNameAndCode
|
||||
SourcesAndDest
|
||||
Chown string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *AddCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandSliceInPlace(c.SourcesAndDest, expander)
|
||||
}
|
||||
|
||||
// CopyCommand : COPY foo /path
|
||||
//
|
||||
// Same as 'ADD' but without the tar and remote url handling.
|
||||
//
|
||||
type CopyCommand struct {
|
||||
withNameAndCode
|
||||
SourcesAndDest
|
||||
From string
|
||||
Chown string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *CopyCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandSliceInPlace(c.SourcesAndDest, expander)
|
||||
}
|
||||
|
||||
// OnbuildCommand : ONBUILD <some other command>
|
||||
type OnbuildCommand struct {
|
||||
withNameAndCode
|
||||
Expression string
|
||||
}
|
||||
|
||||
// WorkdirCommand : WORKDIR /tmp
|
||||
//
|
||||
// Set the working directory for future RUN/CMD/etc statements.
|
||||
//
|
||||
type WorkdirCommand struct {
|
||||
withNameAndCode
|
||||
Path string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *WorkdirCommand) Expand(expander SingleWordExpander) error {
|
||||
p, err := expander(c.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Path = p
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShellDependantCmdLine represents a cmdline optionaly prepended with the shell
|
||||
type ShellDependantCmdLine struct {
|
||||
CmdLine strslice.StrSlice
|
||||
PrependShell bool
|
||||
}
|
||||
|
||||
// RunCommand : RUN some command yo
|
||||
//
|
||||
// run a command and commit the image. Args are automatically prepended with
|
||||
// the current SHELL which defaults to 'sh -c' under linux or 'cmd /S /C' under
|
||||
// Windows, in the event there is only one argument The difference in processing:
|
||||
//
|
||||
// RUN echo hi # sh -c echo hi (Linux)
|
||||
// RUN echo hi # cmd /S /C echo hi (Windows)
|
||||
// RUN [ "echo", "hi" ] # echo hi
|
||||
//
|
||||
type RunCommand struct {
|
||||
withNameAndCode
|
||||
ShellDependantCmdLine
|
||||
}
|
||||
|
||||
// CmdCommand : CMD foo
|
||||
//
|
||||
// Set the default command to run in the container (which may be empty).
|
||||
// Argument handling is the same as RUN.
|
||||
//
|
||||
type CmdCommand struct {
|
||||
withNameAndCode
|
||||
ShellDependantCmdLine
|
||||
}
|
||||
|
||||
// HealthCheckCommand : HEALTHCHECK foo
|
||||
//
|
||||
// Set the default healthcheck command to run in the container (which may be empty).
|
||||
// Argument handling is the same as RUN.
|
||||
//
|
||||
type HealthCheckCommand struct {
|
||||
withNameAndCode
|
||||
Health *container.HealthConfig
|
||||
}
|
||||
|
||||
// EntrypointCommand : ENTRYPOINT /usr/sbin/nginx
|
||||
//
|
||||
// Set the entrypoint to /usr/sbin/nginx. Will accept the CMD as the arguments
|
||||
// to /usr/sbin/nginx. Uses the default shell if not in JSON format.
|
||||
//
|
||||
// Handles command processing similar to CMD and RUN, only req.runConfig.Entrypoint
|
||||
// is initialized at newBuilder time instead of through argument parsing.
|
||||
//
|
||||
type EntrypointCommand struct {
|
||||
withNameAndCode
|
||||
ShellDependantCmdLine
|
||||
}
|
||||
|
||||
// ExposeCommand : EXPOSE 6667/tcp 7000/tcp
|
||||
//
|
||||
// Expose ports for links and port mappings. This all ends up in
|
||||
// req.runConfig.ExposedPorts for runconfig.
|
||||
//
|
||||
type ExposeCommand struct {
|
||||
withNameAndCode
|
||||
Ports []string
|
||||
}
|
||||
|
||||
// UserCommand : USER foo
|
||||
//
|
||||
// Set the user to 'foo' for future commands and when running the
|
||||
// ENTRYPOINT/CMD at container run time.
|
||||
//
|
||||
type UserCommand struct {
|
||||
withNameAndCode
|
||||
User string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *UserCommand) Expand(expander SingleWordExpander) error {
|
||||
p, err := expander(c.User)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.User = p
|
||||
return nil
|
||||
}
|
||||
|
||||
// VolumeCommand : VOLUME /foo
|
||||
//
|
||||
// Expose the volume /foo for use. Will also accept the JSON array form.
|
||||
//
|
||||
type VolumeCommand struct {
|
||||
withNameAndCode
|
||||
Volumes []string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *VolumeCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandSliceInPlace(c.Volumes, expander)
|
||||
}
|
||||
|
||||
// StopSignalCommand : STOPSIGNAL signal
|
||||
//
|
||||
// Set the signal that will be used to kill the container.
|
||||
type StopSignalCommand struct {
|
||||
withNameAndCode
|
||||
Signal string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *StopSignalCommand) Expand(expander SingleWordExpander) error {
|
||||
p, err := expander(c.Signal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Signal = p
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckPlatform checks that the command is supported in the target platform
|
||||
func (c *StopSignalCommand) CheckPlatform(platform string) error {
|
||||
if platform == "windows" {
|
||||
return errors.New("The daemon on this platform does not support the command stopsignal")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ArgCommand : ARG name[=value]
|
||||
//
|
||||
// Adds the variable foo to the trusted list of variables that can be passed
|
||||
// to builder using the --build-arg flag for expansion/substitution or passing to 'run'.
|
||||
// Dockerfile author may optionally set a default value of this variable.
|
||||
type ArgCommand struct {
|
||||
withNameAndCode
|
||||
Key string
|
||||
Value *string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *ArgCommand) Expand(expander SingleWordExpander) error {
|
||||
p, err := expander(c.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Key = p
|
||||
if c.Value != nil {
|
||||
p, err = expander(*c.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Value = &p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShellCommand : SHELL powershell -command
|
||||
//
|
||||
// Set the non-default shell to use.
|
||||
type ShellCommand struct {
|
||||
withNameAndCode
|
||||
Shell strslice.StrSlice
|
||||
}
|
||||
|
||||
// Stage represents a single stage in a multi-stage build
|
||||
type Stage struct {
|
||||
Name string
|
||||
Commands []Command
|
||||
BaseName string
|
||||
SourceCode string
|
||||
}
|
||||
|
||||
// AddCommand to the stage
|
||||
func (s *Stage) AddCommand(cmd Command) {
|
||||
// todo: validate cmd type
|
||||
s.Commands = append(s.Commands, cmd)
|
||||
}
|
||||
|
||||
// IsCurrentStage check if the stage name is the current stage
|
||||
func IsCurrentStage(s []Stage, name string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
return s[len(s)-1].Name == name
|
||||
}
|
||||
|
||||
// CurrentStage return the last stage in a slice
|
||||
func CurrentStage(s []Stage) (*Stage, error) {
|
||||
if len(s) == 0 {
|
||||
return nil, errors.New("No build stage in current context")
|
||||
}
|
||||
return &s[len(s)-1], nil
|
||||
}
|
||||
|
||||
// HasStage looks for the presence of a given stage name
|
||||
func HasStage(s []Stage, name string) (int, bool) {
|
||||
for i, stage := range s {
|
||||
if stage.Name == name {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return -1, false
|
||||
}
|
9
vendor/github.com/docker/docker/builder/dockerfile/instructions/errors_unix.go
generated
vendored
Normal file
9
vendor/github.com/docker/docker/builder/dockerfile/instructions/errors_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
// +build !windows
|
||||
|
||||
package instructions
|
||||
|
||||
import "fmt"
|
||||
|
||||
func errNotJSON(command, _ string) error {
|
||||
return fmt.Errorf("%s requires the arguments to be in JSON form", command)
|
||||
}
|
27
vendor/github.com/docker/docker/builder/dockerfile/instructions/errors_windows.go
generated
vendored
Normal file
27
vendor/github.com/docker/docker/builder/dockerfile/instructions/errors_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
package instructions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func errNotJSON(command, original string) error {
|
||||
// For Windows users, give a hint if it looks like it might contain
|
||||
// a path which hasn't been escaped such as ["c:\windows\system32\prog.exe", "-param"],
|
||||
// as JSON must be escaped. Unfortunate...
|
||||
//
|
||||
// Specifically looking for quote-driveletter-colon-backslash, there's no
|
||||
// double backslash and a [] pair. No, this is not perfect, but it doesn't
|
||||
// have to be. It's simply a hint to make life a little easier.
|
||||
extra := ""
|
||||
original = filepath.FromSlash(strings.ToLower(strings.Replace(strings.ToLower(original), strings.ToLower(command)+" ", "", -1)))
|
||||
if len(regexp.MustCompile(`"[a-z]:\\.*`).FindStringSubmatch(original)) > 0 &&
|
||||
!strings.Contains(original, `\\`) &&
|
||||
strings.Contains(original, "[") &&
|
||||
strings.Contains(original, "]") {
|
||||
extra = fmt.Sprintf(`. It looks like '%s' includes a file path without an escaped back-slash. JSON requires back-slashes to be escaped such as ["c:\\path\\to\\file.exe", "/parameter"]`, original)
|
||||
}
|
||||
return fmt.Errorf("%s requires the arguments to be in JSON form%s", command, extra)
|
||||
}
|
635
vendor/github.com/docker/docker/builder/dockerfile/instructions/parse.go
generated
vendored
Normal file
635
vendor/github.com/docker/docker/builder/dockerfile/instructions/parse.go
generated
vendored
Normal file
|
@ -0,0 +1,635 @@
|
|||
package instructions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
"github.com/docker/docker/builder/dockerfile/command"
|
||||
"github.com/docker/docker/builder/dockerfile/parser"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type parseRequest struct {
|
||||
command string
|
||||
args []string
|
||||
attributes map[string]bool
|
||||
flags *BFlags
|
||||
original string
|
||||
}
|
||||
|
||||
func nodeArgs(node *parser.Node) []string {
|
||||
result := []string{}
|
||||
for ; node.Next != nil; node = node.Next {
|
||||
arg := node.Next
|
||||
if len(arg.Children) == 0 {
|
||||
result = append(result, arg.Value)
|
||||
} else if len(arg.Children) == 1 {
|
||||
//sub command
|
||||
result = append(result, arg.Children[0].Value)
|
||||
result = append(result, nodeArgs(arg.Children[0])...)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func newParseRequestFromNode(node *parser.Node) parseRequest {
|
||||
return parseRequest{
|
||||
command: node.Value,
|
||||
args: nodeArgs(node),
|
||||
attributes: node.Attributes,
|
||||
original: node.Original,
|
||||
flags: NewBFlagsWithArgs(node.Flags),
|
||||
}
|
||||
}
|
||||
|
||||
// ParseInstruction converts an AST to a typed instruction (either a command or a build stage beginning when encountering a `FROM` statement)
|
||||
func ParseInstruction(node *parser.Node) (interface{}, error) {
|
||||
req := newParseRequestFromNode(node)
|
||||
switch node.Value {
|
||||
case command.Env:
|
||||
return parseEnv(req)
|
||||
case command.Maintainer:
|
||||
return parseMaintainer(req)
|
||||
case command.Label:
|
||||
return parseLabel(req)
|
||||
case command.Add:
|
||||
return parseAdd(req)
|
||||
case command.Copy:
|
||||
return parseCopy(req)
|
||||
case command.From:
|
||||
return parseFrom(req)
|
||||
case command.Onbuild:
|
||||
return parseOnBuild(req)
|
||||
case command.Workdir:
|
||||
return parseWorkdir(req)
|
||||
case command.Run:
|
||||
return parseRun(req)
|
||||
case command.Cmd:
|
||||
return parseCmd(req)
|
||||
case command.Healthcheck:
|
||||
return parseHealthcheck(req)
|
||||
case command.Entrypoint:
|
||||
return parseEntrypoint(req)
|
||||
case command.Expose:
|
||||
return parseExpose(req)
|
||||
case command.User:
|
||||
return parseUser(req)
|
||||
case command.Volume:
|
||||
return parseVolume(req)
|
||||
case command.StopSignal:
|
||||
return parseStopSignal(req)
|
||||
case command.Arg:
|
||||
return parseArg(req)
|
||||
case command.Shell:
|
||||
return parseShell(req)
|
||||
}
|
||||
|
||||
return nil, &UnknownInstruction{Instruction: node.Value, Line: node.StartLine}
|
||||
}
|
||||
|
||||
// ParseCommand converts an AST to a typed Command
|
||||
func ParseCommand(node *parser.Node) (Command, error) {
|
||||
s, err := ParseInstruction(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c, ok := s.(Command); ok {
|
||||
return c, nil
|
||||
}
|
||||
return nil, errors.Errorf("%T is not a command type", s)
|
||||
}
|
||||
|
||||
// UnknownInstruction represents an error occuring when a command is unresolvable
|
||||
type UnknownInstruction struct {
|
||||
Line int
|
||||
Instruction string
|
||||
}
|
||||
|
||||
func (e *UnknownInstruction) Error() string {
|
||||
return fmt.Sprintf("unknown instruction: %s", strings.ToUpper(e.Instruction))
|
||||
}
|
||||
|
||||
// IsUnknownInstruction checks if the error is an UnknownInstruction or a parseError containing an UnknownInstruction
|
||||
func IsUnknownInstruction(err error) bool {
|
||||
_, ok := err.(*UnknownInstruction)
|
||||
if !ok {
|
||||
var pe *parseError
|
||||
if pe, ok = err.(*parseError); ok {
|
||||
_, ok = pe.inner.(*UnknownInstruction)
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
type parseError struct {
|
||||
inner error
|
||||
node *parser.Node
|
||||
}
|
||||
|
||||
func (e *parseError) Error() string {
|
||||
return fmt.Sprintf("Dockerfile parse error line %d: %v", e.node.StartLine, e.inner.Error())
|
||||
}
|
||||
|
||||
// Parse a docker file into a collection of buildable stages
|
||||
func Parse(ast *parser.Node) (stages []Stage, metaArgs []ArgCommand, err error) {
|
||||
for _, n := range ast.Children {
|
||||
cmd, err := ParseInstruction(n)
|
||||
if err != nil {
|
||||
return nil, nil, &parseError{inner: err, node: n}
|
||||
}
|
||||
if len(stages) == 0 {
|
||||
// meta arg case
|
||||
if a, isArg := cmd.(*ArgCommand); isArg {
|
||||
metaArgs = append(metaArgs, *a)
|
||||
continue
|
||||
}
|
||||
}
|
||||
switch c := cmd.(type) {
|
||||
case *Stage:
|
||||
stages = append(stages, *c)
|
||||
case Command:
|
||||
stage, err := CurrentStage(stages)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
stage.AddCommand(c)
|
||||
default:
|
||||
return nil, nil, errors.Errorf("%T is not a command type", cmd)
|
||||
}
|
||||
|
||||
}
|
||||
return stages, metaArgs, nil
|
||||
}
|
||||
|
||||
func parseKvps(args []string, cmdName string) (KeyValuePairs, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, errAtLeastOneArgument(cmdName)
|
||||
}
|
||||
if len(args)%2 != 0 {
|
||||
// should never get here, but just in case
|
||||
return nil, errTooManyArguments(cmdName)
|
||||
}
|
||||
var res KeyValuePairs
|
||||
for j := 0; j < len(args); j += 2 {
|
||||
if len(args[j]) == 0 {
|
||||
return nil, errBlankCommandNames(cmdName)
|
||||
}
|
||||
name := args[j]
|
||||
value := args[j+1]
|
||||
res = append(res, KeyValuePair{Key: name, Value: value})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func parseEnv(req parseRequest) (*EnvCommand, error) {
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
envs, err := parseKvps(req.args, "ENV")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EnvCommand{
|
||||
Env: envs,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseMaintainer(req parseRequest) (*MaintainerCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("MAINTAINER")
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &MaintainerCommand{
|
||||
Maintainer: req.args[0],
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseLabel(req parseRequest) (*LabelCommand, error) {
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels, err := parseKvps(req.args, "LABEL")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &LabelCommand{
|
||||
Labels: labels,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseAdd(req parseRequest) (*AddCommand, error) {
|
||||
if len(req.args) < 2 {
|
||||
return nil, errAtLeastTwoArguments("ADD")
|
||||
}
|
||||
flChown := req.flags.AddString("chown", "")
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &AddCommand{
|
||||
SourcesAndDest: SourcesAndDest(req.args),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
Chown: flChown.Value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseCopy(req parseRequest) (*CopyCommand, error) {
|
||||
if len(req.args) < 2 {
|
||||
return nil, errAtLeastTwoArguments("COPY")
|
||||
}
|
||||
flChown := req.flags.AddString("chown", "")
|
||||
flFrom := req.flags.AddString("from", "")
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CopyCommand{
|
||||
SourcesAndDest: SourcesAndDest(req.args),
|
||||
From: flFrom.Value,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
Chown: flChown.Value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseFrom(req parseRequest) (*Stage, error) {
|
||||
stageName, err := parseBuildStageName(req.args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
code := strings.TrimSpace(req.original)
|
||||
|
||||
return &Stage{
|
||||
BaseName: req.args[0],
|
||||
Name: stageName,
|
||||
SourceCode: code,
|
||||
Commands: []Command{},
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseBuildStageName(args []string) (string, error) {
|
||||
stageName := ""
|
||||
switch {
|
||||
case len(args) == 3 && strings.EqualFold(args[1], "as"):
|
||||
stageName = strings.ToLower(args[2])
|
||||
if ok, _ := regexp.MatchString("^[a-z][a-z0-9-_\\.]*$", stageName); !ok {
|
||||
return "", errors.Errorf("invalid name for build stage: %q, name can't start with a number or contain symbols", stageName)
|
||||
}
|
||||
case len(args) != 1:
|
||||
return "", errors.New("FROM requires either one or three arguments")
|
||||
}
|
||||
|
||||
return stageName, nil
|
||||
}
|
||||
|
||||
func parseOnBuild(req parseRequest) (*OnbuildCommand, error) {
|
||||
if len(req.args) == 0 {
|
||||
return nil, errAtLeastOneArgument("ONBUILD")
|
||||
}
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
triggerInstruction := strings.ToUpper(strings.TrimSpace(req.args[0]))
|
||||
switch strings.ToUpper(triggerInstruction) {
|
||||
case "ONBUILD":
|
||||
return nil, errors.New("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
|
||||
case "MAINTAINER", "FROM":
|
||||
return nil, fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction)
|
||||
}
|
||||
|
||||
original := regexp.MustCompile(`(?i)^\s*ONBUILD\s*`).ReplaceAllString(req.original, "")
|
||||
return &OnbuildCommand{
|
||||
Expression: original,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseWorkdir(req parseRequest) (*WorkdirCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("WORKDIR")
|
||||
}
|
||||
|
||||
err := req.flags.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &WorkdirCommand{
|
||||
Path: req.args[0],
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseShellDependentCommand(req parseRequest, emptyAsNil bool) ShellDependantCmdLine {
|
||||
args := handleJSONArgs(req.args, req.attributes)
|
||||
cmd := strslice.StrSlice(args)
|
||||
if emptyAsNil && len(cmd) == 0 {
|
||||
cmd = nil
|
||||
}
|
||||
return ShellDependantCmdLine{
|
||||
CmdLine: cmd,
|
||||
PrependShell: !req.attributes["json"],
|
||||
}
|
||||
}
|
||||
|
||||
func parseRun(req parseRequest) (*RunCommand, error) {
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &RunCommand{
|
||||
ShellDependantCmdLine: parseShellDependentCommand(req, false),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseCmd(req parseRequest) (*CmdCommand, error) {
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CmdCommand{
|
||||
ShellDependantCmdLine: parseShellDependentCommand(req, false),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseEntrypoint(req parseRequest) (*EntrypointCommand, error) {
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := &EntrypointCommand{
|
||||
ShellDependantCmdLine: parseShellDependentCommand(req, true),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
// parseOptInterval(flag) is the duration of flag.Value, or 0 if
|
||||
// empty. An error is reported if the value is given and less than minimum duration.
|
||||
func parseOptInterval(f *Flag) (time.Duration, error) {
|
||||
s := f.Value
|
||||
if s == "" {
|
||||
return 0, nil
|
||||
}
|
||||
d, err := time.ParseDuration(s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if d < time.Duration(container.MinimumDuration) {
|
||||
return 0, fmt.Errorf("Interval %#v cannot be less than %s", f.name, container.MinimumDuration)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
func parseHealthcheck(req parseRequest) (*HealthCheckCommand, error) {
|
||||
if len(req.args) == 0 {
|
||||
return nil, errAtLeastOneArgument("HEALTHCHECK")
|
||||
}
|
||||
cmd := &HealthCheckCommand{
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}
|
||||
|
||||
typ := strings.ToUpper(req.args[0])
|
||||
args := req.args[1:]
|
||||
if typ == "NONE" {
|
||||
if len(args) != 0 {
|
||||
return nil, errors.New("HEALTHCHECK NONE takes no arguments")
|
||||
}
|
||||
test := strslice.StrSlice{typ}
|
||||
cmd.Health = &container.HealthConfig{
|
||||
Test: test,
|
||||
}
|
||||
} else {
|
||||
|
||||
healthcheck := container.HealthConfig{}
|
||||
|
||||
flInterval := req.flags.AddString("interval", "")
|
||||
flTimeout := req.flags.AddString("timeout", "")
|
||||
flStartPeriod := req.flags.AddString("start-period", "")
|
||||
flRetries := req.flags.AddString("retries", "")
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch typ {
|
||||
case "CMD":
|
||||
cmdSlice := handleJSONArgs(args, req.attributes)
|
||||
if len(cmdSlice) == 0 {
|
||||
return nil, errors.New("Missing command after HEALTHCHECK CMD")
|
||||
}
|
||||
|
||||
if !req.attributes["json"] {
|
||||
typ = "CMD-SHELL"
|
||||
}
|
||||
|
||||
healthcheck.Test = strslice.StrSlice(append([]string{typ}, cmdSlice...))
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown type %#v in HEALTHCHECK (try CMD)", typ)
|
||||
}
|
||||
|
||||
interval, err := parseOptInterval(flInterval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
healthcheck.Interval = interval
|
||||
|
||||
timeout, err := parseOptInterval(flTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
healthcheck.Timeout = timeout
|
||||
|
||||
startPeriod, err := parseOptInterval(flStartPeriod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
healthcheck.StartPeriod = startPeriod
|
||||
|
||||
if flRetries.Value != "" {
|
||||
retries, err := strconv.ParseInt(flRetries.Value, 10, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if retries < 1 {
|
||||
return nil, fmt.Errorf("--retries must be at least 1 (not %d)", retries)
|
||||
}
|
||||
healthcheck.Retries = int(retries)
|
||||
} else {
|
||||
healthcheck.Retries = 0
|
||||
}
|
||||
|
||||
cmd.Health = &healthcheck
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func parseExpose(req parseRequest) (*ExposeCommand, error) {
|
||||
portsTab := req.args
|
||||
|
||||
if len(req.args) == 0 {
|
||||
return nil, errAtLeastOneArgument("EXPOSE")
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Strings(portsTab)
|
||||
return &ExposeCommand{
|
||||
Ports: portsTab,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseUser(req parseRequest) (*UserCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("USER")
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UserCommand{
|
||||
User: req.args[0],
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseVolume(req parseRequest) (*VolumeCommand, error) {
|
||||
if len(req.args) == 0 {
|
||||
return nil, errAtLeastOneArgument("VOLUME")
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := &VolumeCommand{
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}
|
||||
|
||||
for _, v := range req.args {
|
||||
v = strings.TrimSpace(v)
|
||||
if v == "" {
|
||||
return nil, errors.New("VOLUME specified can not be an empty string")
|
||||
}
|
||||
cmd.Volumes = append(cmd.Volumes, v)
|
||||
}
|
||||
return cmd, nil
|
||||
|
||||
}
|
||||
|
||||
func parseStopSignal(req parseRequest) (*StopSignalCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("STOPSIGNAL")
|
||||
}
|
||||
sig := req.args[0]
|
||||
|
||||
cmd := &StopSignalCommand{
|
||||
Signal: sig,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}
|
||||
return cmd, nil
|
||||
|
||||
}
|
||||
|
||||
func parseArg(req parseRequest) (*ArgCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("ARG")
|
||||
}
|
||||
|
||||
var (
|
||||
name string
|
||||
newValue *string
|
||||
)
|
||||
|
||||
arg := req.args[0]
|
||||
// 'arg' can just be a name or name-value pair. Note that this is different
|
||||
// from 'env' that handles the split of name and value at the parser level.
|
||||
// The reason for doing it differently for 'arg' is that we support just
|
||||
// defining an arg and not assign it a value (while 'env' always expects a
|
||||
// name-value pair). If possible, it will be good to harmonize the two.
|
||||
if strings.Contains(arg, "=") {
|
||||
parts := strings.SplitN(arg, "=", 2)
|
||||
if len(parts[0]) == 0 {
|
||||
return nil, errBlankCommandNames("ARG")
|
||||
}
|
||||
|
||||
name = parts[0]
|
||||
newValue = &parts[1]
|
||||
} else {
|
||||
name = arg
|
||||
}
|
||||
|
||||
return &ArgCommand{
|
||||
Key: name,
|
||||
Value: newValue,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseShell(req parseRequest) (*ShellCommand, error) {
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
shellSlice := handleJSONArgs(req.args, req.attributes)
|
||||
switch {
|
||||
case len(shellSlice) == 0:
|
||||
// SHELL []
|
||||
return nil, errAtLeastOneArgument("SHELL")
|
||||
case req.attributes["json"]:
|
||||
// SHELL ["powershell", "-command"]
|
||||
|
||||
return &ShellCommand{
|
||||
Shell: strslice.StrSlice(shellSlice),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
default:
|
||||
// SHELL powershell -command - not JSON
|
||||
return nil, errNotJSON("SHELL", req.original)
|
||||
}
|
||||
}
|
||||
|
||||
func errAtLeastOneArgument(command string) error {
|
||||
return errors.Errorf("%s requires at least one argument", command)
|
||||
}
|
||||
|
||||
func errExactlyOneArgument(command string) error {
|
||||
return errors.Errorf("%s requires exactly one argument", command)
|
||||
}
|
||||
|
||||
func errAtLeastTwoArguments(command string) error {
|
||||
return errors.Errorf("%s requires at least two arguments", command)
|
||||
}
|
||||
|
||||
func errBlankCommandNames(command string) error {
|
||||
return errors.Errorf("%s names can not be blank", command)
|
||||
}
|
||||
|
||||
func errTooManyArguments(command string) error {
|
||||
return errors.Errorf("Bad input to %s, too many arguments", command)
|
||||
}
|
19
vendor/github.com/docker/docker/builder/dockerfile/instructions/support.go
generated
vendored
Normal file
19
vendor/github.com/docker/docker/builder/dockerfile/instructions/support.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
package instructions
|
||||
|
||||
import "strings"
|
||||
|
||||
// handleJSONArgs parses command passed to CMD, ENTRYPOINT, RUN and SHELL instruction in Dockerfile
|
||||
// for exec form it returns untouched args slice
|
||||
// for shell form it returns concatenated args as the first element of a slice
|
||||
func handleJSONArgs(args []string, attributes map[string]bool) []string {
|
||||
if len(args) == 0 {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
if attributes != nil && attributes["json"] {
|
||||
return args
|
||||
}
|
||||
|
||||
// literal string command, not an exec array
|
||||
return []string{strings.Join(args, " ")}
|
||||
}
|
399
vendor/github.com/docker/docker/builder/dockerfile/parser/line_parsers.go
generated
vendored
Normal file
399
vendor/github.com/docker/docker/builder/dockerfile/parser/line_parsers.go
generated
vendored
Normal file
|
@ -0,0 +1,399 @@
|
|||
package parser
|
||||
|
||||
// line parsers are dispatch calls that parse a single unit of text into a
|
||||
// Node object which contains the whole statement. Dockerfiles have varied
|
||||
// (but not usually unique, see ONBUILD for a unique example) parsing rules
|
||||
// per-command, and these unify the processing in a way that makes it
|
||||
// manageable.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/docker/docker/builder/dockerfile/command"
|
||||
)
|
||||
|
||||
var (
|
||||
errDockerfileNotStringArray = errors.New("when using JSON array syntax, arrays must be comprised of strings only")
|
||||
)
|
||||
|
||||
const (
|
||||
commandLabel = "LABEL"
|
||||
)
|
||||
|
||||
// ignore the current argument. This will still leave a command parsed, but
|
||||
// will not incorporate the arguments into the ast.
|
||||
func parseIgnore(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
return &Node{}, nil, nil
|
||||
}
|
||||
|
||||
// used for onbuild. Could potentially be used for anything that represents a
|
||||
// statement with sub-statements.
|
||||
//
|
||||
// ONBUILD RUN foo bar -> (onbuild (run foo bar))
|
||||
//
|
||||
func parseSubCommand(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
if rest == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
child, err := newNodeFromLine(rest, d)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &Node{Children: []*Node{child}}, nil, nil
|
||||
}
|
||||
|
||||
// helper to parse words (i.e space delimited or quoted strings) in a statement.
|
||||
// The quotes are preserved as part of this function and they are stripped later
|
||||
// as part of processWords().
|
||||
func parseWords(rest string, d *Directive) []string {
|
||||
const (
|
||||
inSpaces = iota // looking for start of a word
|
||||
inWord
|
||||
inQuote
|
||||
)
|
||||
|
||||
words := []string{}
|
||||
phase := inSpaces
|
||||
word := ""
|
||||
quote := '\000'
|
||||
blankOK := false
|
||||
var ch rune
|
||||
var chWidth int
|
||||
|
||||
for pos := 0; pos <= len(rest); pos += chWidth {
|
||||
if pos != len(rest) {
|
||||
ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
|
||||
}
|
||||
|
||||
if phase == inSpaces { // Looking for start of word
|
||||
if pos == len(rest) { // end of input
|
||||
break
|
||||
}
|
||||
if unicode.IsSpace(ch) { // skip spaces
|
||||
continue
|
||||
}
|
||||
phase = inWord // found it, fall through
|
||||
}
|
||||
if (phase == inWord || phase == inQuote) && (pos == len(rest)) {
|
||||
if blankOK || len(word) > 0 {
|
||||
words = append(words, word)
|
||||
}
|
||||
break
|
||||
}
|
||||
if phase == inWord {
|
||||
if unicode.IsSpace(ch) {
|
||||
phase = inSpaces
|
||||
if blankOK || len(word) > 0 {
|
||||
words = append(words, word)
|
||||
}
|
||||
word = ""
|
||||
blankOK = false
|
||||
continue
|
||||
}
|
||||
if ch == '\'' || ch == '"' {
|
||||
quote = ch
|
||||
blankOK = true
|
||||
phase = inQuote
|
||||
}
|
||||
if ch == d.escapeToken {
|
||||
if pos+chWidth == len(rest) {
|
||||
continue // just skip an escape token at end of line
|
||||
}
|
||||
// If we're not quoted and we see an escape token, then always just
|
||||
// add the escape token plus the char to the word, even if the char
|
||||
// is a quote.
|
||||
word += string(ch)
|
||||
pos += chWidth
|
||||
ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
|
||||
}
|
||||
word += string(ch)
|
||||
continue
|
||||
}
|
||||
if phase == inQuote {
|
||||
if ch == quote {
|
||||
phase = inWord
|
||||
}
|
||||
// The escape token is special except for ' quotes - can't escape anything for '
|
||||
if ch == d.escapeToken && quote != '\'' {
|
||||
if pos+chWidth == len(rest) {
|
||||
phase = inWord
|
||||
continue // just skip the escape token at end
|
||||
}
|
||||
pos += chWidth
|
||||
word += string(ch)
|
||||
ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
|
||||
}
|
||||
word += string(ch)
|
||||
}
|
||||
}
|
||||
|
||||
return words
|
||||
}
|
||||
|
||||
// parse environment like statements. Note that this does *not* handle
|
||||
// variable interpolation, which will be handled in the evaluator.
|
||||
func parseNameVal(rest string, key string, d *Directive) (*Node, error) {
|
||||
// This is kind of tricky because we need to support the old
|
||||
// variant: KEY name value
|
||||
// as well as the new one: KEY name=value ...
|
||||
// The trigger to know which one is being used will be whether we hit
|
||||
// a space or = first. space ==> old, "=" ==> new
|
||||
|
||||
words := parseWords(rest, d)
|
||||
if len(words) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Old format (KEY name value)
|
||||
if !strings.Contains(words[0], "=") {
|
||||
parts := tokenWhitespace.Split(rest, 2)
|
||||
if len(parts) < 2 {
|
||||
return nil, fmt.Errorf(key + " must have two arguments")
|
||||
}
|
||||
return newKeyValueNode(parts[0], parts[1]), nil
|
||||
}
|
||||
|
||||
var rootNode *Node
|
||||
var prevNode *Node
|
||||
for _, word := range words {
|
||||
if !strings.Contains(word, "=") {
|
||||
return nil, fmt.Errorf("Syntax error - can't find = in %q. Must be of the form: name=value", word)
|
||||
}
|
||||
|
||||
parts := strings.SplitN(word, "=", 2)
|
||||
node := newKeyValueNode(parts[0], parts[1])
|
||||
rootNode, prevNode = appendKeyValueNode(node, rootNode, prevNode)
|
||||
}
|
||||
|
||||
return rootNode, nil
|
||||
}
|
||||
|
||||
func newKeyValueNode(key, value string) *Node {
|
||||
return &Node{
|
||||
Value: key,
|
||||
Next: &Node{Value: value},
|
||||
}
|
||||
}
|
||||
|
||||
func appendKeyValueNode(node, rootNode, prevNode *Node) (*Node, *Node) {
|
||||
if rootNode == nil {
|
||||
rootNode = node
|
||||
}
|
||||
if prevNode != nil {
|
||||
prevNode.Next = node
|
||||
}
|
||||
|
||||
prevNode = node.Next
|
||||
return rootNode, prevNode
|
||||
}
|
||||
|
||||
func parseEnv(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
node, err := parseNameVal(rest, "ENV", d)
|
||||
return node, nil, err
|
||||
}
|
||||
|
||||
func parseLabel(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
node, err := parseNameVal(rest, commandLabel, d)
|
||||
return node, nil, err
|
||||
}
|
||||
|
||||
// NodeFromLabels returns a Node for the injected labels
|
||||
func NodeFromLabels(labels map[string]string) *Node {
|
||||
keys := []string{}
|
||||
for key := range labels {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
// Sort the label to have a repeatable order
|
||||
sort.Strings(keys)
|
||||
|
||||
labelPairs := []string{}
|
||||
var rootNode *Node
|
||||
var prevNode *Node
|
||||
for _, key := range keys {
|
||||
value := labels[key]
|
||||
labelPairs = append(labelPairs, fmt.Sprintf("%q='%s'", key, value))
|
||||
// Value must be single quoted to prevent env variable expansion
|
||||
// See https://github.com/docker/docker/issues/26027
|
||||
node := newKeyValueNode(key, "'"+value+"'")
|
||||
rootNode, prevNode = appendKeyValueNode(node, rootNode, prevNode)
|
||||
}
|
||||
|
||||
return &Node{
|
||||
Value: command.Label,
|
||||
Original: commandLabel + " " + strings.Join(labelPairs, " "),
|
||||
Next: rootNode,
|
||||
}
|
||||
}
|
||||
|
||||
// parses a statement containing one or more keyword definition(s) and/or
|
||||
// value assignments, like `name1 name2= name3="" name4=value`.
|
||||
// Note that this is a stricter format than the old format of assignment,
|
||||
// allowed by parseNameVal(), in a way that this only allows assignment of the
|
||||
// form `keyword=[<value>]` like `name2=`, `name3=""`, and `name4=value` above.
|
||||
// In addition, a keyword definition alone is of the form `keyword` like `name1`
|
||||
// above. And the assignments `name2=` and `name3=""` are equivalent and
|
||||
// assign an empty value to the respective keywords.
|
||||
func parseNameOrNameVal(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
words := parseWords(rest, d)
|
||||
if len(words) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
var (
|
||||
rootnode *Node
|
||||
prevNode *Node
|
||||
)
|
||||
for i, word := range words {
|
||||
node := &Node{}
|
||||
node.Value = word
|
||||
if i == 0 {
|
||||
rootnode = node
|
||||
} else {
|
||||
prevNode.Next = node
|
||||
}
|
||||
prevNode = node
|
||||
}
|
||||
|
||||
return rootnode, nil, nil
|
||||
}
|
||||
|
||||
// parses a whitespace-delimited set of arguments. The result is effectively a
|
||||
// linked list of string arguments.
|
||||
func parseStringsWhitespaceDelimited(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
if rest == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
node := &Node{}
|
||||
rootnode := node
|
||||
prevnode := node
|
||||
for _, str := range tokenWhitespace.Split(rest, -1) { // use regexp
|
||||
prevnode = node
|
||||
node.Value = str
|
||||
node.Next = &Node{}
|
||||
node = node.Next
|
||||
}
|
||||
|
||||
// XXX to get around regexp.Split *always* providing an empty string at the
|
||||
// end due to how our loop is constructed, nil out the last node in the
|
||||
// chain.
|
||||
prevnode.Next = nil
|
||||
|
||||
return rootnode, nil, nil
|
||||
}
|
||||
|
||||
// parseString just wraps the string in quotes and returns a working node.
|
||||
func parseString(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
if rest == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
n := &Node{}
|
||||
n.Value = rest
|
||||
return n, nil, nil
|
||||
}
|
||||
|
||||
// parseJSON converts JSON arrays to an AST.
|
||||
func parseJSON(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
||||
if !strings.HasPrefix(rest, "[") {
|
||||
return nil, nil, fmt.Errorf(`Error parsing "%s" as a JSON array`, rest)
|
||||
}
|
||||
|
||||
var myJSON []interface{}
|
||||
if err := json.NewDecoder(strings.NewReader(rest)).Decode(&myJSON); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var top, prev *Node
|
||||
for _, str := range myJSON {
|
||||
s, ok := str.(string)
|
||||
if !ok {
|
||||
return nil, nil, errDockerfileNotStringArray
|
||||
}
|
||||
|
||||
node := &Node{Value: s}
|
||||
if prev == nil {
|
||||
top = node
|
||||
} else {
|
||||
prev.Next = node
|
||||
}
|
||||
prev = node
|
||||
}
|
||||
|
||||
return top, map[string]bool{"json": true}, nil
|
||||
}
|
||||
|
||||
// parseMaybeJSON determines if the argument appears to be a JSON array. If
|
||||
// so, passes to parseJSON; if not, quotes the result and returns a single
|
||||
// node.
|
||||
func parseMaybeJSON(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
if rest == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
node, attrs, err := parseJSON(rest, d)
|
||||
|
||||
if err == nil {
|
||||
return node, attrs, nil
|
||||
}
|
||||
if err == errDockerfileNotStringArray {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
node = &Node{}
|
||||
node.Value = rest
|
||||
return node, nil, nil
|
||||
}
|
||||
|
||||
// parseMaybeJSONToList determines if the argument appears to be a JSON array. If
|
||||
// so, passes to parseJSON; if not, attempts to parse it as a whitespace
|
||||
// delimited string.
|
||||
func parseMaybeJSONToList(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
node, attrs, err := parseJSON(rest, d)
|
||||
|
||||
if err == nil {
|
||||
return node, attrs, nil
|
||||
}
|
||||
if err == errDockerfileNotStringArray {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return parseStringsWhitespaceDelimited(rest, d)
|
||||
}
|
||||
|
||||
// The HEALTHCHECK command is like parseMaybeJSON, but has an extra type argument.
|
||||
func parseHealthConfig(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
// Find end of first argument
|
||||
var sep int
|
||||
for ; sep < len(rest); sep++ {
|
||||
if unicode.IsSpace(rune(rest[sep])) {
|
||||
break
|
||||
}
|
||||
}
|
||||
next := sep
|
||||
for ; next < len(rest); next++ {
|
||||
if !unicode.IsSpace(rune(rest[next])) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if sep == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
typ := rest[:sep]
|
||||
cmd, attrs, err := parseMaybeJSON(rest[next:], d)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &Node{Value: typ, Next: cmd}, attrs, err
|
||||
}
|
355
vendor/github.com/docker/docker/builder/dockerfile/parser/parser.go
generated
vendored
Normal file
355
vendor/github.com/docker/docker/builder/dockerfile/parser/parser.go
generated
vendored
Normal file
|
@ -0,0 +1,355 @@
|
|||
// Package parser implements a parser and parse tree dumper for Dockerfiles.
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/docker/docker/builder/dockerfile/command"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Node is a structure used to represent a parse tree.
|
||||
//
|
||||
// In the node there are three fields, Value, Next, and Children. Value is the
|
||||
// current token's string value. Next is always the next non-child token, and
|
||||
// children contains all the children. Here's an example:
|
||||
//
|
||||
// (value next (child child-next child-next-next) next-next)
|
||||
//
|
||||
// This data structure is frankly pretty lousy for handling complex languages,
|
||||
// but lucky for us the Dockerfile isn't very complicated. This structure
|
||||
// works a little more effectively than a "proper" parse tree for our needs.
|
||||
//
|
||||
type Node struct {
|
||||
Value string // actual content
|
||||
Next *Node // the next item in the current sexp
|
||||
Children []*Node // the children of this sexp
|
||||
Attributes map[string]bool // special attributes for this node
|
||||
Original string // original line used before parsing
|
||||
Flags []string // only top Node should have this set
|
||||
StartLine int // the line in the original dockerfile where the node begins
|
||||
endLine int // the line in the original dockerfile where the node ends
|
||||
}
|
||||
|
||||
// Dump dumps the AST defined by `node` as a list of sexps.
|
||||
// Returns a string suitable for printing.
|
||||
func (node *Node) Dump() string {
|
||||
str := ""
|
||||
str += node.Value
|
||||
|
||||
if len(node.Flags) > 0 {
|
||||
str += fmt.Sprintf(" %q", node.Flags)
|
||||
}
|
||||
|
||||
for _, n := range node.Children {
|
||||
str += "(" + n.Dump() + ")\n"
|
||||
}
|
||||
|
||||
for n := node.Next; n != nil; n = n.Next {
|
||||
if len(n.Children) > 0 {
|
||||
str += " " + n.Dump()
|
||||
} else {
|
||||
str += " " + strconv.Quote(n.Value)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(str)
|
||||
}
|
||||
|
||||
func (node *Node) lines(start, end int) {
|
||||
node.StartLine = start
|
||||
node.endLine = end
|
||||
}
|
||||
|
||||
// AddChild adds a new child node, and updates line information
|
||||
func (node *Node) AddChild(child *Node, startLine, endLine int) {
|
||||
child.lines(startLine, endLine)
|
||||
if node.StartLine < 0 {
|
||||
node.StartLine = startLine
|
||||
}
|
||||
node.endLine = endLine
|
||||
node.Children = append(node.Children, child)
|
||||
}
|
||||
|
||||
var (
|
||||
dispatch map[string]func(string, *Directive) (*Node, map[string]bool, error)
|
||||
tokenWhitespace = regexp.MustCompile(`[\t\v\f\r ]+`)
|
||||
tokenEscapeCommand = regexp.MustCompile(`^#[ \t]*escape[ \t]*=[ \t]*(?P<escapechar>.).*$`)
|
||||
tokenPlatformCommand = regexp.MustCompile(`^#[ \t]*platform[ \t]*=[ \t]*(?P<platform>.*)$`)
|
||||
tokenComment = regexp.MustCompile(`^#.*$`)
|
||||
)
|
||||
|
||||
// DefaultEscapeToken is the default escape token
|
||||
const DefaultEscapeToken = '\\'
|
||||
|
||||
// defaultPlatformToken is the platform assumed for the build if not explicitly provided
|
||||
var defaultPlatformToken = runtime.GOOS
|
||||
|
||||
// Directive is the structure used during a build run to hold the state of
|
||||
// parsing directives.
|
||||
type Directive struct {
|
||||
escapeToken rune // Current escape token
|
||||
platformToken string // Current platform token
|
||||
lineContinuationRegex *regexp.Regexp // Current line continuation regex
|
||||
processingComplete bool // Whether we are done looking for directives
|
||||
escapeSeen bool // Whether the escape directive has been seen
|
||||
platformSeen bool // Whether the platform directive has been seen
|
||||
}
|
||||
|
||||
// setEscapeToken sets the default token for escaping characters in a Dockerfile.
|
||||
func (d *Directive) setEscapeToken(s string) error {
|
||||
if s != "`" && s != "\\" {
|
||||
return fmt.Errorf("invalid ESCAPE '%s'. Must be ` or \\", s)
|
||||
}
|
||||
d.escapeToken = rune(s[0])
|
||||
d.lineContinuationRegex = regexp.MustCompile(`\` + s + `[ \t]*$`)
|
||||
return nil
|
||||
}
|
||||
|
||||
// setPlatformToken sets the default platform for pulling images in a Dockerfile.
|
||||
func (d *Directive) setPlatformToken(s string) error {
|
||||
s = strings.ToLower(s)
|
||||
valid := []string{runtime.GOOS}
|
||||
if system.LCOWSupported() {
|
||||
valid = append(valid, "linux")
|
||||
}
|
||||
for _, item := range valid {
|
||||
if s == item {
|
||||
d.platformToken = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("invalid PLATFORM '%s'. Must be one of %v", s, valid)
|
||||
}
|
||||
|
||||
// possibleParserDirective looks for one or more parser directives '# escapeToken=<char>' and
|
||||
// '# platform=<string>'. Parser directives must precede any builder instruction
|
||||
// or other comments, and cannot be repeated.
|
||||
func (d *Directive) possibleParserDirective(line string) error {
|
||||
if d.processingComplete {
|
||||
return nil
|
||||
}
|
||||
|
||||
tecMatch := tokenEscapeCommand.FindStringSubmatch(strings.ToLower(line))
|
||||
if len(tecMatch) != 0 {
|
||||
for i, n := range tokenEscapeCommand.SubexpNames() {
|
||||
if n == "escapechar" {
|
||||
if d.escapeSeen == true {
|
||||
return errors.New("only one escape parser directive can be used")
|
||||
}
|
||||
d.escapeSeen = true
|
||||
return d.setEscapeToken(tecMatch[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO @jhowardmsft LCOW Support: Eventually this check can be removed,
|
||||
// but only recognise a platform token if running in LCOW mode.
|
||||
if system.LCOWSupported() {
|
||||
tpcMatch := tokenPlatformCommand.FindStringSubmatch(strings.ToLower(line))
|
||||
if len(tpcMatch) != 0 {
|
||||
for i, n := range tokenPlatformCommand.SubexpNames() {
|
||||
if n == "platform" {
|
||||
if d.platformSeen == true {
|
||||
return errors.New("only one platform parser directive can be used")
|
||||
}
|
||||
d.platformSeen = true
|
||||
return d.setPlatformToken(tpcMatch[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d.processingComplete = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewDefaultDirective returns a new Directive with the default escapeToken token
|
||||
func NewDefaultDirective() *Directive {
|
||||
directive := Directive{}
|
||||
directive.setEscapeToken(string(DefaultEscapeToken))
|
||||
directive.setPlatformToken(defaultPlatformToken)
|
||||
return &directive
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Dispatch Table. see line_parsers.go for the parse functions.
|
||||
// The command is parsed and mapped to the line parser. The line parser
|
||||
// receives the arguments but not the command, and returns an AST after
|
||||
// reformulating the arguments according to the rules in the parser
|
||||
// functions. Errors are propagated up by Parse() and the resulting AST can
|
||||
// be incorporated directly into the existing AST as a next.
|
||||
dispatch = map[string]func(string, *Directive) (*Node, map[string]bool, error){
|
||||
command.Add: parseMaybeJSONToList,
|
||||
command.Arg: parseNameOrNameVal,
|
||||
command.Cmd: parseMaybeJSON,
|
||||
command.Copy: parseMaybeJSONToList,
|
||||
command.Entrypoint: parseMaybeJSON,
|
||||
command.Env: parseEnv,
|
||||
command.Expose: parseStringsWhitespaceDelimited,
|
||||
command.From: parseStringsWhitespaceDelimited,
|
||||
command.Healthcheck: parseHealthConfig,
|
||||
command.Label: parseLabel,
|
||||
command.Maintainer: parseString,
|
||||
command.Onbuild: parseSubCommand,
|
||||
command.Run: parseMaybeJSON,
|
||||
command.Shell: parseMaybeJSON,
|
||||
command.StopSignal: parseString,
|
||||
command.User: parseString,
|
||||
command.Volume: parseMaybeJSONToList,
|
||||
command.Workdir: parseString,
|
||||
}
|
||||
}
|
||||
|
||||
// newNodeFromLine splits the line into parts, and dispatches to a function
|
||||
// based on the command and command arguments. A Node is created from the
|
||||
// result of the dispatch.
|
||||
func newNodeFromLine(line string, directive *Directive) (*Node, error) {
|
||||
cmd, flags, args, err := splitCommand(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fn := dispatch[cmd]
|
||||
// Ignore invalid Dockerfile instructions
|
||||
if fn == nil {
|
||||
fn = parseIgnore
|
||||
}
|
||||
next, attrs, err := fn(args, directive)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Node{
|
||||
Value: cmd,
|
||||
Original: line,
|
||||
Flags: flags,
|
||||
Next: next,
|
||||
Attributes: attrs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Result is the result of parsing a Dockerfile
|
||||
type Result struct {
|
||||
AST *Node
|
||||
EscapeToken rune
|
||||
Platform string
|
||||
Warnings []string
|
||||
}
|
||||
|
||||
// PrintWarnings to the writer
|
||||
func (r *Result) PrintWarnings(out io.Writer) {
|
||||
if len(r.Warnings) == 0 {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(out, strings.Join(r.Warnings, "\n")+"\n")
|
||||
}
|
||||
|
||||
// Parse reads lines from a Reader, parses the lines into an AST and returns
|
||||
// the AST and escape token
|
||||
func Parse(rwc io.Reader) (*Result, error) {
|
||||
d := NewDefaultDirective()
|
||||
currentLine := 0
|
||||
root := &Node{StartLine: -1}
|
||||
scanner := bufio.NewScanner(rwc)
|
||||
warnings := []string{}
|
||||
|
||||
var err error
|
||||
for scanner.Scan() {
|
||||
bytesRead := scanner.Bytes()
|
||||
if currentLine == 0 {
|
||||
// First line, strip the byte-order-marker if present
|
||||
bytesRead = bytes.TrimPrefix(bytesRead, utf8bom)
|
||||
}
|
||||
bytesRead, err = processLine(d, bytesRead, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentLine++
|
||||
|
||||
startLine := currentLine
|
||||
line, isEndOfLine := trimContinuationCharacter(string(bytesRead), d)
|
||||
if isEndOfLine && line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var hasEmptyContinuationLine bool
|
||||
for !isEndOfLine && scanner.Scan() {
|
||||
bytesRead, err := processLine(d, scanner.Bytes(), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentLine++
|
||||
|
||||
if isEmptyContinuationLine(bytesRead) {
|
||||
hasEmptyContinuationLine = true
|
||||
continue
|
||||
}
|
||||
|
||||
continuationLine := string(bytesRead)
|
||||
continuationLine, isEndOfLine = trimContinuationCharacter(continuationLine, d)
|
||||
line += continuationLine
|
||||
}
|
||||
|
||||
if hasEmptyContinuationLine {
|
||||
warning := "[WARNING]: Empty continuation line found in:\n " + line
|
||||
warnings = append(warnings, warning)
|
||||
}
|
||||
|
||||
child, err := newNodeFromLine(line, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root.AddChild(child, startLine, currentLine)
|
||||
}
|
||||
|
||||
if len(warnings) > 0 {
|
||||
warnings = append(warnings, "[WARNING]: Empty continuation lines will become errors in a future release.")
|
||||
}
|
||||
return &Result{
|
||||
AST: root,
|
||||
Warnings: warnings,
|
||||
EscapeToken: d.escapeToken,
|
||||
Platform: d.platformToken,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func trimComments(src []byte) []byte {
|
||||
return tokenComment.ReplaceAll(src, []byte{})
|
||||
}
|
||||
|
||||
func trimWhitespace(src []byte) []byte {
|
||||
return bytes.TrimLeftFunc(src, unicode.IsSpace)
|
||||
}
|
||||
|
||||
func isEmptyContinuationLine(line []byte) bool {
|
||||
return len(trimComments(trimWhitespace(line))) == 0
|
||||
}
|
||||
|
||||
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
|
||||
|
||||
func trimContinuationCharacter(line string, d *Directive) (string, bool) {
|
||||
if d.lineContinuationRegex.MatchString(line) {
|
||||
line = d.lineContinuationRegex.ReplaceAllString(line, "")
|
||||
return line, false
|
||||
}
|
||||
return line, true
|
||||
}
|
||||
|
||||
// TODO: remove stripLeftWhitespace after deprecation period. It seems silly
|
||||
// to preserve whitespace on continuation lines. Why is that done?
|
||||
func processLine(d *Directive, token []byte, stripLeftWhitespace bool) ([]byte, error) {
|
||||
if stripLeftWhitespace {
|
||||
token = trimWhitespace(token)
|
||||
}
|
||||
return trimComments(token), d.possibleParserDirective(string(token))
|
||||
}
|
118
vendor/github.com/docker/docker/builder/dockerfile/parser/split_command.go
generated
vendored
Normal file
118
vendor/github.com/docker/docker/builder/dockerfile/parser/split_command.go
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// splitCommand takes a single line of text and parses out the cmd and args,
|
||||
// which are used for dispatching to more exact parsing functions.
|
||||
func splitCommand(line string) (string, []string, string, error) {
|
||||
var args string
|
||||
var flags []string
|
||||
|
||||
// Make sure we get the same results irrespective of leading/trailing spaces
|
||||
cmdline := tokenWhitespace.Split(strings.TrimSpace(line), 2)
|
||||
cmd := strings.ToLower(cmdline[0])
|
||||
|
||||
if len(cmdline) == 2 {
|
||||
var err error
|
||||
args, flags, err = extractBuilderFlags(cmdline[1])
|
||||
if err != nil {
|
||||
return "", nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
return cmd, flags, strings.TrimSpace(args), nil
|
||||
}
|
||||
|
||||
func extractBuilderFlags(line string) (string, []string, error) {
|
||||
// Parses the BuilderFlags and returns the remaining part of the line
|
||||
|
||||
const (
|
||||
inSpaces = iota // looking for start of a word
|
||||
inWord
|
||||
inQuote
|
||||
)
|
||||
|
||||
words := []string{}
|
||||
phase := inSpaces
|
||||
word := ""
|
||||
quote := '\000'
|
||||
blankOK := false
|
||||
var ch rune
|
||||
|
||||
for pos := 0; pos <= len(line); pos++ {
|
||||
if pos != len(line) {
|
||||
ch = rune(line[pos])
|
||||
}
|
||||
|
||||
if phase == inSpaces { // Looking for start of word
|
||||
if pos == len(line) { // end of input
|
||||
break
|
||||
}
|
||||
if unicode.IsSpace(ch) { // skip spaces
|
||||
continue
|
||||
}
|
||||
|
||||
// Only keep going if the next word starts with --
|
||||
if ch != '-' || pos+1 == len(line) || rune(line[pos+1]) != '-' {
|
||||
return line[pos:], words, nil
|
||||
}
|
||||
|
||||
phase = inWord // found something with "--", fall through
|
||||
}
|
||||
if (phase == inWord || phase == inQuote) && (pos == len(line)) {
|
||||
if word != "--" && (blankOK || len(word) > 0) {
|
||||
words = append(words, word)
|
||||
}
|
||||
break
|
||||
}
|
||||
if phase == inWord {
|
||||
if unicode.IsSpace(ch) {
|
||||
phase = inSpaces
|
||||
if word == "--" {
|
||||
return line[pos:], words, nil
|
||||
}
|
||||
if blankOK || len(word) > 0 {
|
||||
words = append(words, word)
|
||||
}
|
||||
word = ""
|
||||
blankOK = false
|
||||
continue
|
||||
}
|
||||
if ch == '\'' || ch == '"' {
|
||||
quote = ch
|
||||
blankOK = true
|
||||
phase = inQuote
|
||||
continue
|
||||
}
|
||||
if ch == '\\' {
|
||||
if pos+1 == len(line) {
|
||||
continue // just skip \ at end
|
||||
}
|
||||
pos++
|
||||
ch = rune(line[pos])
|
||||
}
|
||||
word += string(ch)
|
||||
continue
|
||||
}
|
||||
if phase == inQuote {
|
||||
if ch == quote {
|
||||
phase = inWord
|
||||
continue
|
||||
}
|
||||
if ch == '\\' {
|
||||
if pos+1 == len(line) {
|
||||
phase = inWord
|
||||
continue // just skip \ at end
|
||||
}
|
||||
pos++
|
||||
ch = rune(line[pos])
|
||||
}
|
||||
word += string(ch)
|
||||
}
|
||||
}
|
||||
|
||||
return "", words, nil
|
||||
}
|
|
@ -18,14 +18,6 @@ Generates AUTHORS; a file with all the names and corresponding emails of
|
|||
individual contributors. AUTHORS can be found in the home directory of
|
||||
this repository.
|
||||
|
||||
## Install (install.sh)
|
||||
|
||||
Executable install script for installing Docker. If updates to this are
|
||||
desired, please use hack/release.sh during a normal release. The following
|
||||
one-liner may be used for script hotfixes:
|
||||
|
||||
- `aws s3 cp --acl public-read hack/install.sh s3://get.docker.com/index`
|
||||
|
||||
## Make
|
||||
|
||||
There are two make files, each with different extensions. Neither are supposed
|
||||
|
@ -45,14 +37,14 @@ More information is found within `make.ps1` by the author, @jhowardmsft
|
|||
- Referenced via `make test` when running tests on a local machine,
|
||||
or directly referenced when running tests inside a Docker development container.
|
||||
- When running on a local machine, `make test` to run all tests found in
|
||||
`test`, `test-unit`, `test-integration-cli`, and `test-docker-py` on
|
||||
`test`, `test-unit`, `test-integration`, and `test-docker-py` on
|
||||
your local machine. The default timeout is set in `make.sh` to 60 minutes
|
||||
(`${TIMEOUT:=60m}`), since it currently takes up to an hour to run
|
||||
all of the tests.
|
||||
- When running inside a Docker development container, `hack/make.sh` does
|
||||
not have a single target that runs all the tests. You need to provide a
|
||||
single command line with multiple targets that performs the same thing.
|
||||
An example referenced from [Run targets inside a development container](https://docs.docker.com/opensource/project/test-and-docs/#run-targets-inside-a-development-container): `root@5f8630b873fe:/go/src/github.com/moby/moby# hack/make.sh dynbinary binary cross test-unit test-integration-cli test-docker-py`
|
||||
An example referenced from [Run targets inside a development container](https://docs.docker.com/opensource/project/test-and-docs/#run-targets-inside-a-development-container): `root@5f8630b873fe:/go/src/github.com/moby/moby# hack/make.sh dynbinary binary cross test-unit test-integration test-docker-py`
|
||||
- For more information related to testing outside the scope of this README,
|
||||
refer to
|
||||
[Run tests and test documentation](https://docs.docker.com/opensource/project/test-and-docs/)
|
||||
|
|
|
@ -1,87 +1,87 @@
|
|||
package mount
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// RDONLY will mount the file system read-only.
|
||||
RDONLY = syscall.MS_RDONLY
|
||||
RDONLY = unix.MS_RDONLY
|
||||
|
||||
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
|
||||
// take effect.
|
||||
NOSUID = syscall.MS_NOSUID
|
||||
NOSUID = unix.MS_NOSUID
|
||||
|
||||
// NODEV will not interpret character or block special devices on the file
|
||||
// system.
|
||||
NODEV = syscall.MS_NODEV
|
||||
NODEV = unix.MS_NODEV
|
||||
|
||||
// NOEXEC will not allow execution of any binaries on the mounted file system.
|
||||
NOEXEC = syscall.MS_NOEXEC
|
||||
NOEXEC = unix.MS_NOEXEC
|
||||
|
||||
// SYNCHRONOUS will allow I/O to the file system to be done synchronously.
|
||||
SYNCHRONOUS = syscall.MS_SYNCHRONOUS
|
||||
SYNCHRONOUS = unix.MS_SYNCHRONOUS
|
||||
|
||||
// DIRSYNC will force all directory updates within the file system to be done
|
||||
// synchronously. This affects the following system calls: create, link,
|
||||
// unlink, symlink, mkdir, rmdir, mknod and rename.
|
||||
DIRSYNC = syscall.MS_DIRSYNC
|
||||
DIRSYNC = unix.MS_DIRSYNC
|
||||
|
||||
// REMOUNT will attempt to remount an already-mounted file system. This is
|
||||
// commonly used to change the mount flags for a file system, especially to
|
||||
// make a readonly file system writeable. It does not change device or mount
|
||||
// point.
|
||||
REMOUNT = syscall.MS_REMOUNT
|
||||
REMOUNT = unix.MS_REMOUNT
|
||||
|
||||
// MANDLOCK will force mandatory locks on a filesystem.
|
||||
MANDLOCK = syscall.MS_MANDLOCK
|
||||
MANDLOCK = unix.MS_MANDLOCK
|
||||
|
||||
// NOATIME will not update the file access time when reading from a file.
|
||||
NOATIME = syscall.MS_NOATIME
|
||||
NOATIME = unix.MS_NOATIME
|
||||
|
||||
// NODIRATIME will not update the directory access time.
|
||||
NODIRATIME = syscall.MS_NODIRATIME
|
||||
NODIRATIME = unix.MS_NODIRATIME
|
||||
|
||||
// BIND remounts a subtree somewhere else.
|
||||
BIND = syscall.MS_BIND
|
||||
BIND = unix.MS_BIND
|
||||
|
||||
// RBIND remounts a subtree and all possible submounts somewhere else.
|
||||
RBIND = syscall.MS_BIND | syscall.MS_REC
|
||||
RBIND = unix.MS_BIND | unix.MS_REC
|
||||
|
||||
// UNBINDABLE creates a mount which cannot be cloned through a bind operation.
|
||||
UNBINDABLE = syscall.MS_UNBINDABLE
|
||||
UNBINDABLE = unix.MS_UNBINDABLE
|
||||
|
||||
// RUNBINDABLE marks the entire mount tree as UNBINDABLE.
|
||||
RUNBINDABLE = syscall.MS_UNBINDABLE | syscall.MS_REC
|
||||
RUNBINDABLE = unix.MS_UNBINDABLE | unix.MS_REC
|
||||
|
||||
// PRIVATE creates a mount which carries no propagation abilities.
|
||||
PRIVATE = syscall.MS_PRIVATE
|
||||
PRIVATE = unix.MS_PRIVATE
|
||||
|
||||
// RPRIVATE marks the entire mount tree as PRIVATE.
|
||||
RPRIVATE = syscall.MS_PRIVATE | syscall.MS_REC
|
||||
RPRIVATE = unix.MS_PRIVATE | unix.MS_REC
|
||||
|
||||
// SLAVE creates a mount which receives propagation from its master, but not
|
||||
// vice versa.
|
||||
SLAVE = syscall.MS_SLAVE
|
||||
SLAVE = unix.MS_SLAVE
|
||||
|
||||
// RSLAVE marks the entire mount tree as SLAVE.
|
||||
RSLAVE = syscall.MS_SLAVE | syscall.MS_REC
|
||||
RSLAVE = unix.MS_SLAVE | unix.MS_REC
|
||||
|
||||
// SHARED creates a mount which provides the ability to create mirrors of
|
||||
// that mount such that mounts and unmounts within any of the mirrors
|
||||
// propagate to the other mirrors.
|
||||
SHARED = syscall.MS_SHARED
|
||||
SHARED = unix.MS_SHARED
|
||||
|
||||
// RSHARED marks the entire mount tree as SHARED.
|
||||
RSHARED = syscall.MS_SHARED | syscall.MS_REC
|
||||
RSHARED = unix.MS_SHARED | unix.MS_REC
|
||||
|
||||
// RELATIME updates inode access times relative to modify or change time.
|
||||
RELATIME = syscall.MS_RELATIME
|
||||
RELATIME = unix.MS_RELATIME
|
||||
|
||||
// STRICTATIME allows to explicitly request full atime updates. This makes
|
||||
// it possible for the kernel to default to relatime or noatime but still
|
||||
// allow userspace to override it.
|
||||
STRICTATIME = syscall.MS_STRICTATIME
|
||||
STRICTATIME = unix.MS_STRICTATIME
|
||||
|
||||
mntDetach = syscall.MNT_DETACH
|
||||
mntDetach = unix.MNT_DETACH
|
||||
)
|
||||
|
|
|
@ -13,8 +13,9 @@ import "C"
|
|||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func allocateIOVecs(options []string) []C.struct_iovec {
|
||||
|
@ -55,5 +56,5 @@ func mount(device, target, mType string, flag uintptr, data string) error {
|
|||
}
|
||||
|
||||
func unmount(target string, flag int) error {
|
||||
return syscall.Unmount(target, flag)
|
||||
return unix.Unmount(target, flag)
|
||||
}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
package mount
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// ptypes is the set propagation types.
|
||||
ptypes = syscall.MS_SHARED | syscall.MS_PRIVATE | syscall.MS_SLAVE | syscall.MS_UNBINDABLE
|
||||
ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE
|
||||
|
||||
// pflags is the full set valid flags for a change propagation call.
|
||||
pflags = ptypes | syscall.MS_REC | syscall.MS_SILENT
|
||||
pflags = ptypes | unix.MS_REC | unix.MS_SILENT
|
||||
|
||||
// broflags is the combination of bind and read only
|
||||
broflags = syscall.MS_BIND | syscall.MS_RDONLY
|
||||
broflags = unix.MS_BIND | unix.MS_RDONLY
|
||||
)
|
||||
|
||||
// isremount returns true if either device name or flags identify a remount request, false otherwise.
|
||||
|
@ -20,7 +20,7 @@ func isremount(device string, flags uintptr) bool {
|
|||
switch {
|
||||
// We treat device "" and "none" as a remount request to provide compatibility with
|
||||
// requests that don't explicitly set MS_REMOUNT such as those manipulating bind mounts.
|
||||
case flags&syscall.MS_REMOUNT != 0, device == "", device == "none":
|
||||
case flags&unix.MS_REMOUNT != 0, device == "", device == "none":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
@ -29,28 +29,29 @@ func isremount(device string, flags uintptr) bool {
|
|||
|
||||
func mount(device, target, mType string, flags uintptr, data string) error {
|
||||
oflags := flags &^ ptypes
|
||||
if !isremount(device, flags) {
|
||||
// Initial call applying all non-propagation flags.
|
||||
if err := syscall.Mount(device, target, mType, oflags, data); err != nil {
|
||||
if !isremount(device, flags) || data != "" {
|
||||
// Initial call applying all non-propagation flags for mount
|
||||
// or remount with changed data
|
||||
if err := unix.Mount(device, target, mType, oflags, data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if flags&ptypes != 0 {
|
||||
// Change the propagation type.
|
||||
if err := syscall.Mount("", target, "", flags&pflags, ""); err != nil {
|
||||
if err := unix.Mount("", target, "", flags&pflags, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if oflags&broflags == broflags {
|
||||
// Remount the bind to apply read only.
|
||||
return syscall.Mount("", target, "", oflags|syscall.MS_REMOUNT, "")
|
||||
return unix.Mount("", target, "", oflags|unix.MS_REMOUNT, "")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmount(target string, flag int) error {
|
||||
return syscall.Unmount(target, flag)
|
||||
return unix.Unmount(target, flag)
|
||||
}
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
package mount
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// #include <stdlib.h>
|
||||
|
|
|
@ -6,49 +6,49 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/docker/pkg/longpath"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func toShort(path string) (string, error) {
|
||||
p, err := syscall.UTF16FromString(path)
|
||||
p, err := windows.UTF16FromString(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b := p // GetShortPathName says we can reuse buffer
|
||||
n, err := syscall.GetShortPathName(&p[0], &b[0], uint32(len(b)))
|
||||
n, err := windows.GetShortPathName(&p[0], &b[0], uint32(len(b)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n > uint32(len(b)) {
|
||||
b = make([]uint16, n)
|
||||
if _, err = syscall.GetShortPathName(&p[0], &b[0], uint32(len(b))); err != nil {
|
||||
if _, err = windows.GetShortPathName(&p[0], &b[0], uint32(len(b))); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return syscall.UTF16ToString(b), nil
|
||||
return windows.UTF16ToString(b), nil
|
||||
}
|
||||
|
||||
func toLong(path string) (string, error) {
|
||||
p, err := syscall.UTF16FromString(path)
|
||||
p, err := windows.UTF16FromString(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b := p // GetLongPathName says we can reuse buffer
|
||||
n, err := syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
|
||||
n, err := windows.GetLongPathName(&p[0], &b[0], uint32(len(b)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n > uint32(len(b)) {
|
||||
b = make([]uint16, n)
|
||||
n, err = syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
|
||||
n, err = windows.GetLongPathName(&p[0], &b[0], uint32(len(b)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
b = b[:n]
|
||||
return syscall.UTF16ToString(b), nil
|
||||
return windows.UTF16ToString(b), nil
|
||||
}
|
||||
|
||||
func evalSymlinks(path string) (string, error) {
|
||||
|
@ -65,7 +65,7 @@ func evalSymlinks(path string) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// syscall.GetLongPathName does not change the case of the drive letter,
|
||||
// windows.GetLongPathName does not change the case of the drive letter,
|
||||
// but the result of EvalSymlinks must be unique, so we have
|
||||
// EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`).
|
||||
// Make drive letter upper case.
|
||||
|
|
|
@ -3,25 +3,26 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//setCTime will set the create time on a file. On Windows, this requires
|
||||
//calling SetFileTime and explicitly including the create time.
|
||||
func setCTime(path string, ctime time.Time) error {
|
||||
ctimespec := syscall.NsecToTimespec(ctime.UnixNano())
|
||||
pathp, e := syscall.UTF16PtrFromString(path)
|
||||
ctimespec := windows.NsecToTimespec(ctime.UnixNano())
|
||||
pathp, e := windows.UTF16PtrFromString(path)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
h, e := syscall.CreateFile(pathp,
|
||||
syscall.FILE_WRITE_ATTRIBUTES, syscall.FILE_SHARE_WRITE, nil,
|
||||
syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||
h, e := windows.CreateFile(pathp,
|
||||
windows.FILE_WRITE_ATTRIBUTES, windows.FILE_SHARE_WRITE, nil,
|
||||
windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer syscall.Close(h)
|
||||
c := syscall.NsecToFiletime(syscall.TimespecToNsec(ctimespec))
|
||||
return syscall.SetFileTime(h, &c, nil, nil)
|
||||
defer windows.Close(h)
|
||||
c := windows.NsecToFiletime(windows.TimespecToNsec(ctimespec))
|
||||
return windows.SetFileTime(h, &c, nil, nil)
|
||||
}
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
package system
|
||||
|
||||
// This file implements syscalls for Win32 events which are not implemented
|
||||
// in golang.
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
procCreateEvent = modkernel32.NewProc("CreateEventW")
|
||||
procOpenEvent = modkernel32.NewProc("OpenEventW")
|
||||
procSetEvent = modkernel32.NewProc("SetEvent")
|
||||
procResetEvent = modkernel32.NewProc("ResetEvent")
|
||||
procPulseEvent = modkernel32.NewProc("PulseEvent")
|
||||
)
|
||||
|
||||
// CreateEvent implements win32 CreateEventW func in golang. It will create an event object.
|
||||
func CreateEvent(eventAttributes *syscall.SecurityAttributes, manualReset bool, initialState bool, name string) (handle syscall.Handle, err error) {
|
||||
namep, _ := syscall.UTF16PtrFromString(name)
|
||||
var _p1 uint32
|
||||
if manualReset {
|
||||
_p1 = 1
|
||||
}
|
||||
var _p2 uint32
|
||||
if initialState {
|
||||
_p2 = 1
|
||||
}
|
||||
r0, _, e1 := procCreateEvent.Call(uintptr(unsafe.Pointer(eventAttributes)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(namep)))
|
||||
use(unsafe.Pointer(namep))
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == syscall.InvalidHandle {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OpenEvent implements win32 OpenEventW func in golang. It opens an event object.
|
||||
func OpenEvent(desiredAccess uint32, inheritHandle bool, name string) (handle syscall.Handle, err error) {
|
||||
namep, _ := syscall.UTF16PtrFromString(name)
|
||||
var _p1 uint32
|
||||
if inheritHandle {
|
||||
_p1 = 1
|
||||
}
|
||||
r0, _, e1 := procOpenEvent.Call(uintptr(desiredAccess), uintptr(_p1), uintptr(unsafe.Pointer(namep)))
|
||||
use(unsafe.Pointer(namep))
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == syscall.InvalidHandle {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetEvent implements win32 SetEvent func in golang.
|
||||
func SetEvent(handle syscall.Handle) (err error) {
|
||||
return setResetPulse(handle, procSetEvent)
|
||||
}
|
||||
|
||||
// ResetEvent implements win32 ResetEvent func in golang.
|
||||
func ResetEvent(handle syscall.Handle) (err error) {
|
||||
return setResetPulse(handle, procResetEvent)
|
||||
}
|
||||
|
||||
// PulseEvent implements win32 PulseEvent func in golang.
|
||||
func PulseEvent(handle syscall.Handle) (err error) {
|
||||
return setResetPulse(handle, procPulseEvent)
|
||||
}
|
||||
|
||||
func setResetPulse(handle syscall.Handle, proc *windows.LazyProc) (err error) {
|
||||
r0, _, _ := proc.Call(uintptr(handle))
|
||||
if r0 != 0 {
|
||||
err = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var temp unsafe.Pointer
|
||||
|
||||
// use ensures a variable is kept alive without the GC freeing while still needed
|
||||
func use(p unsafe.Pointer) {
|
||||
temp = p
|
||||
}
|
|
@ -14,6 +14,7 @@ import (
|
|||
"unsafe"
|
||||
|
||||
winio "github.com/Microsoft/go-winio"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -99,13 +100,12 @@ func mkdirall(path string, applyACL bool, sddl string) error {
|
|||
// mkdirWithACL creates a new directory. If there is an error, it will be of
|
||||
// type *PathError. .
|
||||
//
|
||||
// This is a modified and combined version of os.Mkdir and syscall.Mkdir
|
||||
// This is a modified and combined version of os.Mkdir and windows.Mkdir
|
||||
// in golang to cater for creating a directory am ACL permitting full
|
||||
// access, with inheritance, to any subfolder/file for Built-in Administrators
|
||||
// and Local System.
|
||||
func mkdirWithACL(name string, sddl string) error {
|
||||
sa := syscall.SecurityAttributes{Length: 0}
|
||||
|
||||
sa := windows.SecurityAttributes{Length: 0}
|
||||
sd, err := winio.SddlToSecurityDescriptor(sddl)
|
||||
if err != nil {
|
||||
return &os.PathError{Op: "mkdir", Path: name, Err: err}
|
||||
|
@ -114,12 +114,12 @@ func mkdirWithACL(name string, sddl string) error {
|
|||
sa.InheritHandle = 1
|
||||
sa.SecurityDescriptor = uintptr(unsafe.Pointer(&sd[0]))
|
||||
|
||||
namep, err := syscall.UTF16PtrFromString(name)
|
||||
namep, err := windows.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return &os.PathError{Op: "mkdir", Path: name, Err: err}
|
||||
}
|
||||
|
||||
e := syscall.CreateDirectory(namep, &sa)
|
||||
e := windows.CreateDirectory(namep, &sa)
|
||||
if e != nil {
|
||||
return &os.PathError{Op: "mkdir", Path: name, Err: e}
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ func IsAbs(path string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// The origin of the functions below here are the golang OS and syscall packages,
|
||||
// The origin of the functions below here are the golang OS and windows packages,
|
||||
// slightly modified to only cope with files, not directories due to the
|
||||
// specific use case.
|
||||
//
|
||||
|
@ -174,74 +174,74 @@ func OpenFileSequential(name string, flag int, _ os.FileMode) (*os.File, error)
|
|||
if name == "" {
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT}
|
||||
}
|
||||
r, errf := syscallOpenFileSequential(name, flag, 0)
|
||||
r, errf := windowsOpenFileSequential(name, flag, 0)
|
||||
if errf == nil {
|
||||
return r, nil
|
||||
}
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: errf}
|
||||
}
|
||||
|
||||
func syscallOpenFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) {
|
||||
r, e := syscallOpenSequential(name, flag|syscall.O_CLOEXEC, 0)
|
||||
func windowsOpenFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) {
|
||||
r, e := windowsOpenSequential(name, flag|windows.O_CLOEXEC, 0)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return os.NewFile(uintptr(r), name), nil
|
||||
}
|
||||
|
||||
func makeInheritSa() *syscall.SecurityAttributes {
|
||||
var sa syscall.SecurityAttributes
|
||||
func makeInheritSa() *windows.SecurityAttributes {
|
||||
var sa windows.SecurityAttributes
|
||||
sa.Length = uint32(unsafe.Sizeof(sa))
|
||||
sa.InheritHandle = 1
|
||||
return &sa
|
||||
}
|
||||
|
||||
func syscallOpenSequential(path string, mode int, _ uint32) (fd syscall.Handle, err error) {
|
||||
func windowsOpenSequential(path string, mode int, _ uint32) (fd windows.Handle, err error) {
|
||||
if len(path) == 0 {
|
||||
return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
|
||||
return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND
|
||||
}
|
||||
pathp, err := syscall.UTF16PtrFromString(path)
|
||||
pathp, err := windows.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return syscall.InvalidHandle, err
|
||||
return windows.InvalidHandle, err
|
||||
}
|
||||
var access uint32
|
||||
switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) {
|
||||
case syscall.O_RDONLY:
|
||||
access = syscall.GENERIC_READ
|
||||
case syscall.O_WRONLY:
|
||||
access = syscall.GENERIC_WRITE
|
||||
case syscall.O_RDWR:
|
||||
access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
|
||||
switch mode & (windows.O_RDONLY | windows.O_WRONLY | windows.O_RDWR) {
|
||||
case windows.O_RDONLY:
|
||||
access = windows.GENERIC_READ
|
||||
case windows.O_WRONLY:
|
||||
access = windows.GENERIC_WRITE
|
||||
case windows.O_RDWR:
|
||||
access = windows.GENERIC_READ | windows.GENERIC_WRITE
|
||||
}
|
||||
if mode&syscall.O_CREAT != 0 {
|
||||
access |= syscall.GENERIC_WRITE
|
||||
if mode&windows.O_CREAT != 0 {
|
||||
access |= windows.GENERIC_WRITE
|
||||
}
|
||||
if mode&syscall.O_APPEND != 0 {
|
||||
access &^= syscall.GENERIC_WRITE
|
||||
access |= syscall.FILE_APPEND_DATA
|
||||
if mode&windows.O_APPEND != 0 {
|
||||
access &^= windows.GENERIC_WRITE
|
||||
access |= windows.FILE_APPEND_DATA
|
||||
}
|
||||
sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE)
|
||||
var sa *syscall.SecurityAttributes
|
||||
if mode&syscall.O_CLOEXEC == 0 {
|
||||
sharemode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE)
|
||||
var sa *windows.SecurityAttributes
|
||||
if mode&windows.O_CLOEXEC == 0 {
|
||||
sa = makeInheritSa()
|
||||
}
|
||||
var createmode uint32
|
||||
switch {
|
||||
case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
|
||||
createmode = syscall.CREATE_NEW
|
||||
case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC):
|
||||
createmode = syscall.CREATE_ALWAYS
|
||||
case mode&syscall.O_CREAT == syscall.O_CREAT:
|
||||
createmode = syscall.OPEN_ALWAYS
|
||||
case mode&syscall.O_TRUNC == syscall.O_TRUNC:
|
||||
createmode = syscall.TRUNCATE_EXISTING
|
||||
case mode&(windows.O_CREAT|windows.O_EXCL) == (windows.O_CREAT | windows.O_EXCL):
|
||||
createmode = windows.CREATE_NEW
|
||||
case mode&(windows.O_CREAT|windows.O_TRUNC) == (windows.O_CREAT | windows.O_TRUNC):
|
||||
createmode = windows.CREATE_ALWAYS
|
||||
case mode&windows.O_CREAT == windows.O_CREAT:
|
||||
createmode = windows.OPEN_ALWAYS
|
||||
case mode&windows.O_TRUNC == windows.O_TRUNC:
|
||||
createmode = windows.TRUNCATE_EXISTING
|
||||
default:
|
||||
createmode = syscall.OPEN_EXISTING
|
||||
createmode = windows.OPEN_EXISTING
|
||||
}
|
||||
// Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang.
|
||||
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
|
||||
const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN
|
||||
h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0)
|
||||
h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0)
|
||||
return h, e
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
package system
|
||||
|
||||
import "syscall"
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Lstat takes a path to a file and returns
|
||||
// a system.StatT type pertaining to that file.
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Mknod creates a filesystem node (file, device special file or named pipe) named path
|
||||
// with attributes specified by mode and dev.
|
||||
func Mknod(path string, mode uint32, dev int) error {
|
||||
return syscall.Mknod(path, mode, dev)
|
||||
return unix.Mknod(path, mode, dev)
|
||||
}
|
||||
|
||||
// Mkdev is used to build the value of linux devices (in /dev/) which specifies major
|
||||
|
|
|
@ -4,12 +4,14 @@ package system
|
|||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IsProcessAlive returns true if process with a given pid is running.
|
||||
func IsProcessAlive(pid int) bool {
|
||||
err := syscall.Kill(pid, syscall.Signal(0))
|
||||
if err == nil || err == syscall.EPERM {
|
||||
err := unix.Kill(pid, syscall.Signal(0))
|
||||
if err == nil || err == unix.EPERM {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -18,5 +20,5 @@ func IsProcessAlive(pid int) bool {
|
|||
|
||||
// KillProcess force-stops a process.
|
||||
func KillProcess(pid int) {
|
||||
syscall.Kill(pid, syscall.SIGKILL)
|
||||
unix.Kill(pid, unix.SIGKILL)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
package system
|
||||
|
||||
import "syscall"
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// StatT type contains status of a file. It contains metadata
|
||||
// like permission, owner, group, size, etc about a file.
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
package system
|
||||
|
||||
import "syscall"
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// Unmount is a platform-specific helper function to call
|
||||
// the unmount syscall.
|
||||
func Unmount(dest string) error {
|
||||
return syscall.Unmount(dest, 0)
|
||||
return unix.Unmount(dest, 0)
|
||||
}
|
||||
|
||||
// CommandLineToArgv should not be used on Unix.
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
ntuserApiset = syscall.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0")
|
||||
ntuserApiset = windows.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0")
|
||||
procGetVersionExW = modkernel32.NewProc("GetVersionExW")
|
||||
procGetProductInfo = modkernel32.NewProc("GetProductInfo")
|
||||
)
|
||||
|
@ -42,7 +42,7 @@ type osVersionInfoEx struct {
|
|||
func GetOSVersion() OSVersion {
|
||||
var err error
|
||||
osv := OSVersion{}
|
||||
osv.Version, err = syscall.GetVersion()
|
||||
osv.Version, err = windows.GetVersion()
|
||||
if err != nil {
|
||||
// GetVersion never fails.
|
||||
panic(err)
|
||||
|
@ -93,20 +93,20 @@ func Unmount(dest string) error {
|
|||
func CommandLineToArgv(commandLine string) ([]string, error) {
|
||||
var argc int32
|
||||
|
||||
argsPtr, err := syscall.UTF16PtrFromString(commandLine)
|
||||
argsPtr, err := windows.UTF16PtrFromString(commandLine)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
argv, err := syscall.CommandLineToArgv(argsPtr, &argc)
|
||||
argv, err := windows.CommandLineToArgv(argsPtr, &argc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv))))
|
||||
defer windows.LocalFree(windows.Handle(uintptr(unsafe.Pointer(argv))))
|
||||
|
||||
newArgs := make([]string, argc)
|
||||
for i, v := range (*argv)[:argc] {
|
||||
newArgs[i] = string(syscall.UTF16ToString((*v)[:]))
|
||||
newArgs[i] = string(windows.UTF16ToString((*v)[:]))
|
||||
}
|
||||
|
||||
return newArgs, nil
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Umask sets current process's file mode creation mask to newmask
|
||||
// and returns oldmask.
|
||||
func Umask(newmask int) (oldmask int, err error) {
|
||||
return syscall.Umask(newmask), nil
|
||||
return unix.Umask(newmask), nil
|
||||
}
|
||||
|
|
|
@ -3,18 +3,20 @@ package system
|
|||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// LUtimesNano is used to change access and modification time of the specified path.
|
||||
// It's used for symbol link file because syscall.UtimesNano doesn't support a NOFOLLOW flag atm.
|
||||
// It's used for symbol link file because unix.UtimesNano doesn't support a NOFOLLOW flag atm.
|
||||
func LUtimesNano(path string, ts []syscall.Timespec) error {
|
||||
var _path *byte
|
||||
_path, err := syscall.BytePtrFromString(path)
|
||||
_path, err := unix.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_LUTIMES, uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), 0); err != 0 && err != syscall.ENOSYS {
|
||||
if _, _, err := unix.Syscall(unix.SYS_LUTIMES, uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), 0); err != 0 && err != unix.ENOSYS {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -3,22 +3,21 @@ package system
|
|||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// LUtimesNano is used to change access and modification time of the specified path.
|
||||
// It's used for symbol link file because syscall.UtimesNano doesn't support a NOFOLLOW flag atm.
|
||||
// It's used for symbol link file because unix.UtimesNano doesn't support a NOFOLLOW flag atm.
|
||||
func LUtimesNano(path string, ts []syscall.Timespec) error {
|
||||
// These are not currently available in syscall
|
||||
atFdCwd := -100
|
||||
atSymLinkNoFollow := 0x100
|
||||
atFdCwd := unix.AT_FDCWD
|
||||
|
||||
var _path *byte
|
||||
_path, err := syscall.BytePtrFromString(path)
|
||||
_path, err := unix.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_UTIMENSAT, uintptr(atFdCwd), uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), uintptr(atSymLinkNoFollow), 0, 0); err != 0 && err != syscall.ENOSYS {
|
||||
if _, _, err := unix.Syscall6(unix.SYS_UTIMENSAT, uintptr(atFdCwd), uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), unix.AT_SYMLINK_NOFOLLOW, 0, 0); err != 0 && err != unix.ENOSYS {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,63 +1,29 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// Lgetxattr retrieves the value of the extended attribute identified by attr
|
||||
// and associated with the given path in the file system.
|
||||
// It will returns a nil slice and nil error if the xattr is not set.
|
||||
func Lgetxattr(path string, attr string) ([]byte, error) {
|
||||
pathBytes, err := syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attrBytes, err := syscall.BytePtrFromString(attr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dest := make([]byte, 128)
|
||||
destBytes := unsafe.Pointer(&dest[0])
|
||||
sz, _, errno := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0)
|
||||
if errno == syscall.ENODATA {
|
||||
sz, errno := unix.Lgetxattr(path, attr, dest)
|
||||
if errno == unix.ENODATA {
|
||||
return nil, nil
|
||||
}
|
||||
if errno == syscall.ERANGE {
|
||||
if errno == unix.ERANGE {
|
||||
dest = make([]byte, sz)
|
||||
destBytes := unsafe.Pointer(&dest[0])
|
||||
sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0)
|
||||
sz, errno = unix.Lgetxattr(path, attr, dest)
|
||||
}
|
||||
if errno != 0 {
|
||||
if errno != nil {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
return dest[:sz], nil
|
||||
}
|
||||
|
||||
var _zero uintptr
|
||||
|
||||
// Lsetxattr sets the value of the extended attribute identified by attr
|
||||
// and associated with the given path in the file system.
|
||||
func Lsetxattr(path string, attr string, data []byte, flags int) error {
|
||||
pathBytes, err := syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attrBytes, err := syscall.BytePtrFromString(attr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var dataBytes unsafe.Pointer
|
||||
if len(data) > 0 {
|
||||
dataBytes = unsafe.Pointer(&data[0])
|
||||
} else {
|
||||
dataBytes = unsafe.Pointer(&_zero)
|
||||
}
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(dataBytes), uintptr(len(data)), uintptr(flags), 0)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
return unix.Lsetxattr(path, attr, data, flags)
|
||||
}
|
||||
|
|
|
@ -1,33 +1,35 @@
|
|||
# the following lines are in sorted order, FYI
|
||||
github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62
|
||||
github.com/Microsoft/hcsshim v0.5.25
|
||||
github.com/Microsoft/go-winio v0.4.2
|
||||
github.com/sirupsen/logrus v1.0.0
|
||||
github.com/Azure/go-ansiterm 19f72df4d05d31cbe1c56bfc8045c96babff6c7e
|
||||
github.com/Microsoft/hcsshim v0.6.3
|
||||
github.com/Microsoft/go-winio v0.4.5
|
||||
github.com/moby/buildkit da2b9dc7dab99e824b2b1067ad7d0523e32dd2d9 https://github.com/dmcgowan/buildkit.git
|
||||
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
||||
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
||||
github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git
|
||||
github.com/gorilla/context v1.1
|
||||
github.com/gorilla/mux v1.1
|
||||
github.com/jhowardmsft/opengcs v0.0.7
|
||||
github.com/Microsoft/opengcs v0.3.2
|
||||
github.com/kr/pty 5cf931ef8f
|
||||
github.com/mattn/go-shellwords v1.0.3
|
||||
github.com/sirupsen/logrus v1.0.1
|
||||
github.com/tchap/go-patricia v2.2.6
|
||||
github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
|
||||
golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
|
||||
golang.org/x/sys 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9
|
||||
golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
|
||||
github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
|
||||
github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
|
||||
golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
|
||||
github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
|
||||
github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/gotestyourself/gotestyourself v1.1.0
|
||||
|
||||
github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5
|
||||
github.com/imdario/mergo 0.2.1
|
||||
golang.org/x/sync de49d9dcd27d4f764488181bea099dfe6179bcf0
|
||||
|
||||
#get libnetwork packages
|
||||
github.com/docker/libnetwork 6426d1e66f33c0b0c8bb135b7ee547447f54d043
|
||||
github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894
|
||||
github.com/docker/libnetwork 5b28c0ec98236c489e39ae6a9e1aeb802e071681
|
||||
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
|
||||
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
|
||||
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
||||
github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
|
||||
|
@ -42,14 +44,15 @@ github.com/vishvananda/netlink bd6d5de5ccef2d66b0a26177928d0d8895d7f969
|
|||
github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060
|
||||
github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
|
||||
github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
|
||||
github.com/coreos/etcd ea5389a79f40206170582c1ea076191b8622cb8e https://github.com/aaronlehmann/etcd # for https://github.com/coreos/etcd/pull/7830
|
||||
github.com/coreos/etcd v3.2.1
|
||||
github.com/coreos/go-semver v0.2.0
|
||||
github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065
|
||||
github.com/hashicorp/consul v0.5.2
|
||||
github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904
|
||||
github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7
|
||||
|
||||
# get graph and distribution packages
|
||||
github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621
|
||||
github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c
|
||||
github.com/vbatts/tar-split v0.10.1
|
||||
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
||||
|
||||
|
@ -60,9 +63,9 @@ github.com/pborman/uuid v1.0
|
|||
google.golang.org/grpc v1.3.0
|
||||
|
||||
# When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly
|
||||
github.com/opencontainers/runc 2d41c047c83e09a6d61d464906feb2a2f3c52aa4 https://github.com/docker/runc
|
||||
github.com/opencontainers/image-spec f03dbe35d449c54915d235f1a3cf8f585a24babe
|
||||
github.com/opencontainers/runtime-spec d42f1eb741e6361e858d83fc75aa6893b66292c4 # specs
|
||||
github.com/opencontainers/runc 3f2f8b84a77f73d38244dd690525642a72156c64
|
||||
github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13
|
||||
github.com/opencontainers/runtime-spec v1.0.0
|
||||
|
||||
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
|
||||
|
||||
|
@ -81,7 +84,7 @@ github.com/philhofer/fwd 98c11a7a6ec829d672b03833c3d69a7fae1ca972
|
|||
github.com/tinylib/msgp 75ee40d2601edf122ef667e2a07d600d4c44490c
|
||||
|
||||
# fsnotify
|
||||
github.com/fsnotify/fsnotify v1.2.11
|
||||
github.com/fsnotify/fsnotify v1.4.2
|
||||
|
||||
# awslogs deps
|
||||
github.com/aws/aws-sdk-go v1.4.22
|
||||
|
@ -99,13 +102,13 @@ github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7
|
|||
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
|
||||
|
||||
# containerd
|
||||
github.com/containerd/containerd 3addd840653146c90a254301d6c3a663c7fd6429
|
||||
github.com/containerd/containerd 06b9cb35161009dcb7123345749fef02f7cea8e0
|
||||
github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4
|
||||
github.com/stevvooe/continuity cd7a8e21e2b6f84799f5dd4b65faf49c8d3ee02d
|
||||
github.com/tonistiigi/fsutil 0ac4c11b053b9c5c7c47558f81f96c7100ce50fb
|
||||
|
||||
# cluster
|
||||
github.com/docker/swarmkit 79381d0840be27f8b3f5c667b348a4467d866eeb
|
||||
github.com/docker/swarmkit ddb4539f883b18ea40af44ee6de63ac2adc8dc1e
|
||||
github.com/gogo/protobuf v0.4
|
||||
github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
|
||||
github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e
|
||||
|
@ -135,3 +138,11 @@ github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.
|
|||
github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18
|
||||
|
||||
github.com/opencontainers/selinux v1.0.0-rc1
|
||||
|
||||
# archive/tar
|
||||
# mkdir -p ./vendor/archive
|
||||
# git clone git://github.com/tonistiigi/go-1.git ./go
|
||||
# git --git-dir ./go/.git --work-tree ./go checkout revert-prefix-ignore
|
||||
# cp -a go/src/archive/tar ./vendor/archive/tar
|
||||
# rm -rf ./go
|
||||
# vndr
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2015 Docker, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,13 @@
|
|||
[![GoDoc](https://godoc.org/github.com/docker/go-connections?status.svg)](https://godoc.org/github.com/docker/go-connections)
|
||||
|
||||
# Introduction
|
||||
|
||||
go-connections provides common package to work with network connections.
|
||||
|
||||
## Usage
|
||||
|
||||
See the [docs in godoc](https://godoc.org/github.com/docker/go-connections) for examples and documentation.
|
||||
|
||||
## License
|
||||
|
||||
go-connections is licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for the full license text.
|
|
@ -0,0 +1,242 @@
|
|||
// Package nat is a convenience package for manipulation of strings describing network ports.
|
||||
package nat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// portSpecTemplate is the expected format for port specifications
|
||||
portSpecTemplate = "ip:hostPort:containerPort"
|
||||
)
|
||||
|
||||
// PortBinding represents a binding between a Host IP address and a Host Port
|
||||
type PortBinding struct {
|
||||
// HostIP is the host IP Address
|
||||
HostIP string `json:"HostIp"`
|
||||
// HostPort is the host port number
|
||||
HostPort string
|
||||
}
|
||||
|
||||
// PortMap is a collection of PortBinding indexed by Port
|
||||
type PortMap map[Port][]PortBinding
|
||||
|
||||
// PortSet is a collection of structs indexed by Port
|
||||
type PortSet map[Port]struct{}
|
||||
|
||||
// Port is a string containing port number and protocol in the format "80/tcp"
|
||||
type Port string
|
||||
|
||||
// NewPort creates a new instance of a Port given a protocol and port number or port range
|
||||
func NewPort(proto, port string) (Port, error) {
|
||||
// Check for parsing issues on "port" now so we can avoid having
|
||||
// to check it later on.
|
||||
|
||||
portStartInt, portEndInt, err := ParsePortRangeToInt(port)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if portStartInt == portEndInt {
|
||||
return Port(fmt.Sprintf("%d/%s", portStartInt, proto)), nil
|
||||
}
|
||||
return Port(fmt.Sprintf("%d-%d/%s", portStartInt, portEndInt, proto)), nil
|
||||
}
|
||||
|
||||
// ParsePort parses the port number string and returns an int
|
||||
func ParsePort(rawPort string) (int, error) {
|
||||
if len(rawPort) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
port, err := strconv.ParseUint(rawPort, 10, 16)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(port), nil
|
||||
}
|
||||
|
||||
// ParsePortRangeToInt parses the port range string and returns start/end ints
|
||||
func ParsePortRangeToInt(rawPort string) (int, int, error) {
|
||||
if len(rawPort) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
start, end, err := ParsePortRange(rawPort)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(start), int(end), nil
|
||||
}
|
||||
|
||||
// Proto returns the protocol of a Port
|
||||
func (p Port) Proto() string {
|
||||
proto, _ := SplitProtoPort(string(p))
|
||||
return proto
|
||||
}
|
||||
|
||||
// Port returns the port number of a Port
|
||||
func (p Port) Port() string {
|
||||
_, port := SplitProtoPort(string(p))
|
||||
return port
|
||||
}
|
||||
|
||||
// Int returns the port number of a Port as an int
|
||||
func (p Port) Int() int {
|
||||
portStr := p.Port()
|
||||
// We don't need to check for an error because we're going to
|
||||
// assume that any error would have been found, and reported, in NewPort()
|
||||
port, _ := ParsePort(portStr)
|
||||
return port
|
||||
}
|
||||
|
||||
// Range returns the start/end port numbers of a Port range as ints
|
||||
func (p Port) Range() (int, int, error) {
|
||||
return ParsePortRangeToInt(p.Port())
|
||||
}
|
||||
|
||||
// SplitProtoPort splits a port in the format of proto/port
|
||||
func SplitProtoPort(rawPort string) (string, string) {
|
||||
parts := strings.Split(rawPort, "/")
|
||||
l := len(parts)
|
||||
if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 {
|
||||
return "", ""
|
||||
}
|
||||
if l == 1 {
|
||||
return "tcp", rawPort
|
||||
}
|
||||
if len(parts[1]) == 0 {
|
||||
return "tcp", parts[0]
|
||||
}
|
||||
return parts[1], parts[0]
|
||||
}
|
||||
|
||||
func validateProto(proto string) bool {
|
||||
for _, availableProto := range []string{"tcp", "udp"} {
|
||||
if availableProto == proto {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ParsePortSpecs receives port specs in the format of ip:public:private/proto and parses
|
||||
// these in to the internal types
|
||||
func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) {
|
||||
var (
|
||||
exposedPorts = make(map[Port]struct{}, len(ports))
|
||||
bindings = make(map[Port][]PortBinding)
|
||||
)
|
||||
for _, rawPort := range ports {
|
||||
portMappings, err := ParsePortSpec(rawPort)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, portMapping := range portMappings {
|
||||
port := portMapping.Port
|
||||
if _, exists := exposedPorts[port]; !exists {
|
||||
exposedPorts[port] = struct{}{}
|
||||
}
|
||||
bslice, exists := bindings[port]
|
||||
if !exists {
|
||||
bslice = []PortBinding{}
|
||||
}
|
||||
bindings[port] = append(bslice, portMapping.Binding)
|
||||
}
|
||||
}
|
||||
return exposedPorts, bindings, nil
|
||||
}
|
||||
|
||||
// PortMapping is a data object mapping a Port to a PortBinding
|
||||
type PortMapping struct {
|
||||
Port Port
|
||||
Binding PortBinding
|
||||
}
|
||||
|
||||
func splitParts(rawport string) (string, string, string) {
|
||||
parts := strings.Split(rawport, ":")
|
||||
n := len(parts)
|
||||
containerport := parts[n-1]
|
||||
|
||||
switch n {
|
||||
case 1:
|
||||
return "", "", containerport
|
||||
case 2:
|
||||
return "", parts[0], containerport
|
||||
case 3:
|
||||
return parts[0], parts[1], containerport
|
||||
default:
|
||||
return strings.Join(parts[:n-2], ":"), parts[n-2], containerport
|
||||
}
|
||||
}
|
||||
|
||||
// ParsePortSpec parses a port specification string into a slice of PortMappings
|
||||
func ParsePortSpec(rawPort string) ([]PortMapping, error) {
|
||||
var proto string
|
||||
rawIP, hostPort, containerPort := splitParts(rawPort)
|
||||
proto, containerPort = SplitProtoPort(containerPort)
|
||||
|
||||
// Strip [] from IPV6 addresses
|
||||
ip, _, err := net.SplitHostPort(rawIP + ":")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid ip address %v: %s", rawIP, err)
|
||||
}
|
||||
if ip != "" && net.ParseIP(ip) == nil {
|
||||
return nil, fmt.Errorf("Invalid ip address: %s", ip)
|
||||
}
|
||||
if containerPort == "" {
|
||||
return nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
|
||||
}
|
||||
|
||||
startPort, endPort, err := ParsePortRange(containerPort)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
|
||||
}
|
||||
|
||||
var startHostPort, endHostPort uint64 = 0, 0
|
||||
if len(hostPort) > 0 {
|
||||
startHostPort, endHostPort, err = ParsePortRange(hostPort)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
|
||||
}
|
||||
}
|
||||
|
||||
if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) {
|
||||
// Allow host port range iff containerPort is not a range.
|
||||
// In this case, use the host port range as the dynamic
|
||||
// host port range to allocate into.
|
||||
if endPort != startPort {
|
||||
return nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort)
|
||||
}
|
||||
}
|
||||
|
||||
if !validateProto(strings.ToLower(proto)) {
|
||||
return nil, fmt.Errorf("Invalid proto: %s", proto)
|
||||
}
|
||||
|
||||
ports := []PortMapping{}
|
||||
for i := uint64(0); i <= (endPort - startPort); i++ {
|
||||
containerPort = strconv.FormatUint(startPort+i, 10)
|
||||
if len(hostPort) > 0 {
|
||||
hostPort = strconv.FormatUint(startHostPort+i, 10)
|
||||
}
|
||||
// Set hostPort to a range only if there is a single container port
|
||||
// and a dynamic host port.
|
||||
if startPort == endPort && startHostPort != endHostPort {
|
||||
hostPort = fmt.Sprintf("%s-%s", hostPort, strconv.FormatUint(endHostPort, 10))
|
||||
}
|
||||
port, err := NewPort(strings.ToLower(proto), containerPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
binding := PortBinding{
|
||||
HostIP: ip,
|
||||
HostPort: hostPort,
|
||||
}
|
||||
ports = append(ports, PortMapping{Port: port, Binding: binding})
|
||||
}
|
||||
return ports, nil
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package nat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PartParser parses and validates the specified string (data) using the specified template
|
||||
// e.g. ip:public:private -> 192.168.0.1:80:8000
|
||||
// DEPRECATED: do not use, this function may be removed in a future version
|
||||
func PartParser(template, data string) (map[string]string, error) {
|
||||
// ip:public:private
|
||||
var (
|
||||
templateParts = strings.Split(template, ":")
|
||||
parts = strings.Split(data, ":")
|
||||
out = make(map[string]string, len(templateParts))
|
||||
)
|
||||
if len(parts) != len(templateParts) {
|
||||
return nil, fmt.Errorf("Invalid format to parse. %s should match template %s", data, template)
|
||||
}
|
||||
|
||||
for i, t := range templateParts {
|
||||
value := ""
|
||||
if len(parts) > i {
|
||||
value = parts[i]
|
||||
}
|
||||
out[t] = value
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ParsePortRange parses and validates the specified string as a port-range (8000-9000)
|
||||
func ParsePortRange(ports string) (uint64, uint64, error) {
|
||||
if ports == "" {
|
||||
return 0, 0, fmt.Errorf("Empty string specified for ports.")
|
||||
}
|
||||
if !strings.Contains(ports, "-") {
|
||||
start, err := strconv.ParseUint(ports, 10, 16)
|
||||
end := start
|
||||
return start, end, err
|
||||
}
|
||||
|
||||
parts := strings.Split(ports, "-")
|
||||
start, err := strconv.ParseUint(parts[0], 10, 16)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
end, err := strconv.ParseUint(parts[1], 10, 16)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if end < start {
|
||||
return 0, 0, fmt.Errorf("Invalid range specified for the Port: %s", ports)
|
||||
}
|
||||
return start, end, nil
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package nat
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type portSorter struct {
|
||||
ports []Port
|
||||
by func(i, j Port) bool
|
||||
}
|
||||
|
||||
func (s *portSorter) Len() int {
|
||||
return len(s.ports)
|
||||
}
|
||||
|
||||
func (s *portSorter) Swap(i, j int) {
|
||||
s.ports[i], s.ports[j] = s.ports[j], s.ports[i]
|
||||
}
|
||||
|
||||
func (s *portSorter) Less(i, j int) bool {
|
||||
ip := s.ports[i]
|
||||
jp := s.ports[j]
|
||||
|
||||
return s.by(ip, jp)
|
||||
}
|
||||
|
||||
// Sort sorts a list of ports using the provided predicate
|
||||
// This function should compare `i` and `j`, returning true if `i` is
|
||||
// considered to be less than `j`
|
||||
func Sort(ports []Port, predicate func(i, j Port) bool) {
|
||||
s := &portSorter{ports, predicate}
|
||||
sort.Sort(s)
|
||||
}
|
||||
|
||||
type portMapEntry struct {
|
||||
port Port
|
||||
binding PortBinding
|
||||
}
|
||||
|
||||
type portMapSorter []portMapEntry
|
||||
|
||||
func (s portMapSorter) Len() int { return len(s) }
|
||||
func (s portMapSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// sort the port so that the order is:
|
||||
// 1. port with larger specified bindings
|
||||
// 2. larger port
|
||||
// 3. port with tcp protocol
|
||||
func (s portMapSorter) Less(i, j int) bool {
|
||||
pi, pj := s[i].port, s[j].port
|
||||
hpi, hpj := toInt(s[i].binding.HostPort), toInt(s[j].binding.HostPort)
|
||||
return hpi > hpj || pi.Int() > pj.Int() || (pi.Int() == pj.Int() && strings.ToLower(pi.Proto()) == "tcp")
|
||||
}
|
||||
|
||||
// SortPortMap sorts the list of ports and their respected mapping. The ports
|
||||
// will explicit HostPort will be placed first.
|
||||
func SortPortMap(ports []Port, bindings PortMap) {
|
||||
s := portMapSorter{}
|
||||
for _, p := range ports {
|
||||
if binding, ok := bindings[p]; ok {
|
||||
for _, b := range binding {
|
||||
s = append(s, portMapEntry{port: p, binding: b})
|
||||
}
|
||||
bindings[p] = []PortBinding{}
|
||||
} else {
|
||||
s = append(s, portMapEntry{port: p})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(s)
|
||||
var (
|
||||
i int
|
||||
pm = make(map[Port]struct{})
|
||||
)
|
||||
// reorder ports
|
||||
for _, entry := range s {
|
||||
if _, ok := pm[entry.port]; !ok {
|
||||
ports[i] = entry.port
|
||||
pm[entry.port] = struct{}{}
|
||||
i++
|
||||
}
|
||||
// reorder bindings for this port
|
||||
if _, ok := bindings[entry.port]; ok {
|
||||
bindings[entry.port] = append(bindings[entry.port], entry.binding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toInt(s string) uint64 {
|
||||
i, _, err := ParsePortRange(s)
|
||||
if err != nil {
|
||||
i = 0
|
||||
}
|
||||
return i
|
||||
}
|
Loading…
Reference in New Issue