Implement plugins for logging drivers

Logging plugins use the same HTTP interface as other plugins for basic
command operations meanwhile actual logging operations are handled (on
Unix) via a fifo.

The plugin interface looks like so:

```go
type loggingPlugin interface {
  StartLogging(fifoPath string, loggingContext Context) error
  StopLogging(fifoPath)
```

This means a plugin must implement `LoggingDriver.StartLogging` and
`LoggingDriver.StopLogging` endpoints and be able to consume the passed
in fifo.

Logs are sent via stream encoder to the fifo encoded with protobuf.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
Upstream-commit: 27bd6842f8518780b99fdb42f8e7f84c17856d87
Component: engine
This commit is contained in:
Brian Goff
2017-04-10 13:17:20 -04:00
parent cb5c83df55
commit 276ea6a456
22 changed files with 1842 additions and 4 deletions
@@ -0,0 +1,449 @@
// Code generated by protoc-gen-gogo.
// source: entry.proto
// DO NOT EDIT!
/*
Package logdriver is a generated protocol buffer package.
It is generated from these files:
entry.proto
It has these top-level messages:
LogEntry
*/
package logdriver
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
import io "io"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type LogEntry struct {
Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"`
TimeNano int64 `protobuf:"varint,2,opt,name=time_nano,json=timeNano,proto3" json:"time_nano,omitempty"`
Line []byte `protobuf:"bytes,3,opt,name=line,proto3" json:"line,omitempty"`
Partial bool `protobuf:"varint,4,opt,name=partial,proto3" json:"partial,omitempty"`
}
func (m *LogEntry) Reset() { *m = LogEntry{} }
func (m *LogEntry) String() string { return proto.CompactTextString(m) }
func (*LogEntry) ProtoMessage() {}
func (*LogEntry) Descriptor() ([]byte, []int) { return fileDescriptorEntry, []int{0} }
func (m *LogEntry) GetSource() string {
if m != nil {
return m.Source
}
return ""
}
func (m *LogEntry) GetTimeNano() int64 {
if m != nil {
return m.TimeNano
}
return 0
}
func (m *LogEntry) GetLine() []byte {
if m != nil {
return m.Line
}
return nil
}
func (m *LogEntry) GetPartial() bool {
if m != nil {
return m.Partial
}
return false
}
func init() {
proto.RegisterType((*LogEntry)(nil), "LogEntry")
}
func (m *LogEntry) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *LogEntry) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.Source) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintEntry(dAtA, i, uint64(len(m.Source)))
i += copy(dAtA[i:], m.Source)
}
if m.TimeNano != 0 {
dAtA[i] = 0x10
i++
i = encodeVarintEntry(dAtA, i, uint64(m.TimeNano))
}
if len(m.Line) > 0 {
dAtA[i] = 0x1a
i++
i = encodeVarintEntry(dAtA, i, uint64(len(m.Line)))
i += copy(dAtA[i:], m.Line)
}
if m.Partial {
dAtA[i] = 0x20
i++
if m.Partial {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i++
}
return i, nil
}
func encodeFixed64Entry(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
dAtA[offset+4] = uint8(v >> 32)
dAtA[offset+5] = uint8(v >> 40)
dAtA[offset+6] = uint8(v >> 48)
dAtA[offset+7] = uint8(v >> 56)
return offset + 8
}
func encodeFixed32Entry(dAtA []byte, offset int, v uint32) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
return offset + 4
}
func encodeVarintEntry(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
}
func (m *LogEntry) Size() (n int) {
var l int
_ = l
l = len(m.Source)
if l > 0 {
n += 1 + l + sovEntry(uint64(l))
}
if m.TimeNano != 0 {
n += 1 + sovEntry(uint64(m.TimeNano))
}
l = len(m.Line)
if l > 0 {
n += 1 + l + sovEntry(uint64(l))
}
if m.Partial {
n += 2
}
return n
}
func sovEntry(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
}
func sozEntry(x uint64) (n int) {
return sovEntry(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *LogEntry) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEntry
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: LogEntry: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: LogEntry: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEntry
}
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 ErrInvalidLengthEntry
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Source = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field TimeNano", wireType)
}
m.TimeNano = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEntry
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.TimeNano |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Line", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEntry
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthEntry
}
postIndex := iNdEx + byteLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Line = append(m.Line[:0], dAtA[iNdEx:postIndex]...)
if m.Line == nil {
m.Line = []byte{}
}
iNdEx = postIndex
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Partial", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEntry
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
m.Partial = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipEntry(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthEntry
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipEntry(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowEntry
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowEntry
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowEntry
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthEntry
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowEntry
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipEntry(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
}
return iNdEx, nil
case 4:
return iNdEx, nil
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
}
panic("unreachable")
}
var (
ErrInvalidLengthEntry = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowEntry = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("entry.proto", fileDescriptorEntry) }
var fileDescriptorEntry = []byte{
// 149 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0xcd, 0x2b, 0x29,
0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0xca, 0xe5, 0xe2, 0xf0, 0xc9, 0x4f, 0x77, 0x05,
0x89, 0x08, 0x89, 0x71, 0xb1, 0x15, 0xe7, 0x97, 0x16, 0x25, 0xa7, 0x4a, 0x30, 0x2a, 0x30, 0x6a,
0x70, 0x06, 0x41, 0x79, 0x42, 0xd2, 0x5c, 0x9c, 0x25, 0x99, 0xb9, 0xa9, 0xf1, 0x79, 0x89, 0x79,
0xf9, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x1c, 0x20, 0x01, 0xbf, 0xc4, 0xbc, 0x7c, 0x21,
0x21, 0x2e, 0x96, 0x9c, 0xcc, 0xbc, 0x54, 0x09, 0x66, 0x05, 0x46, 0x0d, 0x9e, 0x20, 0x30, 0x5b,
0x48, 0x82, 0x8b, 0xbd, 0x20, 0xb1, 0xa8, 0x24, 0x33, 0x31, 0x47, 0x82, 0x45, 0x81, 0x51, 0x83,
0x23, 0x08, 0xc6, 0x75, 0xe2, 0x39, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f,
0xe4, 0x18, 0x93, 0xd8, 0xc0, 0x6e, 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x2d, 0x24, 0x5a,
0xd4, 0x92, 0x00, 0x00, 0x00,
}
@@ -0,0 +1,8 @@
syntax = "proto3";
message LogEntry {
string source = 1;
int64 time_nano = 2;
bytes line = 3;
bool partial = 4;
}
@@ -0,0 +1,3 @@
//go:generate protoc --gogofast_out=import_path=github.com/docker/docker/api/types/plugins/logdriver:. entry.proto
package logdriver
@@ -0,0 +1,87 @@
package logdriver
import (
"encoding/binary"
"io"
)
const binaryEncodeLen = 4
// LogEntryEncoder encodes a LogEntry to a protobuf stream
// The stream should look like:
//
// [uint32 binary encoded message size][protobuf message]
//
// To decode an entry, read the first 4 bytes to get the size of the entry,
// then read `size` bytes from the stream.
type LogEntryEncoder interface {
Encode(*LogEntry) error
}
// NewLogEntryEncoder creates a protobuf stream encoder for log entries.
// This is used to write out log entries to a stream.
func NewLogEntryEncoder(w io.Writer) LogEntryEncoder {
return &logEntryEncoder{
w: w,
buf: make([]byte, 1024),
}
}
type logEntryEncoder struct {
buf []byte
w io.Writer
}
func (e *logEntryEncoder) Encode(l *LogEntry) error {
n := l.Size()
total := n + binaryEncodeLen
if total > len(e.buf) {
e.buf = make([]byte, total)
}
binary.BigEndian.PutUint32(e.buf, uint32(n))
if _, err := l.MarshalTo(e.buf[binaryEncodeLen:]); err != nil {
return err
}
_, err := e.w.Write(e.buf[:total])
return err
}
// LogEntryDecoder decodes log entries from a stream
// It is expected that the wire format is as defined by LogEntryEncoder.
type LogEntryDecoder interface {
Decode(*LogEntry) error
}
// NewLogEntryDecoder creates a new stream decoder for log entries
func NewLogEntryDecoder(r io.Reader) LogEntryDecoder {
return &logEntryDecoder{
lenBuf: make([]byte, binaryEncodeLen),
buf: make([]byte, 1024),
r: r,
}
}
type logEntryDecoder struct {
r io.Reader
lenBuf []byte
buf []byte
}
func (d *logEntryDecoder) Decode(l *LogEntry) error {
_, err := io.ReadFull(d.r, d.lenBuf)
if err != nil {
return err
}
size := int(binary.BigEndian.Uint32(d.lenBuf))
if len(d.buf) < size {
d.buf = make([]byte, size)
}
if _, err := io.ReadFull(d.r, d.buf[:size]); err != nil {
return err
}
return l.Unmarshal(d.buf[:size])
}