Merge pull request #1148 from vieux/add_container_mesos

add container name in mesos task label and update mesos-go
This commit is contained in:
Chanwit Kaewkasi 2015-08-14 20:53:35 -04:00
commit 918c7a0466
69 changed files with 25125 additions and 8967 deletions

29
Godeps/Godeps.json generated
View File

@ -5,11 +5,6 @@
"./..." "./..."
], ],
"Deps": [ "Deps": [
{
"ImportPath": "code.google.com/p/go-uuid/uuid",
"Comment": "null-15",
"Rev": "35bc42037350f0078e3c974c6ea690f1926603ab"
},
{ {
"ImportPath": "github.com/Sirupsen/logrus", "ImportPath": "github.com/Sirupsen/logrus",
"Comment": "v0.6.4-6-g539d4dc", "Comment": "v0.6.4-6-g539d4dc",
@ -56,7 +51,7 @@
}, },
{ {
"ImportPath": "github.com/gogo/protobuf/proto", "ImportPath": "github.com/gogo/protobuf/proto",
"Rev": "bc946d07d1016848dfd2507f90f0859c9471681e" "Rev": "8edb24c179ed858a38f18920d9005c2dde05ec17"
}, },
{ {
"ImportPath": "github.com/golang/glog", "ImportPath": "github.com/golang/glog",
@ -77,35 +72,35 @@
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/auth", "ImportPath": "github.com/mesos/mesos-go/auth",
"Rev": "83b52d7f648d2a2fa91330123d348d4090be2aed" "Rev": "d23a18b51b1ddf4072a223b38cc8d05cffb1ea42"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/detector", "ImportPath": "github.com/mesos/mesos-go/detector",
"Rev": "83b52d7f648d2a2fa91330123d348d4090be2aed" "Rev": "d23a18b51b1ddf4072a223b38cc8d05cffb1ea42"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/mesosproto", "ImportPath": "github.com/mesos/mesos-go/mesosproto",
"Rev": "83b52d7f648d2a2fa91330123d348d4090be2aed" "Rev": "d23a18b51b1ddf4072a223b38cc8d05cffb1ea42"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/mesosutil", "ImportPath": "github.com/mesos/mesos-go/mesosutil",
"Rev": "83b52d7f648d2a2fa91330123d348d4090be2aed" "Rev": "d23a18b51b1ddf4072a223b38cc8d05cffb1ea42"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/messenger", "ImportPath": "github.com/mesos/mesos-go/messenger",
"Rev": "83b52d7f648d2a2fa91330123d348d4090be2aed" "Rev": "d23a18b51b1ddf4072a223b38cc8d05cffb1ea42"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/scheduler", "ImportPath": "github.com/mesos/mesos-go/scheduler",
"Rev": "83b52d7f648d2a2fa91330123d348d4090be2aed" "Rev": "d23a18b51b1ddf4072a223b38cc8d05cffb1ea42"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/upid", "ImportPath": "github.com/mesos/mesos-go/upid",
"Rev": "83b52d7f648d2a2fa91330123d348d4090be2aed" "Rev": "d23a18b51b1ddf4072a223b38cc8d05cffb1ea42"
}, },
{ {
"ImportPath": "github.com/skarademir/naturalsort", "ImportPath": "github.com/pborman/uuid",
"Rev": "69a5d87bef620f77ee8508db30c846b3b84b111e" "Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4"
}, },
{ {
"ImportPath": "github.com/samalba/dockerclient", "ImportPath": "github.com/samalba/dockerclient",
@ -115,6 +110,10 @@
"ImportPath": "github.com/samuel/go-zookeeper/zk", "ImportPath": "github.com/samuel/go-zookeeper/zk",
"Rev": "fa6674abf3f4580b946a01bf7a1ce4ba8766205b" "Rev": "fa6674abf3f4580b946a01bf7a1ce4ba8766205b"
}, },
{
"ImportPath": "github.com/skarademir/naturalsort",
"Rev": "69a5d87bef620f77ee8508db30c846b3b84b111e"
},
{ {
"ImportPath": "github.com/stretchr/objx", "ImportPath": "github.com/stretchr/objx",
"Rev": "cbeaeb16a013161a98496fad62933b1d21786672" "Rev": "cbeaeb16a013161a98496fad62933b1d21786672"

View File

@ -37,4 +37,7 @@ test: install generate-test-pbs
generate-test-pbs: generate-test-pbs:
make install && cd testdata && make make install
make -C testdata
protoc-min-version --version="3.0.0" --proto_path=.:../../../../ --gogo_out=. proto3_proto/proto3.proto
make

View File

@ -44,8 +44,8 @@ import (
"testing" "testing"
"time" "time"
. "./testdata"
. "github.com/gogo/protobuf/proto" . "github.com/gogo/protobuf/proto"
. "github.com/gogo/protobuf/proto/testdata"
) )
var globalO *Buffer var globalO *Buffer
@ -1252,7 +1252,8 @@ func TestProto1RepeatedGroup(t *testing.T) {
} }
o := old() o := old()
if err := o.Marshal(pb); err != ErrRepeatedHasNil { err := o.Marshal(pb)
if err == nil || !strings.Contains(err.Error(), "repeated field Message has nil") {
t.Fatalf("unexpected or no error when marshaling: %v", err) t.Fatalf("unexpected or no error when marshaling: %v", err)
} }
} }
@ -1441,6 +1442,17 @@ func TestSetDefaultsWithRepeatedSubMessage(t *testing.T) {
} }
} }
func TestSetDefaultWithRepeatedNonMessage(t *testing.T) {
m := &MyMessage{
Pet: []string{"turtle", "wombat"},
}
expected := Clone(m)
SetDefaults(m)
if !Equal(m, expected) {
t.Errorf("\n got %v\nwant %v", m, expected)
}
}
func TestMaximumTagNumber(t *testing.T) { func TestMaximumTagNumber(t *testing.T) {
m := &MaxTag{ m := &MaxTag{
LastField: String("natural goat essence"), LastField: String("natural goat essence"),
@ -1833,6 +1845,98 @@ func fuzzUnmarshal(t *testing.T, data []byte) {
Unmarshal(data, pb) Unmarshal(data, pb)
} }
func TestMapFieldMarshal(t *testing.T) {
m := &MessageWithMap{
NameMapping: map[int32]string{
1: "Rob",
4: "Ian",
8: "Dave",
},
}
b, err := Marshal(m)
if err != nil {
t.Fatalf("Marshal: %v", err)
}
// b should be the concatenation of these three byte sequences in some order.
parts := []string{
"\n\a\b\x01\x12\x03Rob",
"\n\a\b\x04\x12\x03Ian",
"\n\b\b\x08\x12\x04Dave",
}
ok := false
for i := range parts {
for j := range parts {
if j == i {
continue
}
for k := range parts {
if k == i || k == j {
continue
}
try := parts[i] + parts[j] + parts[k]
if bytes.Equal(b, []byte(try)) {
ok = true
break
}
}
}
}
if !ok {
t.Fatalf("Incorrect Marshal output.\n got %q\nwant %q (or a permutation of that)", b, parts[0]+parts[1]+parts[2])
}
t.Logf("FYI b: %q", b)
(new(Buffer)).DebugPrint("Dump of b", b)
}
func TestMapFieldRoundTrips(t *testing.T) {
m := &MessageWithMap{
NameMapping: map[int32]string{
1: "Rob",
4: "Ian",
8: "Dave",
},
MsgMapping: map[int64]*FloatingPoint{
0x7001: {F: Float64(2.0)},
},
ByteMapping: map[bool][]byte{
false: []byte("that's not right!"),
true: []byte("aye, 'tis true!"),
},
}
b, err := Marshal(m)
if err != nil {
t.Fatalf("Marshal: %v", err)
}
t.Logf("FYI b: %q", b)
m2 := new(MessageWithMap)
if err := Unmarshal(b, m2); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
for _, pair := range [][2]interface{}{
{m.NameMapping, m2.NameMapping},
{m.MsgMapping, m2.MsgMapping},
{m.ByteMapping, m2.ByteMapping},
} {
if !reflect.DeepEqual(pair[0], pair[1]) {
t.Errorf("Map did not survive a round trip.\ninitial: %v\n final: %v", pair[0], pair[1])
}
}
}
func TestMapFieldWithNil(t *testing.T) {
m := &MessageWithMap{
MsgMapping: map[int64]*FloatingPoint{
1: nil,
},
}
b, err := Marshal(m)
if err == nil {
t.Fatalf("Marshal of bad map should have failed, got these bytes: %v", b)
}
}
// Benchmarks // Benchmarks
func testMsg() *GoTest { func testMsg() *GoTest {

View File

@ -29,7 +29,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Protocol buffer deep copy. // Protocol buffer deep copy and merge.
// TODO: MessageSet and RawMessage. // TODO: MessageSet and RawMessage.
package proto package proto
@ -75,12 +75,13 @@ func Merge(dst, src Message) {
} }
func mergeStruct(out, in reflect.Value) { func mergeStruct(out, in reflect.Value) {
sprop := GetProperties(in.Type())
for i := 0; i < in.NumField(); i++ { for i := 0; i < in.NumField(); i++ {
f := in.Type().Field(i) f := in.Type().Field(i)
if strings.HasPrefix(f.Name, "XXX_") { if strings.HasPrefix(f.Name, "XXX_") {
continue continue
} }
mergeAny(out.Field(i), in.Field(i)) mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i])
} }
if emIn, ok := in.Addr().Interface().(extensionsMap); ok { if emIn, ok := in.Addr().Interface().(extensionsMap); ok {
@ -103,7 +104,10 @@ func mergeStruct(out, in reflect.Value) {
} }
} }
func mergeAny(out, in reflect.Value) { // mergeAny performs a merge between two values of the same type.
// viaPtr indicates whether the values were indirected through a pointer (implying proto2).
// prop is set if this is a struct field (it may be nil).
func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) {
if in.Type() == protoMessageType { if in.Type() == protoMessageType {
if !in.IsNil() { if !in.IsNil() {
if out.IsNil() { if out.IsNil() {
@ -117,7 +121,33 @@ func mergeAny(out, in reflect.Value) {
switch in.Kind() { switch in.Kind() {
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
reflect.String, reflect.Uint32, reflect.Uint64: reflect.String, reflect.Uint32, reflect.Uint64:
if !viaPtr && isProto3Zero(in) {
return
}
out.Set(in) out.Set(in)
case reflect.Map:
if in.Len() == 0 {
return
}
if out.IsNil() {
out.Set(reflect.MakeMap(in.Type()))
}
// For maps with value types of *T or []byte we need to deep copy each value.
elemKind := in.Type().Elem().Kind()
for _, key := range in.MapKeys() {
var val reflect.Value
switch elemKind {
case reflect.Ptr:
val = reflect.New(in.Type().Elem().Elem())
mergeAny(val, in.MapIndex(key), false, nil)
case reflect.Slice:
val = in.MapIndex(key)
val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
default:
val = in.MapIndex(key)
}
out.SetMapIndex(key, val)
}
case reflect.Ptr: case reflect.Ptr:
if in.IsNil() { if in.IsNil() {
return return
@ -125,13 +155,21 @@ func mergeAny(out, in reflect.Value) {
if out.IsNil() { if out.IsNil() {
out.Set(reflect.New(in.Elem().Type())) out.Set(reflect.New(in.Elem().Type()))
} }
mergeAny(out.Elem(), in.Elem()) mergeAny(out.Elem(), in.Elem(), true, nil)
case reflect.Slice: case reflect.Slice:
if in.IsNil() { if in.IsNil() {
return return
} }
if in.Type().Elem().Kind() == reflect.Uint8 { if in.Type().Elem().Kind() == reflect.Uint8 {
// []byte is a scalar bytes field, not a repeated field. // []byte is a scalar bytes field, not a repeated field.
// Edge case: if this is in a proto3 message, a zero length
// bytes field is considered the zero value, and should not
// be merged.
if prop != nil && prop.proto3 && in.Len() == 0 {
return
}
// Make a deep copy. // Make a deep copy.
// Append to []byte{} instead of []byte(nil) so that we never end up // Append to []byte{} instead of []byte(nil) so that we never end up
// with a nil result. // with a nil result.
@ -149,7 +187,7 @@ func mergeAny(out, in reflect.Value) {
default: default:
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
x := reflect.Indirect(reflect.New(in.Type().Elem())) x := reflect.Indirect(reflect.New(in.Type().Elem()))
mergeAny(x, in.Index(i)) mergeAny(x, in.Index(i), false, nil)
out.Set(reflect.Append(out, x)) out.Set(reflect.Append(out, x))
} }
} }
@ -166,7 +204,7 @@ func mergeExtension(out, in map[int32]Extension) {
eOut := Extension{desc: eIn.desc} eOut := Extension{desc: eIn.desc}
if eIn.value != nil { if eIn.value != nil {
v := reflect.New(reflect.TypeOf(eIn.value)).Elem() v := reflect.New(reflect.TypeOf(eIn.value)).Elem()
mergeAny(v, reflect.ValueOf(eIn.value)) mergeAny(v, reflect.ValueOf(eIn.value), false, nil)
eOut.value = v.Interface() eOut.value = v.Interface()
} }
if eIn.enc != nil { if eIn.enc != nil {

View File

@ -36,7 +36,8 @@ import (
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
pb "./testdata" proto3pb "github.com/gogo/protobuf/proto/proto3_proto"
pb "github.com/gogo/protobuf/proto/testdata"
) )
var cloneTestMessage = &pb.MyMessage{ var cloneTestMessage = &pb.MyMessage{
@ -189,6 +190,48 @@ var mergeTests = []struct {
dst: &pb.OtherMessage{Value: []byte("bar")}, dst: &pb.OtherMessage{Value: []byte("bar")},
want: &pb.OtherMessage{Value: []byte("foo")}, want: &pb.OtherMessage{Value: []byte("foo")},
}, },
{
src: &pb.MessageWithMap{
NameMapping: map[int32]string{6: "Nigel"},
MsgMapping: map[int64]*pb.FloatingPoint{
0x4001: {F: proto.Float64(2.0)},
},
ByteMapping: map[bool][]byte{true: []byte("wowsa")},
},
dst: &pb.MessageWithMap{
NameMapping: map[int32]string{
6: "Bruce", // should be overwritten
7: "Andrew",
},
},
want: &pb.MessageWithMap{
NameMapping: map[int32]string{
6: "Nigel",
7: "Andrew",
},
MsgMapping: map[int64]*pb.FloatingPoint{
0x4001: {F: proto.Float64(2.0)},
},
ByteMapping: map[bool][]byte{true: []byte("wowsa")},
},
},
// proto3 shouldn't merge zero values,
// in the same way that proto2 shouldn't merge nils.
{
src: &proto3pb.Message{
Name: "Aaron",
Data: []byte(""), // zero value, but not nil
},
dst: &proto3pb.Message{
HeightInCm: 176,
Data: []byte("texas!"),
},
want: &proto3pb.Message{
Name: "Aaron",
HeightInCm: 176,
Data: []byte("texas!"),
},
},
} }
func TestMerge(t *testing.T) { func TestMerge(t *testing.T) {

View File

@ -178,7 +178,7 @@ func (p *Buffer) DecodeZigzag32() (x uint64, err error) {
func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) { func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) {
n, err := p.DecodeVarint() n, err := p.DecodeVarint()
if err != nil { if err != nil {
return return nil, err
} }
nb := int(n) nb := int(n)
@ -470,6 +470,15 @@ func (o *Buffer) dec_bool(p *Properties, base structPointer) error {
return nil return nil
} }
func (o *Buffer) dec_proto3_bool(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
*structPointer_BoolVal(base, p.field) = u != 0
return nil
}
// Decode an int32. // Decode an int32.
func (o *Buffer) dec_int32(p *Properties, base structPointer) error { func (o *Buffer) dec_int32(p *Properties, base structPointer) error {
u, err := p.valDec(o) u, err := p.valDec(o)
@ -480,6 +489,15 @@ func (o *Buffer) dec_int32(p *Properties, base structPointer) error {
return nil return nil
} }
func (o *Buffer) dec_proto3_int32(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
word32Val_Set(structPointer_Word32Val(base, p.field), uint32(u))
return nil
}
// Decode an int64. // Decode an int64.
func (o *Buffer) dec_int64(p *Properties, base structPointer) error { func (o *Buffer) dec_int64(p *Properties, base structPointer) error {
u, err := p.valDec(o) u, err := p.valDec(o)
@ -490,15 +508,31 @@ func (o *Buffer) dec_int64(p *Properties, base structPointer) error {
return nil return nil
} }
func (o *Buffer) dec_proto3_int64(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
word64Val_Set(structPointer_Word64Val(base, p.field), o, u)
return nil
}
// Decode a string. // Decode a string.
func (o *Buffer) dec_string(p *Properties, base structPointer) error { func (o *Buffer) dec_string(p *Properties, base structPointer) error {
s, err := o.DecodeStringBytes() s, err := o.DecodeStringBytes()
if err != nil { if err != nil {
return err return err
} }
sp := new(string) *structPointer_String(base, p.field) = &s
*sp = s return nil
*structPointer_String(base, p.field) = sp }
func (o *Buffer) dec_proto3_string(p *Properties, base structPointer) error {
s, err := o.DecodeStringBytes()
if err != nil {
return err
}
*structPointer_StringVal(base, p.field) = s
return nil return nil
} }
@ -637,6 +671,78 @@ func (o *Buffer) dec_slice_slice_byte(p *Properties, base structPointer) error {
return nil return nil
} }
// Decode a map field.
func (o *Buffer) dec_new_map(p *Properties, base structPointer) error {
raw, err := o.DecodeRawBytes(false)
if err != nil {
return err
}
oi := o.index // index at the end of this map entry
o.index -= len(raw) // move buffer back to start of map entry
mptr := structPointer_Map(base, p.field, p.mtype) // *map[K]V
if mptr.Elem().IsNil() {
mptr.Elem().Set(reflect.MakeMap(mptr.Type().Elem()))
}
v := mptr.Elem() // map[K]V
// Prepare addressable doubly-indirect placeholders for the key and value types.
// See enc_new_map for why.
keyptr := reflect.New(reflect.PtrTo(p.mtype.Key())).Elem() // addressable *K
keybase := toStructPointer(keyptr.Addr()) // **K
var valbase structPointer
var valptr reflect.Value
switch p.mtype.Elem().Kind() {
case reflect.Slice:
// []byte
var dummy []byte
valptr = reflect.ValueOf(&dummy) // *[]byte
valbase = toStructPointer(valptr) // *[]byte
case reflect.Ptr:
// message; valptr is **Msg; need to allocate the intermediate pointer
valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
valptr.Set(reflect.New(valptr.Type().Elem()))
valbase = toStructPointer(valptr)
default:
// everything else
valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
valbase = toStructPointer(valptr.Addr()) // **V
}
// Decode.
// This parses a restricted wire format, namely the encoding of a message
// with two fields. See enc_new_map for the format.
for o.index < oi {
// tagcode for key and value properties are always a single byte
// because they have tags 1 and 2.
tagcode := o.buf[o.index]
o.index++
switch tagcode {
case p.mkeyprop.tagcode[0]:
if err := p.mkeyprop.dec(o, p.mkeyprop, keybase); err != nil {
return err
}
case p.mvalprop.tagcode[0]:
if err := p.mvalprop.dec(o, p.mvalprop, valbase); err != nil {
return err
}
default:
// TODO: Should we silently skip this instead?
return fmt.Errorf("proto: bad map data tag %d", raw[0])
}
}
keyelem, valelem := keyptr.Elem(), valptr.Elem()
if !keyelem.IsValid() || !valelem.IsValid() {
// We did not decode the key or the value in the map entry.
// Either way, it's an invalid map entry.
return fmt.Errorf("proto: bad map data: missing key/val")
}
v.SetMapIndex(keyelem, valelem)
return nil
}
// Decode a group. // Decode a group.
func (o *Buffer) dec_struct_group(p *Properties, base structPointer) error { func (o *Buffer) dec_struct_group(p *Properties, base structPointer) error {
bas := structPointer_GetStructPointer(base, p.field) bas := structPointer_GetStructPointer(base, p.field)

View File

@ -30,51 +30,6 @@ import (
"reflect" "reflect"
) )
// Decode a reference to a bool pointer.
func (o *Buffer) dec_ref_bool(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
if len(o.bools) == 0 {
o.bools = make([]bool, boolPoolSize)
}
o.bools[0] = u != 0
*structPointer_RefBool(base, p.field) = o.bools[0]
o.bools = o.bools[1:]
return nil
}
// Decode a reference to an int32 pointer.
func (o *Buffer) dec_ref_int32(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
refWord32_Set(structPointer_RefWord32(base, p.field), o, uint32(u))
return nil
}
// Decode a reference to an int64 pointer.
func (o *Buffer) dec_ref_int64(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
refWord64_Set(structPointer_RefWord64(base, p.field), o, u)
return nil
}
// Decode a reference to a string pointer.
func (o *Buffer) dec_ref_string(p *Properties, base structPointer) error {
s, err := o.DecodeStringBytes()
if err != nil {
return err
}
*structPointer_RefString(base, p.field) = s
return nil
}
// Decode a reference to a struct pointer. // Decode a reference to a struct pointer.
func (o *Buffer) dec_ref_struct_message(p *Properties, base structPointer) (err error) { func (o *Buffer) dec_ref_struct_message(p *Properties, base structPointer) (err error) {
raw, e := o.DecodeRawBytes(false) raw, e := o.DecodeRawBytes(false)

View File

@ -60,9 +60,9 @@ func (e *RequiredNotSetError) Error() string {
} }
var ( var (
// ErrRepeatedHasNil is the error returned if Marshal is called with // errRepeatedHasNil is the error returned if Marshal is called with
// a struct with a repeated field containing a nil element. // a struct with a repeated field containing a nil element.
ErrRepeatedHasNil = errors.New("proto: repeated field has nil element") errRepeatedHasNil = errors.New("proto: repeated field has nil element")
// ErrNil is the error returned if Marshal is called with nil. // ErrNil is the error returned if Marshal is called with nil.
ErrNil = errors.New("proto: Marshal called with nil") ErrNil = errors.New("proto: Marshal called with nil")
@ -298,6 +298,16 @@ func (o *Buffer) enc_bool(p *Properties, base structPointer) error {
return nil return nil
} }
func (o *Buffer) enc_proto3_bool(p *Properties, base structPointer) error {
v := *structPointer_BoolVal(base, p.field)
if !v {
return ErrNil
}
o.buf = append(o.buf, p.tagcode...)
p.valEnc(o, 1)
return nil
}
func size_bool(p *Properties, base structPointer) int { func size_bool(p *Properties, base structPointer) int {
v := *structPointer_Bool(base, p.field) v := *structPointer_Bool(base, p.field)
if v == nil { if v == nil {
@ -306,6 +316,14 @@ func size_bool(p *Properties, base structPointer) int {
return len(p.tagcode) + 1 // each bool takes exactly one byte return len(p.tagcode) + 1 // each bool takes exactly one byte
} }
func size_proto3_bool(p *Properties, base structPointer) int {
v := *structPointer_BoolVal(base, p.field)
if !v {
return 0
}
return len(p.tagcode) + 1 // each bool takes exactly one byte
}
// Encode an int32. // Encode an int32.
func (o *Buffer) enc_int32(p *Properties, base structPointer) error { func (o *Buffer) enc_int32(p *Properties, base structPointer) error {
v := structPointer_Word32(base, p.field) v := structPointer_Word32(base, p.field)
@ -318,6 +336,17 @@ func (o *Buffer) enc_int32(p *Properties, base structPointer) error {
return nil return nil
} }
func (o *Buffer) enc_proto3_int32(p *Properties, base structPointer) error {
v := structPointer_Word32Val(base, p.field)
x := int32(word32Val_Get(v)) // permit sign extension to use full 64-bit range
if x == 0 {
return ErrNil
}
o.buf = append(o.buf, p.tagcode...)
p.valEnc(o, uint64(x))
return nil
}
func size_int32(p *Properties, base structPointer) (n int) { func size_int32(p *Properties, base structPointer) (n int) {
v := structPointer_Word32(base, p.field) v := structPointer_Word32(base, p.field)
if word32_IsNil(v) { if word32_IsNil(v) {
@ -329,6 +358,17 @@ func size_int32(p *Properties, base structPointer) (n int) {
return return
} }
func size_proto3_int32(p *Properties, base structPointer) (n int) {
v := structPointer_Word32Val(base, p.field)
x := int32(word32Val_Get(v)) // permit sign extension to use full 64-bit range
if x == 0 {
return 0
}
n += len(p.tagcode)
n += p.valSize(uint64(x))
return
}
// Encode a uint32. // Encode a uint32.
// Exactly the same as int32, except for no sign extension. // Exactly the same as int32, except for no sign extension.
func (o *Buffer) enc_uint32(p *Properties, base structPointer) error { func (o *Buffer) enc_uint32(p *Properties, base structPointer) error {
@ -342,6 +382,17 @@ func (o *Buffer) enc_uint32(p *Properties, base structPointer) error {
return nil return nil
} }
func (o *Buffer) enc_proto3_uint32(p *Properties, base structPointer) error {
v := structPointer_Word32Val(base, p.field)
x := word32Val_Get(v)
if x == 0 {
return ErrNil
}
o.buf = append(o.buf, p.tagcode...)
p.valEnc(o, uint64(x))
return nil
}
func size_uint32(p *Properties, base structPointer) (n int) { func size_uint32(p *Properties, base structPointer) (n int) {
v := structPointer_Word32(base, p.field) v := structPointer_Word32(base, p.field)
if word32_IsNil(v) { if word32_IsNil(v) {
@ -353,6 +404,17 @@ func size_uint32(p *Properties, base structPointer) (n int) {
return return
} }
func size_proto3_uint32(p *Properties, base structPointer) (n int) {
v := structPointer_Word32Val(base, p.field)
x := word32Val_Get(v)
if x == 0 {
return 0
}
n += len(p.tagcode)
n += p.valSize(uint64(x))
return
}
// Encode an int64. // Encode an int64.
func (o *Buffer) enc_int64(p *Properties, base structPointer) error { func (o *Buffer) enc_int64(p *Properties, base structPointer) error {
v := structPointer_Word64(base, p.field) v := structPointer_Word64(base, p.field)
@ -365,6 +427,17 @@ func (o *Buffer) enc_int64(p *Properties, base structPointer) error {
return nil return nil
} }
func (o *Buffer) enc_proto3_int64(p *Properties, base structPointer) error {
v := structPointer_Word64Val(base, p.field)
x := word64Val_Get(v)
if x == 0 {
return ErrNil
}
o.buf = append(o.buf, p.tagcode...)
p.valEnc(o, x)
return nil
}
func size_int64(p *Properties, base structPointer) (n int) { func size_int64(p *Properties, base structPointer) (n int) {
v := structPointer_Word64(base, p.field) v := structPointer_Word64(base, p.field)
if word64_IsNil(v) { if word64_IsNil(v) {
@ -376,6 +449,17 @@ func size_int64(p *Properties, base structPointer) (n int) {
return return
} }
func size_proto3_int64(p *Properties, base structPointer) (n int) {
v := structPointer_Word64Val(base, p.field)
x := word64Val_Get(v)
if x == 0 {
return 0
}
n += len(p.tagcode)
n += p.valSize(x)
return
}
// Encode a string. // Encode a string.
func (o *Buffer) enc_string(p *Properties, base structPointer) error { func (o *Buffer) enc_string(p *Properties, base structPointer) error {
v := *structPointer_String(base, p.field) v := *structPointer_String(base, p.field)
@ -388,6 +472,16 @@ func (o *Buffer) enc_string(p *Properties, base structPointer) error {
return nil return nil
} }
func (o *Buffer) enc_proto3_string(p *Properties, base structPointer) error {
v := *structPointer_StringVal(base, p.field)
if v == "" {
return ErrNil
}
o.buf = append(o.buf, p.tagcode...)
o.EncodeStringBytes(v)
return nil
}
func size_string(p *Properties, base structPointer) (n int) { func size_string(p *Properties, base structPointer) (n int) {
v := *structPointer_String(base, p.field) v := *structPointer_String(base, p.field)
if v == nil { if v == nil {
@ -399,6 +493,16 @@ func size_string(p *Properties, base structPointer) (n int) {
return return
} }
func size_proto3_string(p *Properties, base structPointer) (n int) {
v := *structPointer_StringVal(base, p.field)
if v == "" {
return 0
}
n += len(p.tagcode)
n += sizeStringBytes(v)
return
}
// All protocol buffer fields are nillable, but be careful. // All protocol buffer fields are nillable, but be careful.
func isNil(v reflect.Value) bool { func isNil(v reflect.Value) bool {
switch v.Kind() { switch v.Kind() {
@ -551,6 +655,16 @@ func (o *Buffer) enc_slice_byte(p *Properties, base structPointer) error {
return nil return nil
} }
func (o *Buffer) enc_proto3_slice_byte(p *Properties, base structPointer) error {
s := *structPointer_Bytes(base, p.field)
if len(s) == 0 {
return ErrNil
}
o.buf = append(o.buf, p.tagcode...)
o.EncodeRawBytes(s)
return nil
}
func size_slice_byte(p *Properties, base structPointer) (n int) { func size_slice_byte(p *Properties, base structPointer) (n int) {
s := *structPointer_Bytes(base, p.field) s := *structPointer_Bytes(base, p.field)
if s == nil { if s == nil {
@ -561,6 +675,16 @@ func size_slice_byte(p *Properties, base structPointer) (n int) {
return return
} }
func size_proto3_slice_byte(p *Properties, base structPointer) (n int) {
s := *structPointer_Bytes(base, p.field)
if len(s) == 0 {
return 0
}
n += len(p.tagcode)
n += sizeRawBytes(s)
return
}
// Encode a slice of int32s ([]int32). // Encode a slice of int32s ([]int32).
func (o *Buffer) enc_slice_int32(p *Properties, base structPointer) error { func (o *Buffer) enc_slice_int32(p *Properties, base structPointer) error {
s := structPointer_Word32Slice(base, p.field) s := structPointer_Word32Slice(base, p.field)
@ -815,7 +939,7 @@ func (o *Buffer) enc_slice_struct_message(p *Properties, base structPointer) err
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
structp := s.Index(i) structp := s.Index(i)
if structPointer_IsNil(structp) { if structPointer_IsNil(structp) {
return ErrRepeatedHasNil return errRepeatedHasNil
} }
// Can the object marshal itself? // Can the object marshal itself?
@ -834,7 +958,7 @@ func (o *Buffer) enc_slice_struct_message(p *Properties, base structPointer) err
err := o.enc_len_struct(p.sprop, structp, &state) err := o.enc_len_struct(p.sprop, structp, &state)
if err != nil && !state.shouldContinue(err, nil) { if err != nil && !state.shouldContinue(err, nil) {
if err == ErrNil { if err == ErrNil {
return ErrRepeatedHasNil return errRepeatedHasNil
} }
return err return err
} }
@ -877,7 +1001,7 @@ func (o *Buffer) enc_slice_struct_group(p *Properties, base structPointer) error
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
b := s.Index(i) b := s.Index(i)
if structPointer_IsNil(b) { if structPointer_IsNil(b) {
return ErrRepeatedHasNil return errRepeatedHasNil
} }
o.EncodeVarint(uint64((p.Tag << 3) | WireStartGroup)) o.EncodeVarint(uint64((p.Tag << 3) | WireStartGroup))
@ -886,7 +1010,7 @@ func (o *Buffer) enc_slice_struct_group(p *Properties, base structPointer) error
if err != nil && !state.shouldContinue(err, nil) { if err != nil && !state.shouldContinue(err, nil) {
if err == ErrNil { if err == ErrNil {
return ErrRepeatedHasNil return errRepeatedHasNil
} }
return err return err
} }
@ -945,6 +1069,113 @@ func size_map(p *Properties, base structPointer) int {
return sizeExtensionMap(v) return sizeExtensionMap(v)
} }
// Encode a map field.
func (o *Buffer) enc_new_map(p *Properties, base structPointer) error {
var state errorState // XXX: or do we need to plumb this through?
/*
A map defined as
map<key_type, value_type> map_field = N;
is encoded in the same way as
message MapFieldEntry {
key_type key = 1;
value_type value = 2;
}
repeated MapFieldEntry map_field = N;
*/
v := structPointer_Map(base, p.field, p.mtype).Elem() // map[K]V
if v.Len() == 0 {
return nil
}
keycopy, valcopy, keybase, valbase := mapEncodeScratch(p.mtype)
enc := func() error {
if err := p.mkeyprop.enc(o, p.mkeyprop, keybase); err != nil {
return err
}
if err := p.mvalprop.enc(o, p.mvalprop, valbase); err != nil {
return err
}
return nil
}
keys := v.MapKeys()
sort.Sort(mapKeys(keys))
for _, key := range keys {
val := v.MapIndex(key)
// The only illegal map entry values are nil message pointers.
if val.Kind() == reflect.Ptr && val.IsNil() {
return errors.New("proto: map has nil element")
}
keycopy.Set(key)
valcopy.Set(val)
o.buf = append(o.buf, p.tagcode...)
if err := o.enc_len_thing(enc, &state); err != nil {
return err
}
}
return nil
}
func size_new_map(p *Properties, base structPointer) int {
v := structPointer_Map(base, p.field, p.mtype).Elem() // map[K]V
keycopy, valcopy, keybase, valbase := mapEncodeScratch(p.mtype)
n := 0
for _, key := range v.MapKeys() {
val := v.MapIndex(key)
keycopy.Set(key)
valcopy.Set(val)
// Tag codes for key and val are the responsibility of the sub-sizer.
keysize := p.mkeyprop.size(p.mkeyprop, keybase)
valsize := p.mvalprop.size(p.mvalprop, valbase)
entry := keysize + valsize
// Add on tag code and length of map entry itself.
n += len(p.tagcode) + sizeVarint(uint64(entry)) + entry
}
return n
}
// mapEncodeScratch returns a new reflect.Value matching the map's value type,
// and a structPointer suitable for passing to an encoder or sizer.
func mapEncodeScratch(mapType reflect.Type) (keycopy, valcopy reflect.Value, keybase, valbase structPointer) {
// Prepare addressable doubly-indirect placeholders for the key and value types.
// This is needed because the element-type encoders expect **T, but the map iteration produces T.
keycopy = reflect.New(mapType.Key()).Elem() // addressable K
keyptr := reflect.New(reflect.PtrTo(keycopy.Type())).Elem() // addressable *K
keyptr.Set(keycopy.Addr()) //
keybase = toStructPointer(keyptr.Addr()) // **K
// Value types are more varied and require special handling.
switch mapType.Elem().Kind() {
case reflect.Slice:
// []byte
var dummy []byte
valcopy = reflect.ValueOf(&dummy).Elem() // addressable []byte
valbase = toStructPointer(valcopy.Addr())
case reflect.Ptr:
// message; the generated field type is map[K]*Msg (so V is *Msg),
// so we only need one level of indirection.
valcopy = reflect.New(mapType.Elem()).Elem() // addressable V
valbase = toStructPointer(valcopy.Addr())
default:
// everything else
valcopy = reflect.New(mapType.Elem()).Elem() // addressable V
valptr := reflect.New(reflect.PtrTo(valcopy.Type())).Elem() // addressable *V
valptr.Set(valcopy.Addr()) //
valbase = toStructPointer(valptr.Addr()) // **V
}
return
}
// Encode a struct. // Encode a struct.
func (o *Buffer) enc_struct(prop *StructProperties, base structPointer) error { func (o *Buffer) enc_struct(prop *StructProperties, base structPointer) error {
var state errorState var state errorState
@ -960,6 +1191,9 @@ func (o *Buffer) enc_struct(prop *StructProperties, base structPointer) error {
if p.Required && state.err == nil { if p.Required && state.err == nil {
state.err = &RequiredNotSetError{p.Name} state.err = &RequiredNotSetError{p.Name}
} }
} else if err == errRepeatedHasNil {
// Give more context to nil values in repeated fields.
return errors.New("repeated field " + p.OrigName + " has nil element")
} else if !state.shouldContinue(err, p) { } else if !state.shouldContinue(err, p) {
return err return err
} }
@ -999,10 +1233,15 @@ var zeroes [20]byte // longer than any conceivable sizeVarint
// Encode a struct, preceded by its encoded length (as a varint). // Encode a struct, preceded by its encoded length (as a varint).
func (o *Buffer) enc_len_struct(prop *StructProperties, base structPointer, state *errorState) error { func (o *Buffer) enc_len_struct(prop *StructProperties, base structPointer, state *errorState) error {
return o.enc_len_thing(func() error { return o.enc_struct(prop, base) }, state)
}
// Encode something, preceded by its encoded length (as a varint).
func (o *Buffer) enc_len_thing(enc func() error, state *errorState) error {
iLen := len(o.buf) iLen := len(o.buf)
o.buf = append(o.buf, 0, 0, 0, 0) // reserve four bytes for length o.buf = append(o.buf, 0, 0, 0, 0) // reserve four bytes for length
iMsg := len(o.buf) iMsg := len(o.buf)
err := o.enc_struct(prop, base) err := enc()
if err != nil && !state.shouldContinue(err, nil) { if err != nil && !state.shouldContinue(err, nil) {
return err return err
} }

View File

@ -40,6 +40,10 @@ import (
"reflect" "reflect"
) )
func NewRequiredNotSetError(field string) *RequiredNotSetError {
return &RequiredNotSetError{field}
}
type Sizer interface { type Sizer interface {
Size() int Size() int
} }
@ -64,12 +68,9 @@ func size_ext_slice_byte(p *Properties, base structPointer) (n int) {
// Encode a reference to bool pointer. // Encode a reference to bool pointer.
func (o *Buffer) enc_ref_bool(p *Properties, base structPointer) error { func (o *Buffer) enc_ref_bool(p *Properties, base structPointer) error {
v := structPointer_RefBool(base, p.field) v := *structPointer_BoolVal(base, p.field)
if v == nil {
return ErrNil
}
x := 0 x := 0
if *v { if v {
x = 1 x = 1
} }
o.buf = append(o.buf, p.tagcode...) o.buf = append(o.buf, p.tagcode...)
@ -78,53 +79,37 @@ func (o *Buffer) enc_ref_bool(p *Properties, base structPointer) error {
} }
func size_ref_bool(p *Properties, base structPointer) int { func size_ref_bool(p *Properties, base structPointer) int {
v := structPointer_RefBool(base, p.field)
if v == nil {
return 0
}
return len(p.tagcode) + 1 // each bool takes exactly one byte return len(p.tagcode) + 1 // each bool takes exactly one byte
} }
// Encode a reference to int32 pointer. // Encode a reference to int32 pointer.
func (o *Buffer) enc_ref_int32(p *Properties, base structPointer) error { func (o *Buffer) enc_ref_int32(p *Properties, base structPointer) error {
v := structPointer_RefWord32(base, p.field) v := structPointer_Word32Val(base, p.field)
if refWord32_IsNil(v) { x := int32(word32Val_Get(v))
return ErrNil
}
x := int32(refWord32_Get(v))
o.buf = append(o.buf, p.tagcode...) o.buf = append(o.buf, p.tagcode...)
p.valEnc(o, uint64(x)) p.valEnc(o, uint64(x))
return nil return nil
} }
func size_ref_int32(p *Properties, base structPointer) (n int) { func size_ref_int32(p *Properties, base structPointer) (n int) {
v := structPointer_RefWord32(base, p.field) v := structPointer_Word32Val(base, p.field)
if refWord32_IsNil(v) { x := int32(word32Val_Get(v))
return 0
}
x := int32(refWord32_Get(v))
n += len(p.tagcode) n += len(p.tagcode)
n += p.valSize(uint64(x)) n += p.valSize(uint64(x))
return return
} }
func (o *Buffer) enc_ref_uint32(p *Properties, base structPointer) error { func (o *Buffer) enc_ref_uint32(p *Properties, base structPointer) error {
v := structPointer_RefWord32(base, p.field) v := structPointer_Word32Val(base, p.field)
if refWord32_IsNil(v) { x := word32Val_Get(v)
return ErrNil
}
x := refWord32_Get(v)
o.buf = append(o.buf, p.tagcode...) o.buf = append(o.buf, p.tagcode...)
p.valEnc(o, uint64(x)) p.valEnc(o, uint64(x))
return nil return nil
} }
func size_ref_uint32(p *Properties, base structPointer) (n int) { func size_ref_uint32(p *Properties, base structPointer) (n int) {
v := structPointer_RefWord32(base, p.field) v := structPointer_Word32Val(base, p.field)
if refWord32_IsNil(v) { x := word32Val_Get(v)
return 0
}
x := refWord32_Get(v)
n += len(p.tagcode) n += len(p.tagcode)
n += p.valSize(uint64(x)) n += p.valSize(uint64(x))
return return
@ -132,22 +117,16 @@ func size_ref_uint32(p *Properties, base structPointer) (n int) {
// Encode a reference to an int64 pointer. // Encode a reference to an int64 pointer.
func (o *Buffer) enc_ref_int64(p *Properties, base structPointer) error { func (o *Buffer) enc_ref_int64(p *Properties, base structPointer) error {
v := structPointer_RefWord64(base, p.field) v := structPointer_Word64Val(base, p.field)
if refWord64_IsNil(v) { x := word64Val_Get(v)
return ErrNil
}
x := refWord64_Get(v)
o.buf = append(o.buf, p.tagcode...) o.buf = append(o.buf, p.tagcode...)
p.valEnc(o, x) p.valEnc(o, x)
return nil return nil
} }
func size_ref_int64(p *Properties, base structPointer) (n int) { func size_ref_int64(p *Properties, base structPointer) (n int) {
v := structPointer_RefWord64(base, p.field) v := structPointer_Word64Val(base, p.field)
if refWord64_IsNil(v) { x := word64Val_Get(v)
return 0
}
x := refWord64_Get(v)
n += len(p.tagcode) n += len(p.tagcode)
n += p.valSize(x) n += p.valSize(x)
return return
@ -155,24 +134,16 @@ func size_ref_int64(p *Properties, base structPointer) (n int) {
// Encode a reference to a string pointer. // Encode a reference to a string pointer.
func (o *Buffer) enc_ref_string(p *Properties, base structPointer) error { func (o *Buffer) enc_ref_string(p *Properties, base structPointer) error {
v := structPointer_RefString(base, p.field) v := *structPointer_StringVal(base, p.field)
if v == nil {
return ErrNil
}
x := *v
o.buf = append(o.buf, p.tagcode...) o.buf = append(o.buf, p.tagcode...)
o.EncodeStringBytes(x) o.EncodeStringBytes(v)
return nil return nil
} }
func size_ref_string(p *Properties, base structPointer) (n int) { func size_ref_string(p *Properties, base structPointer) (n int) {
v := structPointer_RefString(base, p.field) v := *structPointer_StringVal(base, p.field)
if v == nil {
return 0
}
x := *v
n += len(p.tagcode) n += len(p.tagcode)
n += sizeStringBytes(x) n += sizeStringBytes(v)
return return
} }
@ -232,7 +203,7 @@ func (o *Buffer) enc_slice_ref_struct_message(p *Properties, base structPointer)
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
structp := structPointer_Add(ss1, field(uintptr(i)*size)) structp := structPointer_Add(ss1, field(uintptr(i)*size))
if structPointer_IsNil(structp) { if structPointer_IsNil(structp) {
return ErrRepeatedHasNil return errRepeatedHasNil
} }
// Can the object marshal itself? // Can the object marshal itself?
@ -251,7 +222,7 @@ func (o *Buffer) enc_slice_ref_struct_message(p *Properties, base structPointer)
err := o.enc_len_struct(p.sprop, structp, &state) err := o.enc_len_struct(p.sprop, structp, &state)
if err != nil && !state.shouldContinue(err, nil) { if err != nil && !state.shouldContinue(err, nil) {
if err == ErrNil { if err == ErrNil {
return ErrRepeatedHasNil return errRepeatedHasNil
} }
return err return err
} }

View File

@ -154,6 +154,21 @@ func equalAny(v1, v2 reflect.Value) bool {
return v1.Float() == v2.Float() return v1.Float() == v2.Float()
case reflect.Int32, reflect.Int64: case reflect.Int32, reflect.Int64:
return v1.Int() == v2.Int() return v1.Int() == v2.Int()
case reflect.Map:
if v1.Len() != v2.Len() {
return false
}
for _, key := range v1.MapKeys() {
val2 := v2.MapIndex(key)
if !val2.IsValid() {
// This key was not found in the second map.
return false
}
if !equalAny(v1.MapIndex(key), val2) {
return false
}
}
return true
case reflect.Ptr: case reflect.Ptr:
return equalAny(v1.Elem(), v2.Elem()) return equalAny(v1.Elem(), v2.Elem())
case reflect.Slice: case reflect.Slice:

View File

@ -34,8 +34,8 @@ package proto_test
import ( import (
"testing" "testing"
pb "./testdata"
. "github.com/gogo/protobuf/proto" . "github.com/gogo/protobuf/proto"
pb "github.com/gogo/protobuf/proto/testdata"
) )
// Four identical base messages. // Four identical base messages.
@ -155,6 +155,31 @@ var EqualTests = []struct {
}, },
true, true,
}, },
{
"map same",
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
true,
},
{
"map different entry",
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
&pb.MessageWithMap{NameMapping: map[int32]string{2: "Rob"}},
false,
},
{
"map different key only",
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
&pb.MessageWithMap{NameMapping: map[int32]string{2: "Ken"}},
false,
},
{
"map different value only",
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob"}},
false,
},
} }
func TestEqual(t *testing.T) { func TestEqual(t *testing.T) {

View File

@ -37,6 +37,7 @@ package proto
import ( import (
"errors" "errors"
"fmt"
"reflect" "reflect"
"strconv" "strconv"
"sync" "sync"
@ -310,7 +311,9 @@ func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, er
emap := epb.ExtensionMap() emap := epb.ExtensionMap()
e, ok := emap[extension.Field] e, ok := emap[extension.Field]
if !ok { if !ok {
return nil, ErrMissingExtension // defaultExtensionValue returns the default value or
// ErrMissingExtension if there is no default.
return defaultExtensionValue(extension)
} }
if e.value != nil { if e.value != nil {
// Already decoded. Check the descriptor, though. // Already decoded. Check the descriptor, though.
@ -355,10 +358,46 @@ func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, er
} }
o += n + l o += n + l
} }
return defaultExtensionValue(extension)
} }
panic("unreachable") panic("unreachable")
} }
// defaultExtensionValue returns the default value for extension.
// If no default for an extension is defined ErrMissingExtension is returned.
func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
t := reflect.TypeOf(extension.ExtensionType)
props := extensionProperties(extension)
sf, _, err := fieldDefault(t, props)
if err != nil {
return nil, err
}
if sf == nil || sf.value == nil {
// There is no default value.
return nil, ErrMissingExtension
}
if t.Kind() != reflect.Ptr {
// We do not need to return a Ptr, we can directly return sf.value.
return sf.value, nil
}
// We need to return an interface{} that is a pointer to sf.value.
value := reflect.New(t).Elem()
value.Set(reflect.New(value.Type().Elem()))
if sf.kind == reflect.Int32 {
// We may have an int32 or an enum, but the underlying data is int32.
// Since we can't set an int32 into a non int32 reflect.value directly
// set it as a int32.
value.Elem().SetInt(int64(sf.value.(int32)))
} else {
value.Elem().Set(reflect.ValueOf(sf.value))
}
return value.Interface(), nil
}
// decodeExtension decodes an extension encoded in b. // decodeExtension decodes an extension encoded in b.
func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) { func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
o := NewBuffer(b) o := NewBuffer(b)
@ -423,6 +462,14 @@ func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{
if typ != reflect.TypeOf(value) { if typ != reflect.TypeOf(value) {
return errors.New("proto: bad extension value type") return errors.New("proto: bad extension value type")
} }
// nil extension values need to be caught early, because the
// encoder can't distinguish an ErrNil due to a nil extension
// from an ErrNil due to a missing field. Extensions are
// always optional, so the encoder would just swallow the error
// and drop all the extensions from the encoded message.
if reflect.ValueOf(value).IsNil() {
return fmt.Errorf("proto: SetExtension called with nil value of type %T", value)
}
return setExtension(pb, extension, value) return setExtension(pb, extension, value)
} }

View File

@ -32,10 +32,12 @@
package proto_test package proto_test
import ( import (
"fmt"
"reflect"
"testing" "testing"
pb "./testdata"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
pb "github.com/gogo/protobuf/proto/testdata"
) )
func TestGetExtensionsWithMissingExtensions(t *testing.T) { func TestGetExtensionsWithMissingExtensions(t *testing.T) {
@ -92,3 +94,199 @@ func TestGetExtensionStability(t *testing.T) {
t.Errorf("GetExtension() not stable after unmarshaling") t.Errorf("GetExtension() not stable after unmarshaling")
} }
} }
func TestGetExtensionDefaults(t *testing.T) {
var setFloat64 float64 = 1
var setFloat32 float32 = 2
var setInt32 int32 = 3
var setInt64 int64 = 4
var setUint32 uint32 = 5
var setUint64 uint64 = 6
var setBool = true
var setBool2 = false
var setString = "Goodnight string"
var setBytes = []byte("Goodnight bytes")
var setEnum = pb.DefaultsMessage_TWO
type testcase struct {
ext *proto.ExtensionDesc // Extension we are testing.
want interface{} // Expected value of extension, or nil (meaning that GetExtension will fail).
def interface{} // Expected value of extension after ClearExtension().
}
tests := []testcase{
{pb.E_NoDefaultDouble, setFloat64, nil},
{pb.E_NoDefaultFloat, setFloat32, nil},
{pb.E_NoDefaultInt32, setInt32, nil},
{pb.E_NoDefaultInt64, setInt64, nil},
{pb.E_NoDefaultUint32, setUint32, nil},
{pb.E_NoDefaultUint64, setUint64, nil},
{pb.E_NoDefaultSint32, setInt32, nil},
{pb.E_NoDefaultSint64, setInt64, nil},
{pb.E_NoDefaultFixed32, setUint32, nil},
{pb.E_NoDefaultFixed64, setUint64, nil},
{pb.E_NoDefaultSfixed32, setInt32, nil},
{pb.E_NoDefaultSfixed64, setInt64, nil},
{pb.E_NoDefaultBool, setBool, nil},
{pb.E_NoDefaultBool, setBool2, nil},
{pb.E_NoDefaultString, setString, nil},
{pb.E_NoDefaultBytes, setBytes, nil},
{pb.E_NoDefaultEnum, setEnum, nil},
{pb.E_DefaultDouble, setFloat64, float64(3.1415)},
{pb.E_DefaultFloat, setFloat32, float32(3.14)},
{pb.E_DefaultInt32, setInt32, int32(42)},
{pb.E_DefaultInt64, setInt64, int64(43)},
{pb.E_DefaultUint32, setUint32, uint32(44)},
{pb.E_DefaultUint64, setUint64, uint64(45)},
{pb.E_DefaultSint32, setInt32, int32(46)},
{pb.E_DefaultSint64, setInt64, int64(47)},
{pb.E_DefaultFixed32, setUint32, uint32(48)},
{pb.E_DefaultFixed64, setUint64, uint64(49)},
{pb.E_DefaultSfixed32, setInt32, int32(50)},
{pb.E_DefaultSfixed64, setInt64, int64(51)},
{pb.E_DefaultBool, setBool, true},
{pb.E_DefaultBool, setBool2, true},
{pb.E_DefaultString, setString, "Hello, string"},
{pb.E_DefaultBytes, setBytes, []byte("Hello, bytes")},
{pb.E_DefaultEnum, setEnum, pb.DefaultsMessage_ONE},
}
checkVal := func(test testcase, msg *pb.DefaultsMessage, valWant interface{}) error {
val, err := proto.GetExtension(msg, test.ext)
if err != nil {
if valWant != nil {
return fmt.Errorf("GetExtension(): %s", err)
}
if want := proto.ErrMissingExtension; err != want {
return fmt.Errorf("Unexpected error: got %v, want %v", err, want)
}
return nil
}
// All proto2 extension values are either a pointer to a value or a slice of values.
ty := reflect.TypeOf(val)
tyWant := reflect.TypeOf(test.ext.ExtensionType)
if got, want := ty, tyWant; got != want {
return fmt.Errorf("unexpected reflect.TypeOf(): got %v want %v", got, want)
}
tye := ty.Elem()
tyeWant := tyWant.Elem()
if got, want := tye, tyeWant; got != want {
return fmt.Errorf("unexpected reflect.TypeOf().Elem(): got %v want %v", got, want)
}
// Check the name of the type of the value.
// If it is an enum it will be type int32 with the name of the enum.
if got, want := tye.Name(), tye.Name(); got != want {
return fmt.Errorf("unexpected reflect.TypeOf().Elem().Name(): got %v want %v", got, want)
}
// Check that value is what we expect.
// If we have a pointer in val, get the value it points to.
valExp := val
if ty.Kind() == reflect.Ptr {
valExp = reflect.ValueOf(val).Elem().Interface()
}
if got, want := valExp, valWant; !reflect.DeepEqual(got, want) {
return fmt.Errorf("unexpected reflect.DeepEqual(): got %v want %v", got, want)
}
return nil
}
setTo := func(test testcase) interface{} {
setTo := reflect.ValueOf(test.want)
if typ := reflect.TypeOf(test.ext.ExtensionType); typ.Kind() == reflect.Ptr {
setTo = reflect.New(typ).Elem()
setTo.Set(reflect.New(setTo.Type().Elem()))
setTo.Elem().Set(reflect.ValueOf(test.want))
}
return setTo.Interface()
}
for _, test := range tests {
msg := &pb.DefaultsMessage{}
name := test.ext.Name
// Check the initial value.
if err := checkVal(test, msg, test.def); err != nil {
t.Errorf("%s: %v", name, err)
}
// Set the per-type value and check value.
name = fmt.Sprintf("%s (set to %T %v)", name, test.want, test.want)
if err := proto.SetExtension(msg, test.ext, setTo(test)); err != nil {
t.Errorf("%s: SetExtension(): %v", name, err)
continue
}
if err := checkVal(test, msg, test.want); err != nil {
t.Errorf("%s: %v", name, err)
continue
}
// Set and check the value.
name += " (cleared)"
proto.ClearExtension(msg, test.ext)
if err := checkVal(test, msg, test.def); err != nil {
t.Errorf("%s: %v", name, err)
}
}
}
func TestExtensionsRoundTrip(t *testing.T) {
msg := &pb.MyMessage{}
ext1 := &pb.Ext{
Data: proto.String("hi"),
}
ext2 := &pb.Ext{
Data: proto.String("there"),
}
exists := proto.HasExtension(msg, pb.E_Ext_More)
if exists {
t.Error("Extension More present unexpectedly")
}
if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil {
t.Error(err)
}
if err := proto.SetExtension(msg, pb.E_Ext_More, ext2); err != nil {
t.Error(err)
}
e, err := proto.GetExtension(msg, pb.E_Ext_More)
if err != nil {
t.Error(err)
}
x, ok := e.(*pb.Ext)
if !ok {
t.Errorf("e has type %T, expected testdata.Ext", e)
} else if *x.Data != "there" {
t.Errorf("SetExtension failed to overwrite, got %+v, not 'there'", x)
}
proto.ClearExtension(msg, pb.E_Ext_More)
if _, err = proto.GetExtension(msg, pb.E_Ext_More); err != proto.ErrMissingExtension {
t.Errorf("got %v, expected ErrMissingExtension", e)
}
if _, err := proto.GetExtension(msg, pb.E_X215); err == nil {
t.Error("expected bad extension error, got nil")
}
if err := proto.SetExtension(msg, pb.E_X215, 12); err == nil {
t.Error("expected extension err")
}
if err := proto.SetExtension(msg, pb.E_Ext_More, 12); err == nil {
t.Error("expected some sort of type mismatch error, got nil")
}
}
func TestNilExtension(t *testing.T) {
msg := &pb.MyMessage{
Count: proto.Int32(1),
}
if err := proto.SetExtension(msg, pb.E_Ext_Text, proto.String("hello")); err != nil {
t.Fatal(err)
}
if err := proto.SetExtension(msg, pb.E_Ext_More, (*pb.Ext)(nil)); err == nil {
t.Error("expected SetExtension to fail due to a nil extension")
} else if want := "proto: SetExtension called with nil value of type *testdata.Ext"; err.Error() != want {
t.Errorf("expected error %v, got %v", want, err)
}
// Note: if the behavior of Marshal is ever changed to ignore nil extensions, update
// this test to verify that E_Ext_Text is properly propagated through marshal->unmarshal.
}

View File

@ -30,12 +30,12 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/* /*
Package proto converts data structures to and from the wire format of Package proto converts data structures to and from the wire format of
protocol buffers. It works in concert with the Go source code generated protocol buffers. It works in concert with the Go source code generated
for .proto files by the protocol compiler. for .proto files by the protocol compiler.
A summary of the properties of the protocol buffer interface A summary of the properties of the protocol buffer interface
for a protocol buffer variable v: for a protocol buffer variable v:
- Names are turned from camel_case to CamelCase for export. - Names are turned from camel_case to CamelCase for export.
- There are no methods on v to set fields; just treat - There are no methods on v to set fields; just treat
@ -50,17 +50,16 @@
That is, optional or required field int32 f becomes F *int32. That is, optional or required field int32 f becomes F *int32.
- Repeated fields are slices. - Repeated fields are slices.
- Helper functions are available to aid the setting of fields. - Helper functions are available to aid the setting of fields.
Helpers for getting values are superseded by the
GetFoo methods and their use is deprecated.
msg.Foo = proto.String("hello") // set field msg.Foo = proto.String("hello") // set field
- Constants are defined to hold the default values of all fields that - Constants are defined to hold the default values of all fields that
have them. They have the form Default_StructName_FieldName. have them. They have the form Default_StructName_FieldName.
Because the getter methods handle defaulted values, Because the getter methods handle defaulted values,
direct use of these constants should be rare. direct use of these constants should be rare.
- Enums are given type names and maps from names to values. - Enums are given type names and maps from names to values.
Enum values are prefixed with the enum's type name. Enum types have Enum values are prefixed by the enclosing message's name, or by the
a String method, and a Enum method to assist in message construction. enum's type name if it is a top-level enum. Enum types have a String
- Nested groups and enums have type names prefixed with the name of method, and a Enum method to assist in message construction.
- Nested messages, groups and enums have type names prefixed with the name of
the surrounding message type. the surrounding message type.
- Extensions are given descriptor names that start with E_, - Extensions are given descriptor names that start with E_,
followed by an underscore-delimited list of the nested messages followed by an underscore-delimited list of the nested messages
@ -69,12 +68,12 @@
and SetExtension are functions for manipulating extensions. and SetExtension are functions for manipulating extensions.
- Marshal and Unmarshal are functions to encode and decode the wire format. - Marshal and Unmarshal are functions to encode and decode the wire format.
The simplest way to describe this is to see an example. The simplest way to describe this is to see an example.
Given file test.proto, containing Given file test.proto, containing
package example; package example;
enum FOO { X = 17; }; enum FOO { X = 17; }
message Test { message Test {
required string label = 1; required string label = 1;
@ -85,11 +84,12 @@
} }
} }
The resulting file, test.pb.go, is: The resulting file, test.pb.go, is:
package example package example
import "github.com/gogo/protobuf/proto" import proto "github.com/gogo/protobuf/proto"
import math "math"
type FOO int32 type FOO int32
const ( const (
@ -110,6 +110,14 @@
func (x FOO) String() string { func (x FOO) String() string {
return proto.EnumName(FOO_name, int32(x)) return proto.EnumName(FOO_name, int32(x))
} }
func (x *FOO) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(FOO_value, data)
if err != nil {
return err
}
*x = FOO(value)
return nil
}
type Test struct { type Test struct {
Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"` Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"`
@ -118,41 +126,41 @@
Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"` Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (this *Test) Reset() { *this = Test{} } func (m *Test) Reset() { *m = Test{} }
func (this *Test) String() string { return proto.CompactTextString(this) } func (m *Test) String() string { return proto.CompactTextString(m) }
func (*Test) ProtoMessage() {}
const Default_Test_Type int32 = 77 const Default_Test_Type int32 = 77
func (this *Test) GetLabel() string { func (m *Test) GetLabel() string {
if this != nil && this.Label != nil { if m != nil && m.Label != nil {
return *this.Label return *m.Label
} }
return "" return ""
} }
func (this *Test) GetType() int32 { func (m *Test) GetType() int32 {
if this != nil && this.Type != nil { if m != nil && m.Type != nil {
return *this.Type return *m.Type
} }
return Default_Test_Type return Default_Test_Type
} }
func (this *Test) GetOptionalgroup() *Test_OptionalGroup { func (m *Test) GetOptionalgroup() *Test_OptionalGroup {
if this != nil { if m != nil {
return this.Optionalgroup return m.Optionalgroup
} }
return nil return nil
} }
type Test_OptionalGroup struct { type Test_OptionalGroup struct {
RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"` RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"`
XXX_unrecognized []byte `json:"-"`
} }
func (this *Test_OptionalGroup) Reset() { *this = Test_OptionalGroup{} } func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} }
func (this *Test_OptionalGroup) String() string { return proto.CompactTextString(this) } func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) }
func (this *Test_OptionalGroup) GetRequiredField() string { func (m *Test_OptionalGroup) GetRequiredField() string {
if this != nil && this.RequiredField != nil { if m != nil && m.RequiredField != nil {
return *this.RequiredField return *m.RequiredField
} }
return "" return ""
} }
@ -161,22 +169,22 @@
proto.RegisterEnum("example.FOO", FOO_name, FOO_value) proto.RegisterEnum("example.FOO", FOO_name, FOO_value)
} }
To create and play with a Test object: To create and play with a Test object:
package main package main
import ( import (
"log" "log"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
"./example.pb" pb "./example.pb"
) )
func main() { func main() {
test := &example.Test{ test := &pb.Test{
Label: proto.String("hello"), Label: proto.String("hello"),
Type: proto.Int32(17), Type: proto.Int32(17),
Optionalgroup: &example.Test_OptionalGroup{ Optionalgroup: &pb.Test_OptionalGroup{
RequiredField: proto.String("good bye"), RequiredField: proto.String("good bye"),
}, },
} }
@ -184,7 +192,7 @@
if err != nil { if err != nil {
log.Fatal("marshaling error: ", err) log.Fatal("marshaling error: ", err)
} }
newTest := new(example.Test) newTest := &pb.Test{}
err = proto.Unmarshal(data, newTest) err = proto.Unmarshal(data, newTest)
if err != nil { if err != nil {
log.Fatal("unmarshaling error: ", err) log.Fatal("unmarshaling error: ", err)
@ -323,9 +331,7 @@ func Float64(v float64) *float64 {
// Uint32 is a helper routine that allocates a new uint32 value // Uint32 is a helper routine that allocates a new uint32 value
// to store v and returns a pointer to it. // to store v and returns a pointer to it.
func Uint32(v uint32) *uint32 { func Uint32(v uint32) *uint32 {
p := new(uint32) return &v
*p = v
return p
} }
// Uint64 is a helper routine that allocates a new uint64 value // Uint64 is a helper routine that allocates a new uint64 value
@ -379,13 +385,13 @@ func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32,
// DebugPrint dumps the encoded data in b in a debugging format with a header // DebugPrint dumps the encoded data in b in a debugging format with a header
// including the string s. Used in testing but made available for general debugging. // including the string s. Used in testing but made available for general debugging.
func (o *Buffer) DebugPrint(s string, b []byte) { func (p *Buffer) DebugPrint(s string, b []byte) {
var u uint64 var u uint64
obuf := o.buf obuf := p.buf
index := o.index index := p.index
o.buf = b p.buf = b
o.index = 0 p.index = 0
depth := 0 depth := 0
fmt.Printf("\n--- %s ---\n", s) fmt.Printf("\n--- %s ---\n", s)
@ -396,12 +402,12 @@ out:
fmt.Print(" ") fmt.Print(" ")
} }
index := o.index index := p.index
if index == len(o.buf) { if index == len(p.buf) {
break break
} }
op, err := o.DecodeVarint() op, err := p.DecodeVarint()
if err != nil { if err != nil {
fmt.Printf("%3d: fetching op err %v\n", index, err) fmt.Printf("%3d: fetching op err %v\n", index, err)
break out break out
@ -418,7 +424,7 @@ out:
case WireBytes: case WireBytes:
var r []byte var r []byte
r, err = o.DecodeRawBytes(false) r, err = p.DecodeRawBytes(false)
if err != nil { if err != nil {
break out break out
} }
@ -439,7 +445,7 @@ out:
fmt.Printf("\n") fmt.Printf("\n")
case WireFixed32: case WireFixed32:
u, err = o.DecodeFixed32() u, err = p.DecodeFixed32()
if err != nil { if err != nil {
fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err) fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err)
break out break out
@ -447,7 +453,7 @@ out:
fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u) fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u)
case WireFixed64: case WireFixed64:
u, err = o.DecodeFixed64() u, err = p.DecodeFixed64()
if err != nil { if err != nil {
fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err) fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err)
break out break out
@ -456,7 +462,7 @@ out:
break break
case WireVarint: case WireVarint:
u, err = o.DecodeVarint() u, err = p.DecodeVarint()
if err != nil { if err != nil {
fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err) fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err)
break out break out
@ -482,12 +488,12 @@ out:
} }
if depth != 0 { if depth != 0 {
fmt.Printf("%3d: start-end not balanced %d\n", o.index, depth) fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth)
} }
fmt.Printf("\n") fmt.Printf("\n")
o.buf = obuf p.buf = obuf
o.index = index p.index = index
} }
// SetDefaults sets unset protocol buffer fields to their default values. // SetDefaults sets unset protocol buffer fields to their default values.
@ -601,13 +607,15 @@ func setDefaults(v reflect.Value, recur, zeros bool) {
for _, ni := range dm.nested { for _, ni := range dm.nested {
f := v.Field(ni) f := v.Field(ni)
// f is *T or []*T or map[T]*T
switch f.Kind() {
case reflect.Ptr:
if f.IsNil() { if f.IsNil() {
continue continue
} }
// f is *T or []*T
if f.Kind() == reflect.Ptr {
setDefaults(f, recur, zeros) setDefaults(f, recur, zeros)
} else {
case reflect.Slice:
for i := 0; i < f.Len(); i++ { for i := 0; i < f.Len(); i++ {
e := f.Index(i) e := f.Index(i)
if e.IsNil() { if e.IsNil() {
@ -615,6 +623,15 @@ func setDefaults(v reflect.Value, recur, zeros bool) {
} }
setDefaults(e, recur, zeros) setDefaults(e, recur, zeros)
} }
case reflect.Map:
for _, k := range f.MapKeys() {
e := f.MapIndex(k)
if e.IsNil() {
continue
}
setDefaults(e, recur, zeros)
}
} }
} }
} }
@ -640,10 +657,6 @@ type scalarField struct {
value interface{} // the proto-declared default value, or nil value interface{} // the proto-declared default value, or nil
} }
func ptrToStruct(t reflect.Type) bool {
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
}
// t is a struct type. // t is a struct type.
func buildDefaultMessage(t reflect.Type) (dm defaultMessage) { func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
sprop := GetProperties(t) sprop := GetProperties(t)
@ -655,21 +668,62 @@ func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
} }
ft := t.Field(fi).Type ft := t.Field(fi).Type
// nested messages sf, nested, err := fieldDefault(ft, prop)
if ptrToStruct(ft) || (ft.Kind() == reflect.Slice && ptrToStruct(ft.Elem())) { switch {
case err != nil:
log.Print(err)
case nested:
dm.nested = append(dm.nested, fi) dm.nested = append(dm.nested, fi)
continue case sf != nil:
sf.index = fi
dm.scalars = append(dm.scalars, *sf)
}
} }
sf := scalarField{ return dm
index: fi, }
kind: ft.Elem().Kind(),
// fieldDefault returns the scalarField for field type ft.
// sf will be nil if the field can not have a default.
// nestedMessage will be true if this is a nested message.
// Note that sf.index is not set on return.
func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) {
var canHaveDefault bool
switch ft.Kind() {
case reflect.Ptr:
if ft.Elem().Kind() == reflect.Struct {
nestedMessage = true
} else {
canHaveDefault = true // proto2 scalar field
} }
case reflect.Slice:
switch ft.Elem().Kind() {
case reflect.Ptr:
nestedMessage = true // repeated message
case reflect.Uint8:
canHaveDefault = true // bytes field
}
case reflect.Map:
if ft.Elem().Kind() == reflect.Ptr {
nestedMessage = true // map with message values
}
}
if !canHaveDefault {
if nestedMessage {
return nil, true, nil
}
return nil, false, nil
}
// We now know that ft is a pointer or slice.
sf = &scalarField{kind: ft.Elem().Kind()}
// scalar fields without defaults // scalar fields without defaults
if !prop.HasDefault { if !prop.HasDefault {
dm.scalars = append(dm.scalars, sf) return sf, false, nil
continue
} }
// a scalar field: either *T or []byte // a scalar field: either *T or []byte
@ -677,36 +731,31 @@ func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
case reflect.Bool: case reflect.Bool:
x, err := strconv.ParseBool(prop.Default) x, err := strconv.ParseBool(prop.Default)
if err != nil { if err != nil {
log.Printf("proto: bad default bool %q: %v", prop.Default, err) return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err)
continue
} }
sf.value = x sf.value = x
case reflect.Float32: case reflect.Float32:
x, err := strconv.ParseFloat(prop.Default, 32) x, err := strconv.ParseFloat(prop.Default, 32)
if err != nil { if err != nil {
log.Printf("proto: bad default float32 %q: %v", prop.Default, err) return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err)
continue
} }
sf.value = float32(x) sf.value = float32(x)
case reflect.Float64: case reflect.Float64:
x, err := strconv.ParseFloat(prop.Default, 64) x, err := strconv.ParseFloat(prop.Default, 64)
if err != nil { if err != nil {
log.Printf("proto: bad default float64 %q: %v", prop.Default, err) return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err)
continue
} }
sf.value = x sf.value = x
case reflect.Int32: case reflect.Int32:
x, err := strconv.ParseInt(prop.Default, 10, 32) x, err := strconv.ParseInt(prop.Default, 10, 32)
if err != nil { if err != nil {
log.Printf("proto: bad default int32 %q: %v", prop.Default, err) return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err)
continue
} }
sf.value = int32(x) sf.value = int32(x)
case reflect.Int64: case reflect.Int64:
x, err := strconv.ParseInt(prop.Default, 10, 64) x, err := strconv.ParseInt(prop.Default, 10, 64)
if err != nil { if err != nil {
log.Printf("proto: bad default int64 %q: %v", prop.Default, err) return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err)
continue
} }
sf.value = x sf.value = x
case reflect.String: case reflect.String:
@ -717,24 +766,48 @@ func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
case reflect.Uint32: case reflect.Uint32:
x, err := strconv.ParseUint(prop.Default, 10, 32) x, err := strconv.ParseUint(prop.Default, 10, 32)
if err != nil { if err != nil {
log.Printf("proto: bad default uint32 %q: %v", prop.Default, err) return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err)
continue
} }
sf.value = uint32(x) sf.value = uint32(x)
case reflect.Uint64: case reflect.Uint64:
x, err := strconv.ParseUint(prop.Default, 10, 64) x, err := strconv.ParseUint(prop.Default, 10, 64)
if err != nil { if err != nil {
log.Printf("proto: bad default uint64 %q: %v", prop.Default, err) return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err)
continue
} }
sf.value = x sf.value = x
default: default:
log.Printf("proto: unhandled def kind %v", ft.Elem().Kind()) return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind())
continue
} }
dm.scalars = append(dm.scalars, sf) return sf, false, nil
} }
return dm // Map fields may have key types of non-float scalars, strings and enums.
// The easiest way to sort them in some deterministic order is to use fmt.
// If this turns out to be inefficient we can always consider other options,
// such as doing a Schwartzian transform.
type mapKeys []reflect.Value
func (s mapKeys) Len() int { return len(s) }
func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s mapKeys) Less(i, j int) bool {
return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface())
}
// isProto3Zero reports whether v is a zero proto3 value.
func isProto3Zero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Bool:
return !v.Bool()
case reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint32, reflect.Uint64:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.String:
return v.String() == ""
}
return false
} }

View File

@ -29,7 +29,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// +build appengine,!appenginevm // +build appengine
// This file contains an implementation of proto field accesses using package reflect. // This file contains an implementation of proto field accesses using package reflect.
// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can // It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
@ -114,6 +114,11 @@ func structPointer_Bool(p structPointer, f field) **bool {
return structPointer_ifield(p, f).(**bool) return structPointer_ifield(p, f).(**bool)
} }
// BoolVal returns the address of a bool field in the struct.
func structPointer_BoolVal(p structPointer, f field) *bool {
return structPointer_ifield(p, f).(*bool)
}
// BoolSlice returns the address of a []bool field in the struct. // BoolSlice returns the address of a []bool field in the struct.
func structPointer_BoolSlice(p structPointer, f field) *[]bool { func structPointer_BoolSlice(p structPointer, f field) *[]bool {
return structPointer_ifield(p, f).(*[]bool) return structPointer_ifield(p, f).(*[]bool)
@ -124,6 +129,11 @@ func structPointer_String(p structPointer, f field) **string {
return structPointer_ifield(p, f).(**string) return structPointer_ifield(p, f).(**string)
} }
// StringVal returns the address of a string field in the struct.
func structPointer_StringVal(p structPointer, f field) *string {
return structPointer_ifield(p, f).(*string)
}
// StringSlice returns the address of a []string field in the struct. // StringSlice returns the address of a []string field in the struct.
func structPointer_StringSlice(p structPointer, f field) *[]string { func structPointer_StringSlice(p structPointer, f field) *[]string {
return structPointer_ifield(p, f).(*[]string) return structPointer_ifield(p, f).(*[]string)
@ -134,6 +144,11 @@ func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
return structPointer_ifield(p, f).(*map[int32]Extension) return structPointer_ifield(p, f).(*map[int32]Extension)
} }
// Map returns the reflect.Value for the address of a map field in the struct.
func structPointer_Map(p structPointer, f field, typ reflect.Type) reflect.Value {
return structPointer_field(p, f).Addr()
}
// SetStructPointer writes a *struct field in the struct. // SetStructPointer writes a *struct field in the struct.
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) { func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
structPointer_field(p, f).Set(q.v) structPointer_field(p, f).Set(q.v)
@ -235,6 +250,49 @@ func structPointer_Word32(p structPointer, f field) word32 {
return word32{structPointer_field(p, f)} return word32{structPointer_field(p, f)}
} }
// A word32Val represents a field of type int32, uint32, float32, or enum.
// That is, v.Type() is int32, uint32, float32, or enum and v is assignable.
type word32Val struct {
v reflect.Value
}
// Set sets *p to x.
func word32Val_Set(p word32Val, x uint32) {
switch p.v.Type() {
case int32Type:
p.v.SetInt(int64(x))
return
case uint32Type:
p.v.SetUint(uint64(x))
return
case float32Type:
p.v.SetFloat(float64(math.Float32frombits(x)))
return
}
// must be enum
p.v.SetInt(int64(int32(x)))
}
// Get gets the bits pointed at by p, as a uint32.
func word32Val_Get(p word32Val) uint32 {
elem := p.v
switch elem.Kind() {
case reflect.Int32:
return uint32(elem.Int())
case reflect.Uint32:
return uint32(elem.Uint())
case reflect.Float32:
return math.Float32bits(float32(elem.Float()))
}
panic("unreachable")
}
// Word32Val returns a reference to a int32, uint32, float32, or enum field in the struct.
func structPointer_Word32Val(p structPointer, f field) word32Val {
return word32Val{structPointer_field(p, f)}
}
// A word32Slice is a slice of 32-bit values. // A word32Slice is a slice of 32-bit values.
// That is, v.Type() is []int32, []uint32, []float32, or []enum. // That is, v.Type() is []int32, []uint32, []float32, or []enum.
type word32Slice struct { type word32Slice struct {
@ -339,6 +397,43 @@ func structPointer_Word64(p structPointer, f field) word64 {
return word64{structPointer_field(p, f)} return word64{structPointer_field(p, f)}
} }
// word64Val is like word32Val but for 64-bit values.
type word64Val struct {
v reflect.Value
}
func word64Val_Set(p word64Val, o *Buffer, x uint64) {
switch p.v.Type() {
case int64Type:
p.v.SetInt(int64(x))
return
case uint64Type:
p.v.SetUint(x)
return
case float64Type:
p.v.SetFloat(math.Float64frombits(x))
return
}
panic("unreachable")
}
func word64Val_Get(p word64Val) uint64 {
elem := p.v
switch elem.Kind() {
case reflect.Int64:
return uint64(elem.Int())
case reflect.Uint64:
return elem.Uint()
case reflect.Float64:
return math.Float64bits(elem.Float())
}
panic("unreachable")
}
func structPointer_Word64Val(p structPointer, f field) word64Val {
return word64Val{structPointer_field(p, f)}
}
type word64Slice struct { type word64Slice struct {
v reflect.Value v reflect.Value
} }

View File

@ -29,7 +29,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// +build !appengine appenginevm // +build !appengine
// This file contains the implementation of the proto field accesses using package unsafe. // This file contains the implementation of the proto field accesses using package unsafe.
@ -100,6 +100,11 @@ func structPointer_Bool(p structPointer, f field) **bool {
return (**bool)(unsafe.Pointer(uintptr(p) + uintptr(f))) return (**bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
} }
// BoolVal returns the address of a bool field in the struct.
func structPointer_BoolVal(p structPointer, f field) *bool {
return (*bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// BoolSlice returns the address of a []bool field in the struct. // BoolSlice returns the address of a []bool field in the struct.
func structPointer_BoolSlice(p structPointer, f field) *[]bool { func structPointer_BoolSlice(p structPointer, f field) *[]bool {
return (*[]bool)(unsafe.Pointer(uintptr(p) + uintptr(f))) return (*[]bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
@ -110,6 +115,11 @@ func structPointer_String(p structPointer, f field) **string {
return (**string)(unsafe.Pointer(uintptr(p) + uintptr(f))) return (**string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
} }
// StringVal returns the address of a string field in the struct.
func structPointer_StringVal(p structPointer, f field) *string {
return (*string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// StringSlice returns the address of a []string field in the struct. // StringSlice returns the address of a []string field in the struct.
func structPointer_StringSlice(p structPointer, f field) *[]string { func structPointer_StringSlice(p structPointer, f field) *[]string {
return (*[]string)(unsafe.Pointer(uintptr(p) + uintptr(f))) return (*[]string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
@ -120,6 +130,11 @@ func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f))) return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f)))
} }
// Map returns the reflect.Value for the address of a map field in the struct.
func structPointer_Map(p structPointer, f field, typ reflect.Type) reflect.Value {
return reflect.NewAt(typ, unsafe.Pointer(uintptr(p)+uintptr(f)))
}
// SetStructPointer writes a *struct field in the struct. // SetStructPointer writes a *struct field in the struct.
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) { func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
*(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f))) = q *(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f))) = q
@ -170,6 +185,24 @@ func structPointer_Word32(p structPointer, f field) word32 {
return word32((**uint32)(unsafe.Pointer(uintptr(p) + uintptr(f)))) return word32((**uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
} }
// A word32Val is the address of a 32-bit value field.
type word32Val *uint32
// Set sets *p to x.
func word32Val_Set(p word32Val, x uint32) {
*p = x
}
// Get gets the value pointed at by p.
func word32Val_Get(p word32Val) uint32 {
return *p
}
// Word32Val returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
func structPointer_Word32Val(p structPointer, f field) word32Val {
return word32Val((*uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
}
// A word32Slice is a slice of 32-bit values. // A word32Slice is a slice of 32-bit values.
type word32Slice []uint32 type word32Slice []uint32
@ -206,6 +239,21 @@ func structPointer_Word64(p structPointer, f field) word64 {
return word64((**uint64)(unsafe.Pointer(uintptr(p) + uintptr(f)))) return word64((**uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
} }
// word64Val is like word32Val but for 64-bit values.
type word64Val *uint64
func word64Val_Set(p word64Val, o *Buffer, x uint64) {
*p = x
}
func word64Val_Get(p word64Val) uint64 {
return *p
}
func structPointer_Word64Val(p structPointer, f field) word64Val {
return word64Val((*uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
}
// word64Slice is like word32Slice but for 64-bit values. // word64Slice is like word32Slice but for 64-bit values.
type word64Slice []uint64 type word64Slice []uint64

View File

@ -87,16 +87,6 @@ func appendStructPointer(base structPointer, f field, typ reflect.Type) structPo
return structPointer(unsafe.Pointer(uintptr(unsafe.Pointer(bas)) + uintptr(uintptr(newLen-1)*size))) return structPointer(unsafe.Pointer(uintptr(unsafe.Pointer(bas)) + uintptr(uintptr(newLen-1)*size)))
} }
// RefBool returns a *bool field in the struct.
func structPointer_RefBool(p structPointer, f field) *bool {
return (*bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// RefString returns the address of a string field in the struct.
func structPointer_RefString(p structPointer, f field) *string {
return (*string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
func structPointer_FieldPointer(p structPointer, f field) structPointer { func structPointer_FieldPointer(p structPointer, f field) structPointer {
return structPointer(unsafe.Pointer(uintptr(p) + uintptr(f))) return structPointer(unsafe.Pointer(uintptr(p) + uintptr(f)))
} }
@ -116,51 +106,3 @@ func structPointer_Add(p structPointer, size field) structPointer {
func structPointer_Len(p structPointer, f field) int { func structPointer_Len(p structPointer, f field) int {
return len(*(*[]interface{})(unsafe.Pointer(structPointer_GetRefStructPointer(p, f)))) return len(*(*[]interface{})(unsafe.Pointer(structPointer_GetRefStructPointer(p, f))))
} }
// refWord32 is the address of a 32-bit value field.
type refWord32 *uint32
func refWord32_IsNil(p refWord32) bool {
return p == nil
}
func refWord32_Set(p refWord32, o *Buffer, x uint32) {
if len(o.uint32s) == 0 {
o.uint32s = make([]uint32, uint32PoolSize)
}
o.uint32s[0] = x
*p = o.uint32s[0]
o.uint32s = o.uint32s[1:]
}
func refWord32_Get(p refWord32) uint32 {
return *p
}
func structPointer_RefWord32(p structPointer, f field) refWord32 {
return refWord32((*uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
}
// refWord64 is like refWord32 but for 32-bit values.
type refWord64 *uint64
func refWord64_Set(p refWord64, o *Buffer, x uint64) {
if len(o.uint64s) == 0 {
o.uint64s = make([]uint64, uint64PoolSize)
}
o.uint64s[0] = x
*p = o.uint64s[0]
o.uint64s = o.uint64s[1:]
}
func refWord64_IsNil(p refWord64) bool {
return p == nil
}
func refWord64_Get(p refWord64) uint64 {
return *p
}
func structPointer_RefWord64(p structPointer, f field) refWord64 {
return refWord64((*uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
}

View File

@ -160,6 +160,7 @@ type Properties struct {
Repeated bool Repeated bool
Packed bool // relevant for repeated primitives only Packed bool // relevant for repeated primitives only
Enum string // set for enum types only Enum string // set for enum types only
proto3 bool // whether this is known to be a proto3 field; set for []byte only
Default string // default value Default string // default value
HasDefault bool // whether an explicit default was provided HasDefault bool // whether an explicit default was provided
@ -178,6 +179,10 @@ type Properties struct {
isMarshaler bool isMarshaler bool
isUnmarshaler bool isUnmarshaler bool
mtype reflect.Type // set for map types only
mkeyprop *Properties // set for map types only
mvalprop *Properties // set for map types only
size sizer size sizer
valSize valueSizer // set for bool and numeric types only valSize valueSizer // set for bool and numeric types only
@ -208,6 +213,9 @@ func (p *Properties) String() string {
if p.OrigName != p.Name { if p.OrigName != p.Name {
s += ",name=" + p.OrigName s += ",name=" + p.OrigName
} }
if p.proto3 {
s += ",proto3"
}
if len(p.Enum) > 0 { if len(p.Enum) > 0 {
s += ",enum=" + p.Enum s += ",enum=" + p.Enum
} }
@ -282,6 +290,8 @@ func (p *Properties) Parse(s string) {
p.OrigName = f[5:] p.OrigName = f[5:]
case strings.HasPrefix(f, "enum="): case strings.HasPrefix(f, "enum="):
p.Enum = f[5:] p.Enum = f[5:]
case f == "proto3":
p.proto3 = true
case strings.HasPrefix(f, "def="): case strings.HasPrefix(f, "def="):
p.HasDefault = true p.HasDefault = true
p.Default = f[4:] // rest of string p.Default = f[4:] // rest of string
@ -305,7 +315,7 @@ func logNoSliceEnc(t1, t2 reflect.Type) {
var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem() var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
// Initialize the fields for encoding and decoding. // Initialize the fields for encoding and decoding.
func (p *Properties) setEncAndDec(typ reflect.Type, lockGetProp bool) { func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
p.enc = nil p.enc = nil
p.dec = nil p.dec = nil
p.size = nil p.size = nil
@ -316,13 +326,96 @@ func (p *Properties) setEncAndDec(typ reflect.Type, lockGetProp bool) {
} }
switch t1 := typ; t1.Kind() { switch t1 := typ; t1.Kind() {
default: default:
if !p.setNonNullableEncAndDec(t1) {
fmt.Fprintf(os.Stderr, "proto: no coders for %v\n", t1) fmt.Fprintf(os.Stderr, "proto: no coders for %v\n", t1)
// proto3 scalar types
case reflect.Bool:
if p.proto3 {
p.enc = (*Buffer).enc_proto3_bool
p.dec = (*Buffer).dec_proto3_bool
p.size = size_proto3_bool
} else {
p.enc = (*Buffer).enc_ref_bool
p.dec = (*Buffer).dec_proto3_bool
p.size = size_ref_bool
} }
case reflect.Int32:
if p.proto3 {
p.enc = (*Buffer).enc_proto3_int32
p.dec = (*Buffer).dec_proto3_int32
p.size = size_proto3_int32
} else {
p.enc = (*Buffer).enc_ref_int32
p.dec = (*Buffer).dec_proto3_int32
p.size = size_ref_int32
}
case reflect.Uint32:
if p.proto3 {
p.enc = (*Buffer).enc_proto3_uint32
p.dec = (*Buffer).dec_proto3_int32 // can reuse
p.size = size_proto3_uint32
} else {
p.enc = (*Buffer).enc_ref_uint32
p.dec = (*Buffer).dec_proto3_int32 // can reuse
p.size = size_ref_uint32
}
case reflect.Int64, reflect.Uint64:
if p.proto3 {
p.enc = (*Buffer).enc_proto3_int64
p.dec = (*Buffer).dec_proto3_int64
p.size = size_proto3_int64
} else {
p.enc = (*Buffer).enc_ref_int64
p.dec = (*Buffer).dec_proto3_int64
p.size = size_ref_int64
}
case reflect.Float32:
if p.proto3 {
p.enc = (*Buffer).enc_proto3_uint32 // can just treat them as bits
p.dec = (*Buffer).dec_proto3_int32
p.size = size_proto3_uint32
} else {
p.enc = (*Buffer).enc_ref_uint32 // can just treat them as bits
p.dec = (*Buffer).dec_proto3_int32
p.size = size_ref_uint32
}
case reflect.Float64:
if p.proto3 {
p.enc = (*Buffer).enc_proto3_int64 // can just treat them as bits
p.dec = (*Buffer).dec_proto3_int64
p.size = size_proto3_int64
} else {
p.enc = (*Buffer).enc_ref_int64 // can just treat them as bits
p.dec = (*Buffer).dec_proto3_int64
p.size = size_ref_int64
}
case reflect.String:
if p.proto3 {
p.enc = (*Buffer).enc_proto3_string
p.dec = (*Buffer).dec_proto3_string
p.size = size_proto3_string
} else {
p.enc = (*Buffer).enc_ref_string
p.dec = (*Buffer).dec_proto3_string
p.size = size_ref_string
}
case reflect.Struct:
p.stype = typ
p.isMarshaler = isMarshaler(typ)
p.isUnmarshaler = isUnmarshaler(typ)
if p.Wire == "bytes" {
p.enc = (*Buffer).enc_ref_struct_message
p.dec = (*Buffer).dec_ref_struct_message
p.size = size_ref_struct_message
} else {
fmt.Fprintf(os.Stderr, "proto: no coders for struct %T\n", typ)
}
case reflect.Ptr: case reflect.Ptr:
switch t2 := t1.Elem(); t2.Kind() { switch t2 := t1.Elem(); t2.Kind() {
default: default:
fmt.Fprintf(os.Stderr, "proto: no encoder function for %T -> %T\n", t1, t2) fmt.Fprintf(os.Stderr, "proto: no encoder function for %v -> %v\n", t1, t2)
break break
case reflect.Bool: case reflect.Bool:
p.enc = (*Buffer).enc_bool p.enc = (*Buffer).enc_bool
@ -416,6 +509,15 @@ func (p *Properties) setEncAndDec(typ reflect.Type, lockGetProp bool) {
p.enc = (*Buffer).enc_slice_byte p.enc = (*Buffer).enc_slice_byte
p.dec = (*Buffer).dec_slice_byte p.dec = (*Buffer).dec_slice_byte
p.size = size_slice_byte p.size = size_slice_byte
// This is a []byte, which is either a bytes field,
// or the value of a map field. In the latter case,
// we always encode an empty []byte, so we should not
// use the proto3 enc/size funcs.
// f == nil iff this is the key/value of a map field.
if p.proto3 && f != nil {
p.enc = (*Buffer).enc_proto3_slice_byte
p.size = size_proto3_slice_byte
}
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
switch t2.Bits() { switch t2.Bits() {
case 32: case 32:
@ -480,6 +582,23 @@ func (p *Properties) setEncAndDec(typ reflect.Type, lockGetProp bool) {
case reflect.Struct: case reflect.Struct:
p.setSliceOfNonPointerStructs(t1) p.setSliceOfNonPointerStructs(t1)
} }
case reflect.Map:
p.enc = (*Buffer).enc_new_map
p.dec = (*Buffer).dec_new_map
p.size = size_new_map
p.mtype = t1
p.mkeyprop = &Properties{}
p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
p.mvalprop = &Properties{}
vtype := p.mtype.Elem()
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
// The value type is not a message (*T) or bytes ([]byte),
// so we need encoders for the pointer to this type.
vtype = reflect.PtrTo(vtype)
}
p.mvalprop.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
} }
p.setTag(lockGetProp) p.setTag(lockGetProp)
} }
@ -539,11 +658,11 @@ func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructF
return return
} }
p.Parse(tag) p.Parse(tag)
p.setEncAndDec(typ, lockGetProp) p.setEncAndDec(typ, f, lockGetProp)
} }
var ( var (
mutex sync.Mutex propertiesMu sync.RWMutex
propertiesMap = make(map[reflect.Type]*StructProperties) propertiesMap = make(map[reflect.Type]*StructProperties)
) )
@ -553,13 +672,26 @@ func GetProperties(t reflect.Type) *StructProperties {
if t.Kind() != reflect.Struct { if t.Kind() != reflect.Struct {
panic("proto: type must have kind struct") panic("proto: type must have kind struct")
} }
mutex.Lock()
sprop := getPropertiesLocked(t) // Most calls to GetProperties in a long-running program will be
mutex.Unlock() // retrieving details for types we have seen before.
propertiesMu.RLock()
sprop, ok := propertiesMap[t]
propertiesMu.RUnlock()
if ok {
if collectStats {
stats.Chit++
}
return sprop
}
propertiesMu.Lock()
sprop = getPropertiesLocked(t)
propertiesMu.Unlock()
return sprop return sprop
} }
// getPropertiesLocked requires that mutex is held. // getPropertiesLocked requires that propertiesMu is held.
func getPropertiesLocked(t reflect.Type) *StructProperties { func getPropertiesLocked(t reflect.Type) *StructProperties {
if prop, ok := propertiesMap[t]; ok { if prop, ok := propertiesMap[t]; ok {
if collectStats { if collectStats {

View File

@ -49,53 +49,6 @@ func (p *Properties) setCustomEncAndDec(typ reflect.Type) {
} }
} }
func (p *Properties) setNonNullableEncAndDec(typ reflect.Type) bool {
switch typ.Kind() {
case reflect.Bool:
p.enc = (*Buffer).enc_ref_bool
p.dec = (*Buffer).dec_ref_bool
p.size = size_ref_bool
case reflect.Int32:
p.enc = (*Buffer).enc_ref_int32
p.dec = (*Buffer).dec_ref_int32
p.size = size_ref_int32
case reflect.Uint32:
p.enc = (*Buffer).enc_ref_uint32
p.dec = (*Buffer).dec_ref_int32
p.size = size_ref_uint32
case reflect.Int64, reflect.Uint64:
p.enc = (*Buffer).enc_ref_int64
p.dec = (*Buffer).dec_ref_int64
p.size = size_ref_int64
case reflect.Float32:
p.enc = (*Buffer).enc_ref_uint32 // can just treat them as bits
p.dec = (*Buffer).dec_ref_int32
p.size = size_ref_uint32
case reflect.Float64:
p.enc = (*Buffer).enc_ref_int64 // can just treat them as bits
p.dec = (*Buffer).dec_ref_int64
p.size = size_ref_int64
case reflect.String:
p.dec = (*Buffer).dec_ref_string
p.enc = (*Buffer).enc_ref_string
p.size = size_ref_string
case reflect.Struct:
p.stype = typ
p.isMarshaler = isMarshaler(typ)
p.isUnmarshaler = isUnmarshaler(typ)
if p.Wire == "bytes" {
p.enc = (*Buffer).enc_ref_struct_message
p.dec = (*Buffer).dec_ref_struct_message
p.size = size_ref_struct_message
} else {
fmt.Fprintf(os.Stderr, "proto: no coders for struct %T\n", typ)
}
default:
return false
}
return true
}
func (p *Properties) setSliceOfNonPointerStructs(typ reflect.Type) { func (p *Properties) setSliceOfNonPointerStructs(typ reflect.Type) {
t2 := typ.Elem() t2 := typ.Elem()
p.sstype = typ p.sstype = typ

View File

@ -0,0 +1,122 @@
// Code generated by protoc-gen-gogo.
// source: proto3_proto/proto3.proto
// DO NOT EDIT!
/*
Package proto3_proto is a generated protocol buffer package.
It is generated from these files:
proto3_proto/proto3.proto
It has these top-level messages:
Message
Nested
MessageWithMap
*/
package proto3_proto
import proto "github.com/gogo/protobuf/proto"
import testdata "github.com/gogo/protobuf/proto/testdata"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
type Message_Humour int32
const (
Message_UNKNOWN Message_Humour = 0
Message_PUNS Message_Humour = 1
Message_SLAPSTICK Message_Humour = 2
Message_BILL_BAILEY Message_Humour = 3
)
var Message_Humour_name = map[int32]string{
0: "UNKNOWN",
1: "PUNS",
2: "SLAPSTICK",
3: "BILL_BAILEY",
}
var Message_Humour_value = map[string]int32{
"UNKNOWN": 0,
"PUNS": 1,
"SLAPSTICK": 2,
"BILL_BAILEY": 3,
}
func (x Message_Humour) String() string {
return proto.EnumName(Message_Humour_name, int32(x))
}
type Message struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Hilarity Message_Humour `protobuf:"varint,2,opt,name=hilarity,proto3,enum=proto3_proto.Message_Humour" json:"hilarity,omitempty"`
HeightInCm uint32 `protobuf:"varint,3,opt,name=height_in_cm,proto3" json:"height_in_cm,omitempty"`
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
ResultCount int64 `protobuf:"varint,7,opt,name=result_count,proto3" json:"result_count,omitempty"`
TrueScotsman bool `protobuf:"varint,8,opt,name=true_scotsman,proto3" json:"true_scotsman,omitempty"`
Score float32 `protobuf:"fixed32,9,opt,name=score,proto3" json:"score,omitempty"`
Key []uint64 `protobuf:"varint,5,rep,name=key" json:"key,omitempty"`
Nested *Nested `protobuf:"bytes,6,opt,name=nested" json:"nested,omitempty"`
Terrain map[string]*Nested `protobuf:"bytes,10,rep,name=terrain" json:"terrain,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
Proto2Field *testdata.SubDefaults `protobuf:"bytes,11,opt,name=proto2_field" json:"proto2_field,omitempty"`
Proto2Value map[string]*testdata.SubDefaults `protobuf:"bytes,13,rep,name=proto2_value" json:"proto2_value,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
}
func (m *Message) Reset() { *m = Message{} }
func (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {}
func (m *Message) GetNested() *Nested {
if m != nil {
return m.Nested
}
return nil
}
func (m *Message) GetTerrain() map[string]*Nested {
if m != nil {
return m.Terrain
}
return nil
}
func (m *Message) GetProto2Field() *testdata.SubDefaults {
if m != nil {
return m.Proto2Field
}
return nil
}
func (m *Message) GetProto2Value() map[string]*testdata.SubDefaults {
if m != nil {
return m.Proto2Value
}
return nil
}
type Nested struct {
Bunny string `protobuf:"bytes,1,opt,name=bunny,proto3" json:"bunny,omitempty"`
}
func (m *Nested) Reset() { *m = Nested{} }
func (m *Nested) String() string { return proto.CompactTextString(m) }
func (*Nested) ProtoMessage() {}
type MessageWithMap struct {
ByteMapping map[bool][]byte `protobuf:"bytes,1,rep,name=byte_mapping" json:"byte_mapping,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
func (m *MessageWithMap) Reset() { *m = MessageWithMap{} }
func (m *MessageWithMap) String() string { return proto.CompactTextString(m) }
func (*MessageWithMap) ProtoMessage() {}
func (m *MessageWithMap) GetByteMapping() map[bool][]byte {
if m != nil {
return m.ByteMapping
}
return nil
}
func init() {
proto.RegisterEnum("proto3_proto.Message_Humour", Message_Humour_name, Message_Humour_value)
}

View File

@ -0,0 +1,68 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2014 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package proto3_proto;
import "github.com/gogo/protobuf/proto/testdata/test.proto";
message Message {
enum Humour {
UNKNOWN = 0;
PUNS = 1;
SLAPSTICK = 2;
BILL_BAILEY = 3;
}
string name = 1;
Humour hilarity = 2;
uint32 height_in_cm = 3;
bytes data = 4;
int64 result_count = 7;
bool true_scotsman = 8;
float score = 9;
repeated uint64 key = 5;
Nested nested = 6;
map<string, Nested> terrain = 10;
testdata.SubDefaults proto2_field = 11;
map<string, testdata.SubDefaults> proto2_value = 13;
}
message Nested {
string bunny = 1;
}
message MessageWithMap {
map<bool, bytes> byte_mapping = 1;
}

View File

@ -0,0 +1,125 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2014 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package proto_test
import (
"testing"
"github.com/gogo/protobuf/proto"
pb "github.com/gogo/protobuf/proto/proto3_proto"
tpb "github.com/gogo/protobuf/proto/testdata"
)
func TestProto3ZeroValues(t *testing.T) {
tests := []struct {
desc string
m proto.Message
}{
{"zero message", &pb.Message{}},
{"empty bytes field", &pb.Message{Data: []byte{}}},
}
for _, test := range tests {
b, err := proto.Marshal(test.m)
if err != nil {
t.Errorf("%s: proto.Marshal: %v", test.desc, err)
continue
}
if len(b) > 0 {
t.Errorf("%s: Encoding is non-empty: %q", test.desc, b)
}
}
}
func TestRoundTripProto3(t *testing.T) {
m := &pb.Message{
Name: "David", // (2 | 1<<3): 0x0a 0x05 "David"
Hilarity: pb.Message_PUNS, // (0 | 2<<3): 0x10 0x01
HeightInCm: 178, // (0 | 3<<3): 0x18 0xb2 0x01
Data: []byte("roboto"), // (2 | 4<<3): 0x20 0x06 "roboto"
ResultCount: 47, // (0 | 7<<3): 0x38 0x2f
TrueScotsman: true, // (0 | 8<<3): 0x40 0x01
Score: 8.1, // (5 | 9<<3): 0x4d <8.1>
Key: []uint64{1, 0xdeadbeef},
Nested: &pb.Nested{
Bunny: "Monty",
},
}
t.Logf(" m: %v", m)
b, err := proto.Marshal(m)
if err != nil {
t.Fatalf("proto.Marshal: %v", err)
}
t.Logf(" b: %q", b)
m2 := new(pb.Message)
if err := proto.Unmarshal(b, m2); err != nil {
t.Fatalf("proto.Unmarshal: %v", err)
}
t.Logf("m2: %v", m2)
if !proto.Equal(m, m2) {
t.Errorf("proto.Equal returned false:\n m: %v\nm2: %v", m, m2)
}
}
func TestProto3SetDefaults(t *testing.T) {
in := &pb.Message{
Terrain: map[string]*pb.Nested{
"meadow": new(pb.Nested),
},
Proto2Field: new(tpb.SubDefaults),
Proto2Value: map[string]*tpb.SubDefaults{
"badlands": new(tpb.SubDefaults),
},
}
got := proto.Clone(in).(*pb.Message)
proto.SetDefaults(got)
// There are no defaults in proto3. Everything should be the zero value, but
// we need to remember to set defaults for nested proto2 messages.
want := &pb.Message{
Terrain: map[string]*pb.Nested{
"meadow": new(pb.Nested),
},
Proto2Field: &tpb.SubDefaults{N: proto.Int64(7)},
Proto2Value: map[string]*tpb.SubDefaults{
"badlands": {N: proto.Int64(7)},
},
}
if !proto.Equal(got, want) {
t.Errorf("with in = %v\nproto.SetDefaults(in) =>\ngot %v\nwant %v", in, got, want)
}
}

View File

@ -33,10 +33,12 @@ package proto_test
import ( import (
"log" "log"
"strings"
"testing" "testing"
pb "./testdata"
. "github.com/gogo/protobuf/proto" . "github.com/gogo/protobuf/proto"
proto3pb "github.com/gogo/protobuf/proto/proto3_proto"
pb "github.com/gogo/protobuf/proto/testdata"
) )
var messageWithExtension1 = &pb.MyMessage{Count: Int32(7)} var messageWithExtension1 = &pb.MyMessage{Count: Int32(7)}
@ -102,6 +104,26 @@ var SizeTests = []struct {
{"unrecognized", &pb.MoreRepeated{XXX_unrecognized: []byte{13<<3 | 0, 4}}}, {"unrecognized", &pb.MoreRepeated{XXX_unrecognized: []byte{13<<3 | 0, 4}}},
{"extension (unencoded)", messageWithExtension1}, {"extension (unencoded)", messageWithExtension1},
{"extension (encoded)", messageWithExtension3}, {"extension (encoded)", messageWithExtension3},
// proto3 message
{"proto3 empty", &proto3pb.Message{}},
{"proto3 bool", &proto3pb.Message{TrueScotsman: true}},
{"proto3 int64", &proto3pb.Message{ResultCount: 1}},
{"proto3 uint32", &proto3pb.Message{HeightInCm: 123}},
{"proto3 float", &proto3pb.Message{Score: 12.6}},
{"proto3 string", &proto3pb.Message{Name: "Snezana"}},
{"proto3 bytes", &proto3pb.Message{Data: []byte("wowsa")}},
{"proto3 bytes, empty", &proto3pb.Message{Data: []byte{}}},
{"proto3 enum", &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}},
{"proto3 map field with empty bytes", &proto3pb.MessageWithMap{ByteMapping: map[bool][]byte{false: {}}}},
{"map field", &pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob", 7: "Andrew"}}},
{"map field with message", &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{0x7001: {F: Float64(2.0)}}}},
{"map field with bytes", &pb.MessageWithMap{ByteMapping: map[bool][]byte{true: []byte("this time for sure")}}},
{"map field with empty bytes", &pb.MessageWithMap{ByteMapping: map[bool][]byte{true: {}}}},
{"map field with big entry", &pb.MessageWithMap{NameMapping: map[int32]string{8: strings.Repeat("x", 125)}}},
{"map field with big key and val", &pb.MessageWithMap{StrToStr: map[string]string{strings.Repeat("x", 70): strings.Repeat("y", 70)}}},
{"map field with big numeric key", &pb.MessageWithMap{NameMapping: map[int32]string{0xf00d: "om nom nom"}}},
} }
func TestSize(t *testing.T) { func TestSize(t *testing.T) {

View File

@ -80,7 +80,7 @@ func Skip(data []byte) (n int, err error) {
return index, nil return index, nil
case 3: case 3:
for { for {
var wire uint64 var innerWire uint64
var start int = index var start int = index
for shift := uint(0); ; shift += 7 { for shift := uint(0); ; shift += 7 {
if index >= l { if index >= l {
@ -88,13 +88,13 @@ func Skip(data []byte) (n int, err error) {
} }
b := data[index] b := data[index]
index++ index++
wire |= (uint64(b) & 0x7F) << shift innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 { if b < 0x80 {
break break
} }
} }
wireType := int(wire & 0x7) innerWireType := int(innerWire & 0x7)
if wireType == 4 { if innerWireType == 4 {
break break
} }
next, err := Skip(data[start:]) next, err := Skip(data[start:])

View File

@ -32,16 +32,6 @@
all: regenerate all: regenerate
regenerate: regenerate:
rm -f test.pb.go go install github.com/gogo/protobuf/protoc-gen-gogo/version/protoc-min-version
protoc --gogo_out=. test.proto protoc-min-version --version="3.0.0" --gogo_out=. test.proto
# The following rules are just aids to development. Not needed for typical testing.
diff: regenerate
hg diff test.pb.go
restore:
cp test.pb.go.golden test.pb.go
preserve:
cp test.pb.go test.pb.go.golden

View File

@ -22,6 +22,7 @@ It has these top-level messages:
OtherMessage OtherMessage
MyMessage MyMessage
Ext Ext
DefaultsMessage
MyMessageSet MyMessageSet
Empty Empty
MessageList MessageList
@ -33,6 +34,7 @@ It has these top-level messages:
GroupOld GroupOld
GroupNew GroupNew
FloatingPoint FloatingPoint
MessageWithMap
*/ */
package testdata package testdata
@ -180,6 +182,42 @@ func (x *MyMessage_Color) UnmarshalJSON(data []byte) error {
return nil return nil
} }
type DefaultsMessage_DefaultsEnum int32
const (
DefaultsMessage_ZERO DefaultsMessage_DefaultsEnum = 0
DefaultsMessage_ONE DefaultsMessage_DefaultsEnum = 1
DefaultsMessage_TWO DefaultsMessage_DefaultsEnum = 2
)
var DefaultsMessage_DefaultsEnum_name = map[int32]string{
0: "ZERO",
1: "ONE",
2: "TWO",
}
var DefaultsMessage_DefaultsEnum_value = map[string]int32{
"ZERO": 0,
"ONE": 1,
"TWO": 2,
}
func (x DefaultsMessage_DefaultsEnum) Enum() *DefaultsMessage_DefaultsEnum {
p := new(DefaultsMessage_DefaultsEnum)
*p = x
return p
}
func (x DefaultsMessage_DefaultsEnum) String() string {
return proto.EnumName(DefaultsMessage_DefaultsEnum_name, int32(x))
}
func (x *DefaultsMessage_DefaultsEnum) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(DefaultsMessage_DefaultsEnum_value, data, "DefaultsMessage_DefaultsEnum")
if err != nil {
return err
}
*x = DefaultsMessage_DefaultsEnum(value)
return nil
}
type Defaults_Color int32 type Defaults_Color int32
const ( const (
@ -1401,6 +1439,29 @@ var E_Ext_Number = &proto.ExtensionDesc{
Tag: "varint,105,opt,name=number", Tag: "varint,105,opt,name=number",
} }
type DefaultsMessage struct {
XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"`
}
func (m *DefaultsMessage) Reset() { *m = DefaultsMessage{} }
func (m *DefaultsMessage) String() string { return proto.CompactTextString(m) }
func (*DefaultsMessage) ProtoMessage() {}
var extRange_DefaultsMessage = []proto.ExtensionRange{
{100, 536870911},
}
func (*DefaultsMessage) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_DefaultsMessage
}
func (m *DefaultsMessage) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
type MyMessageSet struct { type MyMessageSet struct {
XXX_extensions map[int32]proto.Extension `json:"-"` XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -1885,6 +1946,46 @@ func (m *FloatingPoint) GetF() float64 {
return 0 return 0
} }
type MessageWithMap struct {
NameMapping map[int32]string `protobuf:"bytes,1,rep,name=name_mapping" json:"name_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
MsgMapping map[int64]*FloatingPoint `protobuf:"bytes,2,rep,name=msg_mapping" json:"msg_mapping,omitempty" protobuf_key:"zigzag64,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
ByteMapping map[bool][]byte `protobuf:"bytes,3,rep,name=byte_mapping" json:"byte_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
StrToStr map[string]string `protobuf:"bytes,4,rep,name=str_to_str" json:"str_to_str,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MessageWithMap) Reset() { *m = MessageWithMap{} }
func (m *MessageWithMap) String() string { return proto.CompactTextString(m) }
func (*MessageWithMap) ProtoMessage() {}
func (m *MessageWithMap) GetNameMapping() map[int32]string {
if m != nil {
return m.NameMapping
}
return nil
}
func (m *MessageWithMap) GetMsgMapping() map[int64]*FloatingPoint {
if m != nil {
return m.MsgMapping
}
return nil
}
func (m *MessageWithMap) GetByteMapping() map[bool][]byte {
if m != nil {
return m.ByteMapping
}
return nil
}
func (m *MessageWithMap) GetStrToStr() map[string]string {
if m != nil {
return m.StrToStr
}
return nil
}
var E_Greeting = &proto.ExtensionDesc{ var E_Greeting = &proto.ExtensionDesc{
ExtendedType: (*MyMessage)(nil), ExtendedType: (*MyMessage)(nil),
ExtensionType: ([]string)(nil), ExtensionType: ([]string)(nil),
@ -1893,6 +1994,262 @@ var E_Greeting = &proto.ExtensionDesc{
Tag: "bytes,106,rep,name=greeting", Tag: "bytes,106,rep,name=greeting",
} }
var E_NoDefaultDouble = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*float64)(nil),
Field: 101,
Name: "testdata.no_default_double",
Tag: "fixed64,101,opt,name=no_default_double",
}
var E_NoDefaultFloat = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*float32)(nil),
Field: 102,
Name: "testdata.no_default_float",
Tag: "fixed32,102,opt,name=no_default_float",
}
var E_NoDefaultInt32 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*int32)(nil),
Field: 103,
Name: "testdata.no_default_int32",
Tag: "varint,103,opt,name=no_default_int32",
}
var E_NoDefaultInt64 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*int64)(nil),
Field: 104,
Name: "testdata.no_default_int64",
Tag: "varint,104,opt,name=no_default_int64",
}
var E_NoDefaultUint32 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*uint32)(nil),
Field: 105,
Name: "testdata.no_default_uint32",
Tag: "varint,105,opt,name=no_default_uint32",
}
var E_NoDefaultUint64 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*uint64)(nil),
Field: 106,
Name: "testdata.no_default_uint64",
Tag: "varint,106,opt,name=no_default_uint64",
}
var E_NoDefaultSint32 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*int32)(nil),
Field: 107,
Name: "testdata.no_default_sint32",
Tag: "zigzag32,107,opt,name=no_default_sint32",
}
var E_NoDefaultSint64 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*int64)(nil),
Field: 108,
Name: "testdata.no_default_sint64",
Tag: "zigzag64,108,opt,name=no_default_sint64",
}
var E_NoDefaultFixed32 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*uint32)(nil),
Field: 109,
Name: "testdata.no_default_fixed32",
Tag: "fixed32,109,opt,name=no_default_fixed32",
}
var E_NoDefaultFixed64 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*uint64)(nil),
Field: 110,
Name: "testdata.no_default_fixed64",
Tag: "fixed64,110,opt,name=no_default_fixed64",
}
var E_NoDefaultSfixed32 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*int32)(nil),
Field: 111,
Name: "testdata.no_default_sfixed32",
Tag: "fixed32,111,opt,name=no_default_sfixed32",
}
var E_NoDefaultSfixed64 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*int64)(nil),
Field: 112,
Name: "testdata.no_default_sfixed64",
Tag: "fixed64,112,opt,name=no_default_sfixed64",
}
var E_NoDefaultBool = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*bool)(nil),
Field: 113,
Name: "testdata.no_default_bool",
Tag: "varint,113,opt,name=no_default_bool",
}
var E_NoDefaultString = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*string)(nil),
Field: 114,
Name: "testdata.no_default_string",
Tag: "bytes,114,opt,name=no_default_string",
}
var E_NoDefaultBytes = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: ([]byte)(nil),
Field: 115,
Name: "testdata.no_default_bytes",
Tag: "bytes,115,opt,name=no_default_bytes",
}
var E_NoDefaultEnum = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*DefaultsMessage_DefaultsEnum)(nil),
Field: 116,
Name: "testdata.no_default_enum",
Tag: "varint,116,opt,name=no_default_enum,enum=testdata.DefaultsMessage_DefaultsEnum",
}
var E_DefaultDouble = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*float64)(nil),
Field: 201,
Name: "testdata.default_double",
Tag: "fixed64,201,opt,name=default_double,def=3.1415",
}
var E_DefaultFloat = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*float32)(nil),
Field: 202,
Name: "testdata.default_float",
Tag: "fixed32,202,opt,name=default_float,def=3.14",
}
var E_DefaultInt32 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*int32)(nil),
Field: 203,
Name: "testdata.default_int32",
Tag: "varint,203,opt,name=default_int32,def=42",
}
var E_DefaultInt64 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*int64)(nil),
Field: 204,
Name: "testdata.default_int64",
Tag: "varint,204,opt,name=default_int64,def=43",
}
var E_DefaultUint32 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*uint32)(nil),
Field: 205,
Name: "testdata.default_uint32",
Tag: "varint,205,opt,name=default_uint32,def=44",
}
var E_DefaultUint64 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*uint64)(nil),
Field: 206,
Name: "testdata.default_uint64",
Tag: "varint,206,opt,name=default_uint64,def=45",
}
var E_DefaultSint32 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*int32)(nil),
Field: 207,
Name: "testdata.default_sint32",
Tag: "zigzag32,207,opt,name=default_sint32,def=46",
}
var E_DefaultSint64 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*int64)(nil),
Field: 208,
Name: "testdata.default_sint64",
Tag: "zigzag64,208,opt,name=default_sint64,def=47",
}
var E_DefaultFixed32 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*uint32)(nil),
Field: 209,
Name: "testdata.default_fixed32",
Tag: "fixed32,209,opt,name=default_fixed32,def=48",
}
var E_DefaultFixed64 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*uint64)(nil),
Field: 210,
Name: "testdata.default_fixed64",
Tag: "fixed64,210,opt,name=default_fixed64,def=49",
}
var E_DefaultSfixed32 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*int32)(nil),
Field: 211,
Name: "testdata.default_sfixed32",
Tag: "fixed32,211,opt,name=default_sfixed32,def=50",
}
var E_DefaultSfixed64 = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*int64)(nil),
Field: 212,
Name: "testdata.default_sfixed64",
Tag: "fixed64,212,opt,name=default_sfixed64,def=51",
}
var E_DefaultBool = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*bool)(nil),
Field: 213,
Name: "testdata.default_bool",
Tag: "varint,213,opt,name=default_bool,def=1",
}
var E_DefaultString = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*string)(nil),
Field: 214,
Name: "testdata.default_string",
Tag: "bytes,214,opt,name=default_string,def=Hello, string",
}
var E_DefaultBytes = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: ([]byte)(nil),
Field: 215,
Name: "testdata.default_bytes",
Tag: "bytes,215,opt,name=default_bytes,def=Hello, bytes",
}
var E_DefaultEnum = &proto.ExtensionDesc{
ExtendedType: (*DefaultsMessage)(nil),
ExtensionType: (*DefaultsMessage_DefaultsEnum)(nil),
Field: 216,
Name: "testdata.default_enum",
Tag: "varint,216,opt,name=default_enum,enum=testdata.DefaultsMessage_DefaultsEnum,def=1",
}
var E_X201 = &proto.ExtensionDesc{ var E_X201 = &proto.ExtensionDesc{
ExtendedType: (*MyMessageSet)(nil), ExtendedType: (*MyMessageSet)(nil),
ExtensionType: (*Empty)(nil), ExtensionType: (*Empty)(nil),
@ -2297,12 +2654,45 @@ func init() {
proto.RegisterEnum("testdata.FOO", FOO_name, FOO_value) proto.RegisterEnum("testdata.FOO", FOO_name, FOO_value)
proto.RegisterEnum("testdata.GoTest_KIND", GoTest_KIND_name, GoTest_KIND_value) proto.RegisterEnum("testdata.GoTest_KIND", GoTest_KIND_name, GoTest_KIND_value)
proto.RegisterEnum("testdata.MyMessage_Color", MyMessage_Color_name, MyMessage_Color_value) proto.RegisterEnum("testdata.MyMessage_Color", MyMessage_Color_name, MyMessage_Color_value)
proto.RegisterEnum("testdata.DefaultsMessage_DefaultsEnum", DefaultsMessage_DefaultsEnum_name, DefaultsMessage_DefaultsEnum_value)
proto.RegisterEnum("testdata.Defaults_Color", Defaults_Color_name, Defaults_Color_value) proto.RegisterEnum("testdata.Defaults_Color", Defaults_Color_name, Defaults_Color_value)
proto.RegisterEnum("testdata.RepeatedEnum_Color", RepeatedEnum_Color_name, RepeatedEnum_Color_value) proto.RegisterEnum("testdata.RepeatedEnum_Color", RepeatedEnum_Color_name, RepeatedEnum_Color_value)
proto.RegisterExtension(E_Ext_More) proto.RegisterExtension(E_Ext_More)
proto.RegisterExtension(E_Ext_Text) proto.RegisterExtension(E_Ext_Text)
proto.RegisterExtension(E_Ext_Number) proto.RegisterExtension(E_Ext_Number)
proto.RegisterExtension(E_Greeting) proto.RegisterExtension(E_Greeting)
proto.RegisterExtension(E_NoDefaultDouble)
proto.RegisterExtension(E_NoDefaultFloat)
proto.RegisterExtension(E_NoDefaultInt32)
proto.RegisterExtension(E_NoDefaultInt64)
proto.RegisterExtension(E_NoDefaultUint32)
proto.RegisterExtension(E_NoDefaultUint64)
proto.RegisterExtension(E_NoDefaultSint32)
proto.RegisterExtension(E_NoDefaultSint64)
proto.RegisterExtension(E_NoDefaultFixed32)
proto.RegisterExtension(E_NoDefaultFixed64)
proto.RegisterExtension(E_NoDefaultSfixed32)
proto.RegisterExtension(E_NoDefaultSfixed64)
proto.RegisterExtension(E_NoDefaultBool)
proto.RegisterExtension(E_NoDefaultString)
proto.RegisterExtension(E_NoDefaultBytes)
proto.RegisterExtension(E_NoDefaultEnum)
proto.RegisterExtension(E_DefaultDouble)
proto.RegisterExtension(E_DefaultFloat)
proto.RegisterExtension(E_DefaultInt32)
proto.RegisterExtension(E_DefaultInt64)
proto.RegisterExtension(E_DefaultUint32)
proto.RegisterExtension(E_DefaultUint64)
proto.RegisterExtension(E_DefaultSint32)
proto.RegisterExtension(E_DefaultSint64)
proto.RegisterExtension(E_DefaultFixed32)
proto.RegisterExtension(E_DefaultFixed64)
proto.RegisterExtension(E_DefaultSfixed32)
proto.RegisterExtension(E_DefaultSfixed64)
proto.RegisterExtension(E_DefaultBool)
proto.RegisterExtension(E_DefaultString)
proto.RegisterExtension(E_DefaultBytes)
proto.RegisterExtension(E_DefaultEnum)
proto.RegisterExtension(E_X201) proto.RegisterExtension(E_X201)
proto.RegisterExtension(E_X202) proto.RegisterExtension(E_X202)
proto.RegisterExtension(E_X203) proto.RegisterExtension(E_X203)

View File

@ -277,6 +277,51 @@ extend MyMessage {
repeated string greeting = 106; repeated string greeting = 106;
} }
message DefaultsMessage {
enum DefaultsEnum {
ZERO = 0;
ONE = 1;
TWO = 2;
};
extensions 100 to max;
}
extend DefaultsMessage {
optional double no_default_double = 101;
optional float no_default_float = 102;
optional int32 no_default_int32 = 103;
optional int64 no_default_int64 = 104;
optional uint32 no_default_uint32 = 105;
optional uint64 no_default_uint64 = 106;
optional sint32 no_default_sint32 = 107;
optional sint64 no_default_sint64 = 108;
optional fixed32 no_default_fixed32 = 109;
optional fixed64 no_default_fixed64 = 110;
optional sfixed32 no_default_sfixed32 = 111;
optional sfixed64 no_default_sfixed64 = 112;
optional bool no_default_bool = 113;
optional string no_default_string = 114;
optional bytes no_default_bytes = 115;
optional DefaultsMessage.DefaultsEnum no_default_enum = 116;
optional double default_double = 201 [default = 3.1415];
optional float default_float = 202 [default = 3.14];
optional int32 default_int32 = 203 [default = 42];
optional int64 default_int64 = 204 [default = 43];
optional uint32 default_uint32 = 205 [default = 44];
optional uint64 default_uint64 = 206 [default = 45];
optional sint32 default_sint32 = 207 [default = 46];
optional sint64 default_sint64 = 208 [default = 47];
optional fixed32 default_fixed32 = 209 [default = 48];
optional fixed64 default_fixed64 = 210 [default = 49];
optional sfixed32 default_sfixed32 = 211 [default = 50];
optional sfixed64 default_sfixed64 = 212 [default = 51];
optional bool default_bool = 213 [default = true];
optional string default_string = 214 [default = "Hello, string"];
optional bytes default_bytes = 215 [default = "Hello, bytes"];
optional DefaultsMessage.DefaultsEnum default_enum = 216 [default = ONE];
}
message MyMessageSet { message MyMessageSet {
option message_set_wire_format = true; option message_set_wire_format = true;
extensions 100 to max; extensions 100 to max;
@ -426,3 +471,10 @@ message GroupNew {
message FloatingPoint { message FloatingPoint {
required double f = 1; required double f = 1;
} }
message MessageWithMap {
map<int32, string> name_mapping = 1;
map<sint64, FloatingPoint> msg_mapping = 2;
map<bool, bytes> byte_mapping = 3;
map<string, string> str_to_str = 4;
}

View File

@ -46,7 +46,6 @@ import (
"io" "io"
"log" "log"
"math" "math"
"os"
"reflect" "reflect"
"sort" "sort"
"strings" "strings"
@ -253,6 +252,84 @@ func writeStruct(w *textWriter, sv reflect.Value) error {
} }
continue continue
} }
if fv.Kind() == reflect.Map {
// Map fields are rendered as a repeated struct with key/value fields.
keys := fv.MapKeys() // TODO: should we sort these for deterministic output?
sort.Sort(mapKeys(keys))
for _, key := range keys {
val := fv.MapIndex(key)
if err := writeName(w, props); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte(' '); err != nil {
return err
}
}
// open struct
if err := w.WriteByte('<'); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte('\n'); err != nil {
return err
}
}
w.indent()
// key
if _, err := w.WriteString("key:"); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte(' '); err != nil {
return err
}
}
if err := writeAny(w, key, props.mkeyprop); err != nil {
return err
}
if err := w.WriteByte('\n'); err != nil {
return err
}
// nil values aren't legal, but we can avoid panicking because of them.
if val.Kind() != reflect.Ptr || !val.IsNil() {
// value
if _, err := w.WriteString("value:"); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte(' '); err != nil {
return err
}
}
if err := writeAny(w, val, props.mvalprop); err != nil {
return err
}
if err := w.WriteByte('\n'); err != nil {
return err
}
}
// close struct
w.unindent()
if err := w.WriteByte('>'); err != nil {
return err
}
if err := w.WriteByte('\n'); err != nil {
return err
}
}
continue
}
if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 {
// empty bytes field
continue
}
if props.proto3 && fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice {
// proto3 non-repeated scalar field; skip if zero value
if isProto3Zero(fv) {
continue
}
}
if err := writeName(w, props); err != nil { if err := writeName(w, props); err != nil {
return err return err
@ -354,7 +431,7 @@ func writeAny(w *textWriter, v reflect.Value, props *Properties) error {
switch v.Kind() { switch v.Kind() {
case reflect.Slice: case reflect.Slice:
// Should only be a []byte; repeated fields are handled in writeStruct. // Should only be a []byte; repeated fields are handled in writeStruct.
if err := writeString(w, string(v.Interface().([]byte))); err != nil { if err := writeString(w, string(v.Bytes())); err != nil {
return err return err
} }
case reflect.String: case reflect.String:
@ -607,10 +684,7 @@ func writeExtensions(w *textWriter, pv reflect.Value) error {
pb, err := GetExtension(ep, desc) pb, err := GetExtension(ep, desc)
if err != nil { if err != nil {
if _, err := fmt.Fprintln(os.Stderr, "proto: failed getting extension: ", err); err != nil { return fmt.Errorf("failed getting extension: %v", err)
return err
}
continue
} }
// Repeated extensions will appear as a slice. // Repeated extensions will appear as a slice.

View File

@ -179,7 +179,7 @@ func (p *textParser) advance() {
} }
unq, err := unquoteC(p.s[1:i], rune(p.s[0])) unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
if err != nil { if err != nil {
p.errorf("invalid quoted string %v", p.s[0:i+1]) p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
return return
} }
p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)] p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
@ -360,6 +360,18 @@ func (p *textParser) next() *token {
return &p.cur return &p.cur
} }
func (p *textParser) consumeToken(s string) error {
tok := p.next()
if tok.err != nil {
return tok.err
}
if tok.value != s {
p.back()
return p.errorf("expected %q, found %q", s, tok.value)
}
return nil
}
// Return a RequiredNotSetError indicating which required field was not set. // Return a RequiredNotSetError indicating which required field was not set.
func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError { func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError {
st := sv.Type() st := sv.Type()
@ -414,6 +426,10 @@ func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseEr
if typ.Elem().Kind() != reflect.Ptr { if typ.Elem().Kind() != reflect.Ptr {
break break
} }
} else if typ.Kind() == reflect.String {
// The proto3 exception is for a string field,
// which requires a colon.
break
} }
needColon = false needColon = false
} }
@ -519,6 +535,66 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
dst := sv.Field(fi) dst := sv.Field(fi)
if dst.Kind() == reflect.Map {
// Consume any colon.
if err := p.checkForColon(props, dst.Type()); err != nil {
return err
}
// Construct the map if it doesn't already exist.
if dst.IsNil() {
dst.Set(reflect.MakeMap(dst.Type()))
}
key := reflect.New(dst.Type().Key()).Elem()
val := reflect.New(dst.Type().Elem()).Elem()
// The map entry should be this sequence of tokens:
// < key : KEY value : VALUE >
// Technically the "key" and "value" could come in any order,
// but in practice they won't.
tok := p.next()
var terminator string
switch tok.value {
case "<":
terminator = ">"
case "{":
terminator = "}"
default:
return p.errorf("expected '{' or '<', found %q", tok.value)
}
if err := p.consumeToken("key"); err != nil {
return err
}
if err := p.consumeToken(":"); err != nil {
return err
}
if err := p.readAny(key, props.mkeyprop); err != nil {
return err
}
if err := p.consumeOptionalSeparator(); err != nil {
return err
}
if err := p.consumeToken("value"); err != nil {
return err
}
if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil {
return err
}
if err := p.readAny(val, props.mvalprop); err != nil {
return err
}
if err := p.consumeOptionalSeparator(); err != nil {
return err
}
if err := p.consumeToken(terminator); err != nil {
return err
}
dst.SetMapIndex(key, val)
continue
}
// Check that it's not already set if it's not a repeated field. // Check that it's not already set if it's not a repeated field.
if !props.Repeated && fieldSet[name] { if !props.Repeated && fieldSet[name] {
return p.errorf("non-repeated field %q was repeated", name) return p.errorf("non-repeated field %q was repeated", name)
@ -540,14 +616,10 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
} }
} }
// For backward compatibility, permit a semicolon or comma after a field. if err := p.consumeOptionalSeparator(); err != nil {
tok = p.next() return err
if tok.err != nil {
return tok.err
}
if tok.value != ";" && tok.value != "," {
p.back()
} }
} }
if reqCount > 0 { if reqCount > 0 {
@ -556,6 +628,19 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
return reqFieldErr return reqFieldErr
} }
// consumeOptionalSeparator consumes an optional semicolon or comma.
// It is used in readStruct to provide backward compatibility.
func (p *textParser) consumeOptionalSeparator() error {
tok := p.next()
if tok.err != nil {
return tok.err
}
if tok.value != ";" && tok.value != "," {
p.back()
}
return nil
}
func (p *textParser) readAny(v reflect.Value, props *Properties) error { func (p *textParser) readAny(v reflect.Value, props *Properties) error {
tok := p.next() tok := p.next()
if tok.err != nil { if tok.err != nil {

View File

@ -36,8 +36,9 @@ import (
"reflect" "reflect"
"testing" "testing"
. "./testdata"
. "github.com/gogo/protobuf/proto" . "github.com/gogo/protobuf/proto"
proto3pb "github.com/gogo/protobuf/proto/proto3_proto"
. "github.com/gogo/protobuf/proto/testdata"
) )
type UnmarshalTextTest struct { type UnmarshalTextTest struct {
@ -151,7 +152,7 @@ var unMarshalTextTests = []UnmarshalTextTest{
// Bad quoted string // Bad quoted string
{ {
in: `inner: < host: "\0" >` + "\n", in: `inner: < host: "\0" >` + "\n",
err: `line 1.15: invalid quoted string "\0"`, err: `line 1.15: invalid quoted string "\0": \0 requires 2 following digits`,
}, },
// Number too large for int64 // Number too large for int64
@ -443,6 +444,48 @@ func TestRepeatedEnum(t *testing.T) {
} }
} }
func TestProto3TextParsing(t *testing.T) {
m := new(proto3pb.Message)
const in = `name: "Wallace" true_scotsman: true`
want := &proto3pb.Message{
Name: "Wallace",
TrueScotsman: true,
}
if err := UnmarshalText(in, m); err != nil {
t.Fatal(err)
}
if !Equal(m, want) {
t.Errorf("\n got %v\nwant %v", m, want)
}
}
func TestMapParsing(t *testing.T) {
m := new(MessageWithMap)
const in = `name_mapping:<key:1234 value:"Feist"> name_mapping:<key:1 value:"Beatles">` +
`msg_mapping:<key:-4, value:<f: 2.0>,>` + // separating commas are okay
`msg_mapping<key:-2 value<f: 4.0>>` + // no colon after "value"
`byte_mapping:<key:true value:"so be it">`
want := &MessageWithMap{
NameMapping: map[int32]string{
1: "Beatles",
1234: "Feist",
},
MsgMapping: map[int64]*FloatingPoint{
-4: {F: Float64(2.0)},
-2: {F: Float64(4.0)},
},
ByteMapping: map[bool][]byte{
true: []byte("so be it"),
},
}
if err := UnmarshalText(in, m); err != nil {
t.Fatal(err)
}
if !Equal(m, want) {
t.Errorf("\n got %v\nwant %v", m, want)
}
}
var benchInput string var benchInput string
func init() { func init() {

View File

@ -41,7 +41,8 @@ import (
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
pb "./testdata" proto3pb "github.com/gogo/protobuf/proto/proto3_proto"
pb "github.com/gogo/protobuf/proto/testdata"
) )
// textMessage implements the methods that allow it to marshal and unmarshal // textMessage implements the methods that allow it to marshal and unmarshal
@ -406,3 +407,35 @@ Message <nil>
t.Errorf(" got: %s\nwant: %s", s, want) t.Errorf(" got: %s\nwant: %s", s, want)
} }
} }
func TestProto3Text(t *testing.T) {
tests := []struct {
m proto.Message
want string
}{
// zero message
{&proto3pb.Message{}, ``},
// zero message except for an empty byte slice
{&proto3pb.Message{Data: []byte{}}, ``},
// trivial case
{&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
// empty map
{&pb.MessageWithMap{}, ``},
// non-empty map; current map format is the same as a repeated struct
{
&pb.MessageWithMap{NameMapping: map[int32]string{1234: "Feist"}},
`name_mapping:<key:1234 value:"Feist" >`,
},
// map with nil value; not well-defined, but we shouldn't crash
{
&pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{7: nil}},
`msg_mapping:<key:7 >`,
},
}
for _, test := range tests {
got := strings.TrimSpace(test.m.String())
if got != test.want {
t.Errorf("\n got %s\nwant %s", got, test.want)
}
}
}

View File

@ -285,7 +285,7 @@ func (self *authenticateeProcess) mechanisms(ctx context.Context, from *upid.UPI
message := &mesos.AuthenticationStartMessage{ message := &mesos.AuthenticationStartMessage{
Mechanism: proto.String(selectedMech), Mechanism: proto.String(selectedMech),
Data: proto.String(string(data)), // may be nil, depends on init step Data: data, // may be nil, depends on init step
} }
if err := self.transport.Send(ctx, from, message); err != nil { if err := self.transport.Send(ctx, from, message); err != nil {

View File

@ -73,7 +73,6 @@ func TestAuthticatee_validLogin(t *testing.T) {
transport.On("Send", mock.Anything, &server, &mesos.AuthenticationStartMessage{ transport.On("Send", mock.Anything, &server, &mesos.AuthenticationStartMessage{
Mechanism: proto.String(crammd5.Name), Mechanism: proto.String(crammd5.Name),
Data: proto.String(""), // may be nil, depends on init step
}).Return(nil).Run(func(_ mock.Arguments) { }).Return(nil).Run(func(_ mock.Arguments) {
transport.Recv(&server, &mesos.AuthenticationStepMessage{ transport.Recv(&server, &mesos.AuthenticationStepMessage{
Data: []byte(`lsd;lfkgjs;dlfkgjs;dfklg`), Data: []byte(`lsd;lfkgjs;dlfkgjs;dfklg`),

View File

@ -0,0 +1,139 @@
// Code generated by protoc-gen-gogo.
// source: authentication.proto
// DO NOT EDIT!
/*
Package mesosproto is a generated protocol buffer package.
It is generated from these files:
authentication.proto
containerizer.proto
internal.proto
log.proto
mesos.proto
messages.proto
registry.proto
scheduler.proto
state.proto
It has these top-level messages:
AuthenticateMessage
AuthenticationMechanismsMessage
AuthenticationStartMessage
AuthenticationStepMessage
AuthenticationCompletedMessage
AuthenticationFailedMessage
AuthenticationErrorMessage
*/
package mesosproto
import proto "github.com/gogo/protobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
type AuthenticateMessage struct {
Pid *string `protobuf:"bytes,1,req,name=pid" json:"pid,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticateMessage) Reset() { *m = AuthenticateMessage{} }
func (m *AuthenticateMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticateMessage) ProtoMessage() {}
func (m *AuthenticateMessage) GetPid() string {
if m != nil && m.Pid != nil {
return *m.Pid
}
return ""
}
type AuthenticationMechanismsMessage struct {
Mechanisms []string `protobuf:"bytes,1,rep,name=mechanisms" json:"mechanisms,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticationMechanismsMessage) Reset() { *m = AuthenticationMechanismsMessage{} }
func (m *AuthenticationMechanismsMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticationMechanismsMessage) ProtoMessage() {}
func (m *AuthenticationMechanismsMessage) GetMechanisms() []string {
if m != nil {
return m.Mechanisms
}
return nil
}
type AuthenticationStartMessage struct {
Mechanism *string `protobuf:"bytes,1,req,name=mechanism" json:"mechanism,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticationStartMessage) Reset() { *m = AuthenticationStartMessage{} }
func (m *AuthenticationStartMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticationStartMessage) ProtoMessage() {}
func (m *AuthenticationStartMessage) GetMechanism() string {
if m != nil && m.Mechanism != nil {
return *m.Mechanism
}
return ""
}
func (m *AuthenticationStartMessage) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
type AuthenticationStepMessage struct {
Data []byte `protobuf:"bytes,1,req,name=data" json:"data,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticationStepMessage) Reset() { *m = AuthenticationStepMessage{} }
func (m *AuthenticationStepMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticationStepMessage) ProtoMessage() {}
func (m *AuthenticationStepMessage) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
type AuthenticationCompletedMessage struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticationCompletedMessage) Reset() { *m = AuthenticationCompletedMessage{} }
func (m *AuthenticationCompletedMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticationCompletedMessage) ProtoMessage() {}
type AuthenticationFailedMessage struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticationFailedMessage) Reset() { *m = AuthenticationFailedMessage{} }
func (m *AuthenticationFailedMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticationFailedMessage) ProtoMessage() {}
type AuthenticationErrorMessage struct {
Error *string `protobuf:"bytes,1,opt,name=error" json:"error,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticationErrorMessage) Reset() { *m = AuthenticationErrorMessage{} }
func (m *AuthenticationErrorMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticationErrorMessage) ProtoMessage() {}
func (m *AuthenticationErrorMessage) GetError() string {
if m != nil && m.Error != nil {
return *m.Error
}
return ""
}

View File

@ -0,0 +1,53 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package mesosproto;
import "mesos.proto";
message AuthenticateMessage {
required string pid = 1; // PID that needs to be authenticated.
}
message AuthenticationMechanismsMessage {
repeated string mechanisms = 1; // List of available SASL mechanisms.
}
message AuthenticationStartMessage {
required string mechanism = 1;
optional bytes data = 2;
}
message AuthenticationStepMessage {
required bytes data = 1;
}
message AuthenticationCompletedMessage {}
message AuthenticationFailedMessage {}
message AuthenticationErrorMessage {
optional string error = 1;
}

View File

@ -29,7 +29,7 @@ package mesosproto
import proto "github.com/gogo/protobuf/proto" import proto "github.com/gogo/protobuf/proto"
import math "math" import math "math"
// discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto/gogo.pb" // discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto"
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal
@ -200,6 +200,8 @@ func (m *Usage) GetContainerId() *ContainerID {
type Termination struct { type Termination struct {
// A container may be killed if it exceeds its resources; this will // A container may be killed if it exceeds its resources; this will
// be indicated by killed=true and described by the message string. // be indicated by killed=true and described by the message string.
// TODO(jaybuff): As part of MESOS-2035 we should remove killed and
// replace it with a TaskStatus::Reason.
Killed *bool `protobuf:"varint,1,req,name=killed" json:"killed,omitempty"` Killed *bool `protobuf:"varint,1,req,name=killed" json:"killed,omitempty"`
Message *string `protobuf:"bytes,2,req,name=message" json:"message,omitempty"` Message *string `protobuf:"bytes,2,req,name=message" json:"message,omitempty"`
// Exit status of the process. // Exit status of the process.
@ -250,6 +252,3 @@ func (m *Containers) GetContainers() []*ContainerID {
} }
return nil return nil
} }
func init() {
}

View File

@ -82,6 +82,8 @@ message Usage {
message Termination { message Termination {
// A container may be killed if it exceeds its resources; this will // A container may be killed if it exceeds its resources; this will
// be indicated by killed=true and described by the message string. // be indicated by killed=true and described by the message string.
// TODO(jaybuff): As part of MESOS-2035 we should remove killed and
// replace it with a TaskStatus::Reason.
required bool killed = 1; required bool killed = 1;
required string message = 2; required string message = 2;

View File

@ -7,7 +7,7 @@ package mesosproto
import proto "github.com/gogo/protobuf/proto" import proto "github.com/gogo/protobuf/proto"
import math "math" import math "math"
// discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto/gogo.pb" // discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto"
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal
@ -73,6 +73,3 @@ func (m *InternalAuthenticationResult) GetPid() string {
} }
return "" return ""
} }
func init() {
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -105,52 +105,83 @@ message ContainerID {
/** /**
* Describes a framework. The user field is used to determine the * Describes a framework.
* Unix user that an executor/task should be launched as. If the user
* field is set to an empty string Mesos will automagically set it
* to the current user. Note that the ID is only available after a
* framework has registered, however, it is included here in order to
* facilitate scheduler failover (i.e., if it is set then the
* MesosSchedulerDriver expects the scheduler is performing failover).
* The amount of time that the master will wait for the scheduler to
* failover before removing the framework is specified by
* failover_timeout. If checkpoint is set, framework pid, executor
* pids and status updates are checkpointed to disk by the slaves.
* Checkpointing allows a restarted slave to reconnect with old
* executors and recover status updates, at the cost of disk I/O.
* The role field is used to group frameworks for allocation
* decisions, depending on the allocation policy being used.
* If the hostname field is set to an empty string Mesos will
* automagically set it to the current hostname.
* The principal field should match the credential the framework uses
* in authentication. This field is used for framework API rate
* exporting and limiting and should be set even if authentication is
* not enabled if these features are desired.
* The webui_url field allows a framework to advertise its web UI, so
* that the Mesos web UI can link to it. It is expected to be a full
* URL, for example http://my-scheduler.example.com:8080/.
*/ */
message FrameworkInfo { message FrameworkInfo {
// Used to determine the Unix user that an executor or task should
// be launched as. If the user field is set to an empty string Mesos
// will automagically set it to the current user.
required string user = 1; required string user = 1;
// Name of the framework that shows up in the Mesos Web UI.
required string name = 2; required string name = 2;
// Note that 'id' is only available after a framework has
// registered, however, it is included here in order to facilitate
// scheduler failover (i.e., if it is set then the
// MesosSchedulerDriver expects the scheduler is performing
// failover).
optional FrameworkID id = 3; optional FrameworkID id = 3;
// The amount of time that the master will wait for the scheduler to
// failover before it tears down the framework by killing all its
// tasks/executors. This should be non-zero if a framework expects
// to reconnect after a failover and not lose its tasks/executors.
optional double failover_timeout = 4 [default = 0.0]; optional double failover_timeout = 4 [default = 0.0];
// If set, framework pid, executor pids and status updates are
// checkpointed to disk by the slaves. Checkpointing allows a
// restarted slave to reconnect with old executors and recover
// status updates, at the cost of disk I/O.
optional bool checkpoint = 5 [default = false]; optional bool checkpoint = 5 [default = false];
// Used to group frameworks for allocation decisions, depending on
// the allocation policy being used.
optional string role = 6 [default = "*"]; optional string role = 6 [default = "*"];
// Used to indicate the current host from which the scheduler is
// registered in the Mesos Web UI. If set to an empty string Mesos
// will automagically set it to the current hostname.
optional string hostname = 7; optional string hostname = 7;
// This field should match the credential's principal the framework
// uses for authentication. This field is used for framework API
// rate limiting and dynamic reservations. It should be set even
// if authentication is not enabled if these features are desired.
optional string principal = 8; optional string principal = 8;
// This field allows a framework to advertise its web UI, so that
// the Mesos web UI can link to it. It is expected to be a full URL,
// for example http://my-scheduler.example.com:8080/.
optional string webui_url = 9; optional string webui_url = 9;
message Capability {
enum Type {
// Receive offers with revocable resources. See 'Resource'
// message for details.
// TODO(vinod): This is currently a no-op.
REVOCABLE_RESOURCES = 1;
}
required Type type = 1;
}
// This field allows a framework to advertise its set of
// capabilities (e.g., ability to receive offers for revocable
// resources).
repeated Capability capabilities = 10;
} }
/** /**
* Describes a health check for a task or executor (or any arbitrary * Describes a health check for a task or executor (or any arbitrary
* process/command). A "strategy" is picked by specifying one of the * process/command). A "strategy" is picked by specifying one of the
* optional fields, currently only 'http' and 'command' are * optional fields; currently only 'command' is supported.
* supported. Specifying more than one strategy is an error. * Specifying more than one strategy is an error.
*/ */
message HealthCheck { message HealthCheck {
// Describes an HTTP health check. // Describes an HTTP health check. This is not fully implemented and not
// recommended for use - see MESOS-2533.
message HTTP { message HTTP {
// Port to send the HTTP request. // Port to send the HTTP request.
required uint32 port = 1; required uint32 port = 1;
@ -170,6 +201,7 @@ message HealthCheck {
// for specific data in the response. // for specific data in the response.
} }
// HTTP health check - not yet recommended for use, see MESOS-2533.
optional HTTP http = 1; optional HTTP http = 1;
// TODO(benh): Consider adding a URL health check strategy which // TODO(benh): Consider adding a URL health check strategy which
@ -177,12 +209,7 @@ message HealthCheck {
// encapsulates all the details in a single string field. // encapsulates all the details in a single string field.
// TODO(benh): Other possible health check strategies could include // TODO(benh): Other possible health check strategies could include
// one for TCP/UDP or a "command". A "command" could be running a // one for TCP/UDP.
// (shell) command to check the healthiness. We'd need to determine
// what arguments (or environment variables) we'd want to set so
// that the command could do it's job (i.e., do we want to expose
// the stdout/stderr and/or the pid to make checking for healthiness
// easier).
// Amount of time to wait until starting the health checks. // Amount of time to wait until starting the health checks.
optional double delay_seconds = 2 [default = 15.0]; optional double delay_seconds = 2 [default = 15.0];
@ -218,7 +245,24 @@ message CommandInfo {
message URI { message URI {
required string value = 1; required string value = 1;
optional bool executable = 2; optional bool executable = 2;
// In case the fetched file is recognized as an archive, extract
// its contents into the sandbox. Note that a cached archive is
// not copied from the cache to the sandbox in case extraction
// originates from an archive in the cache.
optional bool extract = 3 [default = true]; optional bool extract = 3 [default = true];
// If this field is "true", the fetcher cache will be used. If not,
// fetching bypasses the cache and downloads directly into the
// sandbox directory, no matter whether a suitable cache file is
// available or not. The former directs the fetcher to download to
// the file cache, then copy from there to the sandbox. Subsequent
// fetch attempts with the same URI will omit downloading and copy
// from the cache as long as the file is resident there. Cache files
// may get evicted at any time, which then leads to renewed
// downloading. See also "docs/fetcher.md" and
// "docs/fetcher-cache-internals.md".
optional bool cache = 4;
} }
// Describes a container. // Describes a container.
@ -293,6 +337,12 @@ message ExecutorInfo {
// usage information into a time series database for monitoring. // usage information into a time series database for monitoring.
optional string source = 10; optional string source = 10;
optional bytes data = 4; optional bytes data = 4;
// Service discovery information for the executor. It is not
// interpreted or acted upon by Mesos. It is up to a service
// discovery system to use this information as needed and to handle
// executors without service discovery information.
optional DiscoveryInfo discovery = 12;
} }
@ -307,6 +357,7 @@ message MasterInfo {
required uint32 port = 3 [default = 5050]; required uint32 port = 3 [default = 5050];
optional string pid = 4; optional string pid = 4;
optional string hostname = 5; optional string hostname = 5;
optional string version = 6;
} }
@ -323,6 +374,8 @@ message SlaveInfo {
repeated Resource resources = 3; repeated Resource resources = 3;
repeated Attribute attributes = 5; repeated Attribute attributes = 5;
optional SlaveID id = 6; optional SlaveID id = 6;
// TODO(joerg84): Remove checkpoint field as with 0.22.0
// slave checkpointing is enabled for all slaves (MESOS-2317).
optional bool checkpoint = 7 [default = false]; optional bool checkpoint = 7 [default = false];
} }
@ -399,15 +452,122 @@ message Resource {
optional Value.Ranges ranges = 4; optional Value.Ranges ranges = 4;
optional Value.Set set = 5; optional Value.Set set = 5;
optional string role = 6 [default = "*"]; optional string role = 6 [default = "*"];
message ReservationInfo {
// Describes a dynamic reservation. A dynamic reservation is
// acquired by an operator via the '/reserve' HTTP endpoint or by
// a framework via the offer cycle by sending back an
// 'Offer::Operation::Reserve' message.
// NOTE: We currently do not allow frameworks with role "*" to
// make dynamic reservations.
// This field indicates the principal of the operator or framework
// that reserved this resource. It is used in conjunction with the
// "unreserve" ACL to determine whether the entity attempting to
// unreserve this resource is permitted to do so.
// NOTE: This field should match the FrameworkInfo.principal of
// the framework that reserved this resource.
required string principal = 1;
}
// If this is set, this resource was dynamically reserved by an
// operator or a framework. Otherwise, this resource is either unreserved
// or statically reserved by an operator via the --resources flag.
optional ReservationInfo reservation = 8;
message DiskInfo {
// Describes a persistent disk volume.
// A persistent disk volume will not be automatically garbage
// collected if the task/executor/slave terminates, but is
// re-offered to the framework(s) belonging to the 'role'.
// A framework can set the ID (if it is not set yet) to express
// the intention to create a new persistent disk volume from a
// regular disk resource. To reuse a previously created volume, a
// framework can launch a task/executor when it receives an offer
// with a persistent volume, i.e., ID is set.
// NOTE: Currently, we do not allow a persistent disk volume
// without a reservation (i.e., 'role' should not be '*').
message Persistence {
// A unique ID for the persistent disk volume.
// NOTE: The ID needs to be unique per role on each slave.
required string id = 1;
}
optional Persistence persistence = 1;
// Describes how this disk resource will be mounted in the
// container. If not set, the disk resource will be used as the
// sandbox. Otherwise, it will be mounted according to the
// 'container_path' inside 'volume'. The 'host_path' inside
// 'volume' is ignored.
// NOTE: If 'volume' is set but 'persistence' is not set, the
// volume will be automatically garbage collected after
// task/executor terminates. Currently, if 'persistence' is set,
// 'volume' must be set.
optional Volume volume = 2;
}
optional DiskInfo disk = 7;
message RevocableInfo {}
// If this is set, the resources are revocable, i.e., any tasks or
// executors launched using these resources could get preempted or
// throttled at any time. This could be used by frameworks to run
// best effort tasks that do not need strict uptime or performance
// guarantees. Note that if this is set, 'disk' or 'reservation'
// cannot be set.
optional RevocableInfo revocable = 9;
}
/**
* When the network bandwidth caps are enabled and the container
* is over its limit, outbound packets may be either delayed or
* dropped completely either because it exceeds the maximum bandwidth
* allocation for a single container (the cap) or because the combined
* network traffic of multiple containers on the host exceeds the
* transmit capacity of the host (the share). We can report the
* following statistics for each of these conditions exported directly
* from the Linux Traffic Control Queueing Discipline.
*
* id : name of the limiter, e.g. 'tx_bw_cap'
* backlog : number of packets currently delayed
* bytes : total bytes seen
* drops : number of packets dropped in total
* overlimits : number of packets which exceeded allocation
* packets : total packets seen
* qlen : number of packets currently queued
* rate_bps : throughput in bytes/sec
* rate_pps : throughput in packets/sec
* requeues : number of times a packet has been delayed due to
* locking or device contention issues
*
* More information on the operation of Linux Traffic Control can be
* found at http://www.lartc.org/lartc.html.
*/
message TrafficControlStatistics {
required string id = 1;
optional uint64 backlog = 2;
optional uint64 bytes = 3;
optional uint64 drops = 4;
optional uint64 overlimits = 5;
optional uint64 packets = 6;
optional uint64 qlen = 7;
optional uint64 ratebps = 8;
optional uint64 ratepps = 9;
optional uint64 requeues = 10;
} }
/* /**
* A snapshot of resource usage statistics. * A snapshot of resource usage statistics.
*/ */
message ResourceStatistics { message ResourceStatistics {
required double timestamp = 1; // Snapshot time, in seconds since the Epoch. required double timestamp = 1; // Snapshot time, in seconds since the Epoch.
optional uint32 processes = 30;
optional uint32 threads = 31;
// CPU Usage Information: // CPU Usage Information:
// Total CPU time spent in user mode, and kernel mode. // Total CPU time spent in user mode, and kernel mode.
optional double cpus_user_time_secs = 2; optional double cpus_user_time_secs = 2;
@ -422,17 +582,55 @@ message ResourceStatistics {
optional double cpus_throttled_time_secs = 9; optional double cpus_throttled_time_secs = 9;
// Memory Usage Information: // Memory Usage Information:
optional uint64 mem_rss_bytes = 5; // Resident Set Size.
// Amount of memory resources allocated. // mem_total_bytes was added in 0.23.0 to represent the total memory
// of a process in RAM (as opposed to in Swap). This was previously
// reported as mem_rss_bytes, which was also changed in 0.23.0 to
// represent only the anonymous memory usage, to keep in sync with
// Linux kernel's (arguably erroneous) use of terminology.
optional uint64 mem_total_bytes = 36;
// Total memory + swap usage. This is set if swap is enabled.
optional uint64 mem_total_memsw_bytes = 37;
// Hard memory limit for a container.
optional uint64 mem_limit_bytes = 6; optional uint64 mem_limit_bytes = 6;
// Broken out memory usage information (files, anonymous, and mmaped files) // Soft memory limit for a container.
optional uint64 mem_soft_limit_bytes = 38;
// Broken out memory usage information: pagecache, rss (anonymous),
// mmaped files and swap.
// TODO(chzhcn) mem_file_bytes and mem_anon_bytes are deprecated in
// 0.23.0 and will be removed in 0.24.0.
optional uint64 mem_file_bytes = 10; optional uint64 mem_file_bytes = 10;
optional uint64 mem_anon_bytes = 11; optional uint64 mem_anon_bytes = 11;
optional uint64 mem_mapped_file_bytes = 12;
// TODO(bmahler): Add disk usage. // mem_cache_bytes is added in 0.23.0 to represent page cache usage.
optional uint64 mem_cache_bytes = 39;
// Since 0.23.0, mem_rss_bytes is changed to represent only
// anonymous memory usage. Note that neither its requiredness, type,
// name nor numeric tag has been changed.
optional uint64 mem_rss_bytes = 5;
optional uint64 mem_mapped_file_bytes = 12;
// This is only set if swap is enabled.
optional uint64 mem_swap_bytes = 40;
// Number of occurrences of different levels of memory pressure
// events reported by memory cgroup. Pressure listening (re)starts
// with these values set to 0 when slave (re)starts. See
// https://www.kernel.org/doc/Documentation/cgroups/memory.txt for
// more details.
optional uint64 mem_low_pressure_counter = 32;
optional uint64 mem_medium_pressure_counter = 33;
optional uint64 mem_critical_pressure_counter = 34;
// Disk Usage Information for executor working directory.
optional uint64 disk_limit_bytes = 26;
optional uint64 disk_used_bytes = 27;
// Perf statistics. // Perf statistics.
optional PerfStatistics perf = 13; optional PerfStatistics perf = 13;
@ -453,34 +651,36 @@ message ResourceStatistics {
optional double net_tcp_rtt_microsecs_p90 = 23; optional double net_tcp_rtt_microsecs_p90 = 23;
optional double net_tcp_rtt_microsecs_p95 = 24; optional double net_tcp_rtt_microsecs_p95 = 24;
optional double net_tcp_rtt_microsecs_p99 = 25; optional double net_tcp_rtt_microsecs_p99 = 25;
optional double net_tcp_active_connections = 28;
optional double net_tcp_time_wait_connections = 29;
// Network traffic flowing into or out of a container can be delayed
// or dropped due to congestion or policy inside and outside the
// container.
repeated TrafficControlStatistics net_traffic_control_statistics = 35;
} }
/** /**
* Describes a snapshot of the resource usage for an executor. * Describes a snapshot of the resource usage for executors.
*
* TODO(bmahler): Note that we want to be sending this information
* to the master, and subsequently to the relevant scheduler. So
* this proto is designed to be easy for the scheduler to use, this
* is why we provide the slave id, executor info / task info.
*/ */
message ResourceUsage { message ResourceUsage {
required SlaveID slave_id = 1; message Executor {
required FrameworkID framework_id = 2; required ExecutorInfo executor_info = 1;
// Resource usage is for an executor. For tasks launched with // This includes resources used by the executor itself
// an explicit executor, the executor id is provided. For tasks // as well as its active tasks.
// launched without an executor, our internal executor will be repeated Resource allocated = 2;
// used. In this case, we provide the task id here instead, in
// order to make this message easier for schedulers to work with.
optional ExecutorID executor_id = 3; // If present, this executor was // Current resource usage. If absent, the containerizer
optional string executor_name = 4; // explicitly specified. // cannot provide resource usage.
optional ResourceStatistics statistics = 3;
}
optional TaskID task_id = 5; // If present, the task did not have an executor. repeated Executor executors = 1;
// If missing, the isolation module cannot provide resource usage. // TODO(jieyu): Include slave's total resources here.
optional ResourceStatistics statistics = 6;
} }
@ -564,6 +764,8 @@ message PerfStatistics {
* to proactively influence the allocator. If 'slave_id' is provided * to proactively influence the allocator. If 'slave_id' is provided
* then this request is assumed to only apply to resources on that * then this request is assumed to only apply to resources on that
* slave. * slave.
*
* TODO(vinod): Remove this once the old driver is removed.
*/ */
message Request { message Request {
optional SlaveID slave_id = 1; optional SlaveID slave_id = 1;
@ -583,6 +785,44 @@ message Offer {
repeated Resource resources = 5; repeated Resource resources = 5;
repeated Attribute attributes = 7; repeated Attribute attributes = 7;
repeated ExecutorID executor_ids = 6; repeated ExecutorID executor_ids = 6;
// Defines an operation that can be performed against offers.
message Operation {
enum Type {
LAUNCH = 1;
RESERVE = 2;
UNRESERVE = 3;
CREATE = 4;
DESTROY = 5;
}
message Launch {
repeated TaskInfo task_infos = 1;
}
message Reserve {
repeated Resource resources = 1;
}
message Unreserve {
repeated Resource resources = 1;
}
message Create {
repeated Resource volumes = 1;
}
message Destroy {
repeated Resource volumes = 1;
}
required Type type = 1;
optional Launch launch = 2;
optional Reserve reserve = 3;
optional Unreserve unreserve = 4;
optional Create create = 5;
optional Destroy destroy = 6;
}
} }
@ -607,6 +847,19 @@ message TaskInfo {
// A health check for the task (currently in *alpha* and initial // A health check for the task (currently in *alpha* and initial
// support will only be for TaskInfo's that have a CommandInfo). // support will only be for TaskInfo's that have a CommandInfo).
optional HealthCheck health_check = 8; optional HealthCheck health_check = 8;
// Labels are free-form key value pairs which are exposed through
// master and slave endpoints. Labels will not be interpreted or
// acted upon by Mesos itself. As opposed to the data field, labels
// will be kept in memory on master and slave processes. Therefore,
// labels should be used to tag tasks with light-weight meta-data.
optional Labels labels = 10;
// Service discovery information for the task. It is not interpreted
// or acted upon by Mesos. It is up to a service discovery system
// to use this information as needed and to handle tasks without
// service discovery information.
optional DiscoveryInfo discovery = 11;
} }
@ -625,8 +878,6 @@ enum TaskState {
TASK_FAILED = 3; // TERMINAL. The task failed to finish successfully. TASK_FAILED = 3; // TERMINAL. The task failed to finish successfully.
TASK_KILLED = 4; // TERMINAL. The task was killed by the executor. TASK_KILLED = 4; // TERMINAL. The task was killed by the executor.
TASK_LOST = 5; // TERMINAL. The task failed but can be rescheduled. TASK_LOST = 5; // TERMINAL. The task failed but can be rescheduled.
// TASK_ERROR is currently unused but will be introduced in 0.22.0.
// TODO(dhamon): Start using TASK_ERROR.
TASK_ERROR = 7; // TERMINAL. The task description contains an error. TASK_ERROR = 7; // TERMINAL. The task description contains an error.
} }
@ -635,16 +886,20 @@ enum TaskState {
* Describes the current status of a task. * Describes the current status of a task.
*/ */
message TaskStatus { message TaskStatus {
/** Describes the source of the task status update. */ // Describes the source of the task status update.
enum Source { enum Source {
SOURCE_MASTER = 0; SOURCE_MASTER = 0;
SOURCE_SLAVE = 1; SOURCE_SLAVE = 1;
SOURCE_EXECUTOR = 2; SOURCE_EXECUTOR = 2;
} }
/** Detailed reason for the task status update. */ // Detailed reason for the task status update.
//
// TODO(bmahler): Differentiate between slave removal reasons
// (e.g. unhealthy vs. unregistered for maintenance).
enum Reason { enum Reason {
REASON_COMMAND_EXECUTOR_FAILED = 0; REASON_COMMAND_EXECUTOR_FAILED = 0;
REASON_EXECUTOR_PREEMPTED = 17;
REASON_EXECUTOR_TERMINATED = 1; REASON_EXECUTOR_TERMINATED = 1;
REASON_EXECUTOR_UNREGISTERED = 2; REASON_EXECUTOR_UNREGISTERED = 2;
REASON_FRAMEWORK_REMOVED = 3; REASON_FRAMEWORK_REMOVED = 3;
@ -654,6 +909,7 @@ message TaskStatus {
REASON_MASTER_DISCONNECTED = 7; REASON_MASTER_DISCONNECTED = 7;
REASON_MEMORY_LIMIT = 8; REASON_MEMORY_LIMIT = 8;
REASON_RECONCILIATION = 9; REASON_RECONCILIATION = 9;
REASON_RESOURCES_UNKNOWN = 18;
REASON_SLAVE_DISCONNECTED = 10; REASON_SLAVE_DISCONNECTED = 10;
REASON_SLAVE_REMOVED = 11; REASON_SLAVE_REMOVED = 11;
REASON_SLAVE_RESTARTED = 12; REASON_SLAVE_RESTARTED = 12;
@ -673,6 +929,17 @@ message TaskStatus {
optional ExecutorID executor_id = 7; // TODO(benh): Use in master/slave. optional ExecutorID executor_id = 7; // TODO(benh): Use in master/slave.
optional double timestamp = 6; optional double timestamp = 6;
// Statuses that are delivered reliably to the scheduler will
// include a 'uuid'. The status is considered delivered once
// it is acknowledged by the scheduler. Schedulers can choose
// to either explicitly acknowledge statuses or let the scheduler
// driver implicitly acknowledge (default).
//
// TODO(bmahler): This is currently overwritten in the scheduler
// driver and executor driver, but executors will need to set this
// to a valid RFC-4122 UUID if using the HTTP API.
optional bytes uuid = 11;
// Describes whether the task has been determined to be healthy // Describes whether the task has been determined to be healthy
// (true) or unhealthy (false) according to the HealthCheck field in // (true) or unhealthy (false) according to the HealthCheck field in
// the command info. // the command info.
@ -928,10 +1195,15 @@ message ContainerInfo {
optional bool privileged = 4 [default = false]; optional bool privileged = 4 [default = false];
// Allowing arbitrary parameters to be passed to docker CLI. // Allowing arbitrary parameters to be passed to docker CLI.
// Note that anything passed to this field is not guranteed // Note that anything passed to this field is not guaranteed
// to be supported moving forward, as we might move away from // to be supported moving forward, as we might move away from
// the docker CLI. // the docker CLI.
repeated Parameter parameters = 5; repeated Parameter parameters = 5;
// With this flag set to true, the docker containerizer will
// pull the docker image from the registry even if the image
// is already downloaded on the slave.
optional bool force_pull_image = 6;
} }
required Type type = 1; required Type type = 1;
@ -940,3 +1212,68 @@ message ContainerInfo {
optional DockerInfo docker = 3; optional DockerInfo docker = 3;
} }
/**
* Collection of labels.
*/
message Labels {
repeated Label labels = 1;
}
/**
* Key, value pair used to store free form user-data.
*/
message Label {
required string key = 1;
optional string value = 2;
}
/**
* Named port used for service discovery.
*/
message Port {
required uint32 number = 1;
optional string name = 2;
optional string protocol = 3;
}
/**
* Collection of ports.
*/
message Ports {
repeated Port ports = 1;
}
/**
* Service discovery information.
* The visibility field restricts discovery within a framework
* (FRAMEWORK), within a Mesos cluster (CLUSTER), or places no
* restrictions (EXTERNAL).
* The environment, location, and version fields provide first class
* support for common attributes used to differentiate between
* similar services. The environment may receive values such as
* PROD/QA/DEV, the location field may receive values like
* EAST-US/WEST-US/EUROPE/AMEA, and the version field may receive
* values like v2.0/v0.9. The exact use of these fields is up to each
* service discovery system.
*/
message DiscoveryInfo {
enum Visibility {
FRAMEWORK = 0;
CLUSTER = 1;
EXTERNAL = 2;
}
required Visibility visibility = 1;
optional string name = 2;
optional string environment = 3;
optional string location = 4;
optional string version = 5;
optional Ports ports = 6;
optional Labels labels = 7;
}

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ package mesosproto
import proto "github.com/gogo/protobuf/proto" import proto "github.com/gogo/protobuf/proto"
import math "math" import math "math"
// discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto/gogo.pb" // discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto"
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal
@ -71,6 +71,12 @@ type Task struct {
// NOTE: Either both the fields must be set or both must be unset. // NOTE: Either both the fields must be set or both must be unset.
StatusUpdateState *TaskState `protobuf:"varint,9,opt,name=status_update_state,enum=mesosproto.TaskState" json:"status_update_state,omitempty"` StatusUpdateState *TaskState `protobuf:"varint,9,opt,name=status_update_state,enum=mesosproto.TaskState" json:"status_update_state,omitempty"`
StatusUpdateUuid []byte `protobuf:"bytes,10,opt,name=status_update_uuid" json:"status_update_uuid,omitempty"` StatusUpdateUuid []byte `protobuf:"bytes,10,opt,name=status_update_uuid" json:"status_update_uuid,omitempty"`
Labels *Labels `protobuf:"bytes,11,opt,name=labels" json:"labels,omitempty"`
// Service discovery information for the task. It is not interpreted
// or acted upon by Mesos. It is up to a service discovery system
// to use this information as needed and to handle tasks without
// service discovery information.
Discovery *DiscoveryInfo `protobuf:"bytes,12,opt,name=discovery" json:"discovery,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
@ -148,33 +154,18 @@ func (m *Task) GetStatusUpdateUuid() []byte {
return nil return nil
} }
// Describes a role, which are used to group frameworks for allocation func (m *Task) GetLabels() *Labels {
// decisions, depending on the allocation policy being used. if m != nil {
// The weight field can be used to indicate forms of priority. return m.Labels
type RoleInfo struct { }
Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` return nil
Weight *float64 `protobuf:"fixed64,2,opt,name=weight,def=1" json:"weight,omitempty"`
XXX_unrecognized []byte `json:"-"`
} }
func (m *RoleInfo) Reset() { *m = RoleInfo{} } func (m *Task) GetDiscovery() *DiscoveryInfo {
func (m *RoleInfo) String() string { return proto.CompactTextString(m) } if m != nil {
func (*RoleInfo) ProtoMessage() {} return m.Discovery
const Default_RoleInfo_Weight float64 = 1
func (m *RoleInfo) GetName() string {
if m != nil && m.Name != nil {
return *m.Name
} }
return "" return nil
}
func (m *RoleInfo) GetWeight() float64 {
if m != nil && m.Weight != nil {
return *m.Weight
}
return Default_RoleInfo_Weight
} }
// TODO(vinod): Create a new UUID message type. // TODO(vinod): Create a new UUID message type.
@ -184,7 +175,10 @@ type StatusUpdate struct {
SlaveId *SlaveID `protobuf:"bytes,3,opt,name=slave_id" json:"slave_id,omitempty"` SlaveId *SlaveID `protobuf:"bytes,3,opt,name=slave_id" json:"slave_id,omitempty"`
Status *TaskStatus `protobuf:"bytes,4,req,name=status" json:"status,omitempty"` Status *TaskStatus `protobuf:"bytes,4,req,name=status" json:"status,omitempty"`
Timestamp *float64 `protobuf:"fixed64,5,req,name=timestamp" json:"timestamp,omitempty"` Timestamp *float64 `protobuf:"fixed64,5,req,name=timestamp" json:"timestamp,omitempty"`
Uuid []byte `protobuf:"bytes,6,req,name=uuid" json:"uuid,omitempty"` // This is being deprecated in favor of TaskStatus.uuid. In 0.23.0,
// we set the TaskStatus 'uuid' in the executor driver for all
// retryable status updates.
Uuid []byte `protobuf:"bytes,6,opt,name=uuid" json:"uuid,omitempty"`
// This corresponds to the latest state of the task according to the // This corresponds to the latest state of the task according to the
// slave. Note that this state might be different than the state in // slave. Note that this state might be different than the state in
// 'status' because status update manager queues updates. In other // 'status' because status update manager queues updates. In other
@ -635,7 +629,8 @@ func (m *ReviveOffersMessage) GetFrameworkId() *FrameworkID {
} }
type RunTaskMessage struct { type RunTaskMessage struct {
FrameworkId *FrameworkID `protobuf:"bytes,1,req,name=framework_id" json:"framework_id,omitempty"` // TODO(karya): Remove framework_id after MESOS-2559 has shipped.
FrameworkId *FrameworkID `protobuf:"bytes,1,opt,name=framework_id" json:"framework_id,omitempty"`
Framework *FrameworkInfo `protobuf:"bytes,2,req,name=framework" json:"framework,omitempty"` Framework *FrameworkInfo `protobuf:"bytes,2,req,name=framework" json:"framework,omitempty"`
Pid *string `protobuf:"bytes,3,req,name=pid" json:"pid,omitempty"` Pid *string `protobuf:"bytes,3,req,name=pid" json:"pid,omitempty"`
Task *TaskInfo `protobuf:"bytes,4,req,name=task" json:"task,omitempty"` Task *TaskInfo `protobuf:"bytes,4,req,name=task" json:"task,omitempty"`
@ -830,6 +825,10 @@ func (m *FrameworkErrorMessage) GetMessage() string {
type RegisterSlaveMessage struct { type RegisterSlaveMessage struct {
Slave *SlaveInfo `protobuf:"bytes,1,req,name=slave" json:"slave,omitempty"` Slave *SlaveInfo `protobuf:"bytes,1,req,name=slave" json:"slave,omitempty"`
// Resources that are checkpointed by the slave (e.g., persistent
// volume or dynamic reservation). Frameworks need to release
// checkpointed resources explicitly.
CheckpointedResources []*Resource `protobuf:"bytes,3,rep,name=checkpointed_resources" json:"checkpointed_resources,omitempty"`
// NOTE: This is a hack for the master to detect the slave's // NOTE: This is a hack for the master to detect the slave's
// version. If unset the slave is < 0.21.0. // version. If unset the slave is < 0.21.0.
// TODO(bmahler): Do proper versioning: MESOS-986. // TODO(bmahler): Do proper versioning: MESOS-986.
@ -848,6 +847,13 @@ func (m *RegisterSlaveMessage) GetSlave() *SlaveInfo {
return nil return nil
} }
func (m *RegisterSlaveMessage) GetCheckpointedResources() []*Resource {
if m != nil {
return m.CheckpointedResources
}
return nil
}
func (m *RegisterSlaveMessage) GetVersion() string { func (m *RegisterSlaveMessage) GetVersion() string {
if m != nil && m.Version != nil { if m != nil && m.Version != nil {
return *m.Version return *m.Version
@ -856,11 +862,11 @@ func (m *RegisterSlaveMessage) GetVersion() string {
} }
type ReregisterSlaveMessage struct { type ReregisterSlaveMessage struct {
// TODO(bmahler): slave_id is deprecated.
// 0.21.0: Now an optional field. Always written, never read.
// 0.22.0: Remove this field.
SlaveId *SlaveID `protobuf:"bytes,1,opt,name=slave_id" json:"slave_id,omitempty"`
Slave *SlaveInfo `protobuf:"bytes,2,req,name=slave" json:"slave,omitempty"` Slave *SlaveInfo `protobuf:"bytes,2,req,name=slave" json:"slave,omitempty"`
// Resources that are checkpointed by the slave (e.g., persistent
// volume or dynamic reservation). Frameworks need to release
// checkpointed resources explicitly.
CheckpointedResources []*Resource `protobuf:"bytes,7,rep,name=checkpointed_resources" json:"checkpointed_resources,omitempty"`
ExecutorInfos []*ExecutorInfo `protobuf:"bytes,4,rep,name=executor_infos" json:"executor_infos,omitempty"` ExecutorInfos []*ExecutorInfo `protobuf:"bytes,4,rep,name=executor_infos" json:"executor_infos,omitempty"`
Tasks []*Task `protobuf:"bytes,3,rep,name=tasks" json:"tasks,omitempty"` Tasks []*Task `protobuf:"bytes,3,rep,name=tasks" json:"tasks,omitempty"`
CompletedFrameworks []*Archive_Framework `protobuf:"bytes,5,rep,name=completed_frameworks" json:"completed_frameworks,omitempty"` CompletedFrameworks []*Archive_Framework `protobuf:"bytes,5,rep,name=completed_frameworks" json:"completed_frameworks,omitempty"`
@ -875,16 +881,16 @@ func (m *ReregisterSlaveMessage) Reset() { *m = ReregisterSlaveMessage{}
func (m *ReregisterSlaveMessage) String() string { return proto.CompactTextString(m) } func (m *ReregisterSlaveMessage) String() string { return proto.CompactTextString(m) }
func (*ReregisterSlaveMessage) ProtoMessage() {} func (*ReregisterSlaveMessage) ProtoMessage() {}
func (m *ReregisterSlaveMessage) GetSlaveId() *SlaveID { func (m *ReregisterSlaveMessage) GetSlave() *SlaveInfo {
if m != nil { if m != nil {
return m.SlaveId return m.Slave
} }
return nil return nil
} }
func (m *ReregisterSlaveMessage) GetSlave() *SlaveInfo { func (m *ReregisterSlaveMessage) GetCheckpointedResources() []*Resource {
if m != nil { if m != nil {
return m.Slave return m.CheckpointedResources
} }
return nil return nil
} }
@ -919,6 +925,7 @@ func (m *ReregisterSlaveMessage) GetVersion() string {
type SlaveRegisteredMessage struct { type SlaveRegisteredMessage struct {
SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"` SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"`
Connection *MasterSlaveConnection `protobuf:"bytes,2,opt,name=connection" json:"connection,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
@ -933,9 +940,17 @@ func (m *SlaveRegisteredMessage) GetSlaveId() *SlaveID {
return nil return nil
} }
func (m *SlaveRegisteredMessage) GetConnection() *MasterSlaveConnection {
if m != nil {
return m.Connection
}
return nil
}
type SlaveReregisteredMessage struct { type SlaveReregisteredMessage struct {
SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"` SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"`
Reconciliations []*ReconcileTasksMessage `protobuf:"bytes,2,rep,name=reconciliations" json:"reconciliations,omitempty"` Reconciliations []*ReconcileTasksMessage `protobuf:"bytes,2,rep,name=reconciliations" json:"reconciliations,omitempty"`
Connection *MasterSlaveConnection `protobuf:"bytes,3,opt,name=connection" json:"connection,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
@ -957,6 +972,13 @@ func (m *SlaveReregisteredMessage) GetReconciliations() []*ReconcileTasksMessage
return nil return nil
} }
func (m *SlaveReregisteredMessage) GetConnection() *MasterSlaveConnection {
if m != nil {
return m.Connection
}
return nil
}
type UnregisterSlaveMessage struct { type UnregisterSlaveMessage struct {
SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"` SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -973,6 +995,25 @@ func (m *UnregisterSlaveMessage) GetSlaveId() *SlaveID {
return nil return nil
} }
type MasterSlaveConnection struct {
// Product of max_slave_ping_timeouts * slave_ping_timeout.
// If no pings are received within the total timeout,
// the master will remove the slave.
TotalPingTimeoutSeconds *float64 `protobuf:"fixed64,1,opt,name=total_ping_timeout_seconds" json:"total_ping_timeout_seconds,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MasterSlaveConnection) Reset() { *m = MasterSlaveConnection{} }
func (m *MasterSlaveConnection) String() string { return proto.CompactTextString(m) }
func (*MasterSlaveConnection) ProtoMessage() {}
func (m *MasterSlaveConnection) GetTotalPingTimeoutSeconds() float64 {
if m != nil && m.TotalPingTimeoutSeconds != nil {
return *m.TotalPingTimeoutSeconds
}
return 0
}
// This message is periodically sent by the master to the slave. // This message is periodically sent by the master to the slave.
// If the slave is connected to the master, "connected" is true. // If the slave is connected to the master, "connected" is true.
type PingSlaveMessage struct { type PingSlaveMessage struct {
@ -1018,9 +1059,13 @@ func (m *ShutdownFrameworkMessage) GetFrameworkId() *FrameworkID {
return nil return nil
} }
// Tells the executor to initiate a shut down by invoking // Tells a slave (and consequently executor) to shutdown an executor.
// Executor::shutdown.
type ShutdownExecutorMessage struct { type ShutdownExecutorMessage struct {
// TODO(vinod): Make these fields required. These are made optional
// for backwards compatibility between 0.23.0 slave and pre 0.23.0
// executor driver.
ExecutorId *ExecutorID `protobuf:"bytes,1,opt,name=executor_id" json:"executor_id,omitempty"`
FrameworkId *FrameworkID `protobuf:"bytes,2,opt,name=framework_id" json:"framework_id,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
@ -1028,6 +1073,20 @@ func (m *ShutdownExecutorMessage) Reset() { *m = ShutdownExecutorMessage
func (m *ShutdownExecutorMessage) String() string { return proto.CompactTextString(m) } func (m *ShutdownExecutorMessage) String() string { return proto.CompactTextString(m) }
func (*ShutdownExecutorMessage) ProtoMessage() {} func (*ShutdownExecutorMessage) ProtoMessage() {}
func (m *ShutdownExecutorMessage) GetExecutorId() *ExecutorID {
if m != nil {
return m.ExecutorId
}
return nil
}
func (m *ShutdownExecutorMessage) GetFrameworkId() *FrameworkID {
if m != nil {
return m.FrameworkId
}
return nil
}
type UpdateFrameworkMessage struct { type UpdateFrameworkMessage struct {
FrameworkId *FrameworkID `protobuf:"bytes,1,req,name=framework_id" json:"framework_id,omitempty"` FrameworkId *FrameworkID `protobuf:"bytes,1,req,name=framework_id" json:"framework_id,omitempty"`
Pid *string `protobuf:"bytes,2,req,name=pid" json:"pid,omitempty"` Pid *string `protobuf:"bytes,2,req,name=pid" json:"pid,omitempty"`
@ -1052,6 +1111,52 @@ func (m *UpdateFrameworkMessage) GetPid() string {
return "" return ""
} }
// This message is sent to the slave whenever there is an update of
// the resources that need to be checkpointed (e.g., persistent volume
// or dynamic reservation).
type CheckpointResourcesMessage struct {
Resources []*Resource `protobuf:"bytes,1,rep,name=resources" json:"resources,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *CheckpointResourcesMessage) Reset() { *m = CheckpointResourcesMessage{} }
func (m *CheckpointResourcesMessage) String() string { return proto.CompactTextString(m) }
func (*CheckpointResourcesMessage) ProtoMessage() {}
func (m *CheckpointResourcesMessage) GetResources() []*Resource {
if m != nil {
return m.Resources
}
return nil
}
// This message is sent by the slave to the master to inform the
// master about the total amount of oversubscribed (allocated and
// allocatable) resources.
type UpdateSlaveMessage struct {
SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"`
OversubscribedResources []*Resource `protobuf:"bytes,2,rep,name=oversubscribed_resources" json:"oversubscribed_resources,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *UpdateSlaveMessage) Reset() { *m = UpdateSlaveMessage{} }
func (m *UpdateSlaveMessage) String() string { return proto.CompactTextString(m) }
func (*UpdateSlaveMessage) ProtoMessage() {}
func (m *UpdateSlaveMessage) GetSlaveId() *SlaveID {
if m != nil {
return m.SlaveId
}
return nil
}
func (m *UpdateSlaveMessage) GetOversubscribedResources() []*Resource {
if m != nil {
return m.OversubscribedResources
}
return nil
}
type RegisterExecutorMessage struct { type RegisterExecutorMessage struct {
FrameworkId *FrameworkID `protobuf:"bytes,1,req,name=framework_id" json:"framework_id,omitempty"` FrameworkId *FrameworkID `protobuf:"bytes,1,req,name=framework_id" json:"framework_id,omitempty"`
ExecutorId *ExecutorID `protobuf:"bytes,2,req,name=executor_id" json:"executor_id,omitempty"` ExecutorId *ExecutorID `protobuf:"bytes,2,req,name=executor_id" json:"executor_id,omitempty"`
@ -1260,110 +1365,6 @@ func (m *ShutdownMessage) GetMessage() string {
return "" return ""
} }
type AuthenticateMessage struct {
Pid *string `protobuf:"bytes,1,req,name=pid" json:"pid,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticateMessage) Reset() { *m = AuthenticateMessage{} }
func (m *AuthenticateMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticateMessage) ProtoMessage() {}
func (m *AuthenticateMessage) GetPid() string {
if m != nil && m.Pid != nil {
return *m.Pid
}
return ""
}
type AuthenticationMechanismsMessage struct {
Mechanisms []string `protobuf:"bytes,1,rep,name=mechanisms" json:"mechanisms,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticationMechanismsMessage) Reset() { *m = AuthenticationMechanismsMessage{} }
func (m *AuthenticationMechanismsMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticationMechanismsMessage) ProtoMessage() {}
func (m *AuthenticationMechanismsMessage) GetMechanisms() []string {
if m != nil {
return m.Mechanisms
}
return nil
}
type AuthenticationStartMessage struct {
Mechanism *string `protobuf:"bytes,1,req,name=mechanism" json:"mechanism,omitempty"`
Data *string `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticationStartMessage) Reset() { *m = AuthenticationStartMessage{} }
func (m *AuthenticationStartMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticationStartMessage) ProtoMessage() {}
func (m *AuthenticationStartMessage) GetMechanism() string {
if m != nil && m.Mechanism != nil {
return *m.Mechanism
}
return ""
}
func (m *AuthenticationStartMessage) GetData() string {
if m != nil && m.Data != nil {
return *m.Data
}
return ""
}
type AuthenticationStepMessage struct {
Data []byte `protobuf:"bytes,1,req,name=data" json:"data,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticationStepMessage) Reset() { *m = AuthenticationStepMessage{} }
func (m *AuthenticationStepMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticationStepMessage) ProtoMessage() {}
func (m *AuthenticationStepMessage) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
type AuthenticationCompletedMessage struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticationCompletedMessage) Reset() { *m = AuthenticationCompletedMessage{} }
func (m *AuthenticationCompletedMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticationCompletedMessage) ProtoMessage() {}
type AuthenticationFailedMessage struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticationFailedMessage) Reset() { *m = AuthenticationFailedMessage{} }
func (m *AuthenticationFailedMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticationFailedMessage) ProtoMessage() {}
type AuthenticationErrorMessage struct {
Error *string `protobuf:"bytes,1,opt,name=error" json:"error,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *AuthenticationErrorMessage) Reset() { *m = AuthenticationErrorMessage{} }
func (m *AuthenticationErrorMessage) String() string { return proto.CompactTextString(m) }
func (*AuthenticationErrorMessage) ProtoMessage() {}
func (m *AuthenticationErrorMessage) GetError() string {
if m != nil && m.Error != nil {
return *m.Error
}
return ""
}
// * // *
// Describes Completed Frameworks, etc. for archival. // Describes Completed Frameworks, etc. for archival.
type Archive struct { type Archive struct {
@ -1465,86 +1466,24 @@ func (m *TaskHealthStatus) GetConsecutiveFailures() int32 {
return 0 return 0
} }
// Collection of Modules. // *
type Modules struct { // Message to signal completion of an event within a module.
Libraries []*Modules_Library `protobuf:"bytes,1,rep,name=libraries" json:"libraries,omitempty"` type HookExecuted struct {
Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *Modules) Reset() { *m = Modules{} } func (m *HookExecuted) Reset() { *m = HookExecuted{} }
func (m *Modules) String() string { return proto.CompactTextString(m) } func (m *HookExecuted) String() string { return proto.CompactTextString(m) }
func (*Modules) ProtoMessage() {} func (*HookExecuted) ProtoMessage() {}
func (m *Modules) GetLibraries() []*Modules_Library { func (m *HookExecuted) GetModule() string {
if m != nil { if m != nil && m.Module != nil {
return m.Libraries return *m.Module
}
return nil
}
type Modules_Library struct {
// If "file" contains a slash ("/"), then it is interpreted as a
// (relative or absolute) pathname. Otherwise a standard library
// search is performed.
File *string `protobuf:"bytes,1,opt,name=file" json:"file,omitempty"`
// We will add the proper prefix ("lib") and suffix (".so" for
// Linux and ".dylib" for OS X) to the "name".
Name *string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
Modules []*Modules_Library_Module `protobuf:"bytes,3,rep,name=modules" json:"modules,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Modules_Library) Reset() { *m = Modules_Library{} }
func (m *Modules_Library) String() string { return proto.CompactTextString(m) }
func (*Modules_Library) ProtoMessage() {}
func (m *Modules_Library) GetFile() string {
if m != nil && m.File != nil {
return *m.File
} }
return "" return ""
} }
func (m *Modules_Library) GetName() string {
if m != nil && m.Name != nil {
return *m.Name
}
return ""
}
func (m *Modules_Library) GetModules() []*Modules_Library_Module {
if m != nil {
return m.Modules
}
return nil
}
type Modules_Library_Module struct {
// Module name.
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// Module-specific parameters.
Parameters []*Parameter `protobuf:"bytes,2,rep,name=parameters" json:"parameters,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Modules_Library_Module) Reset() { *m = Modules_Library_Module{} }
func (m *Modules_Library_Module) String() string { return proto.CompactTextString(m) }
func (*Modules_Library_Module) ProtoMessage() {}
func (m *Modules_Library_Module) GetName() string {
if m != nil && m.Name != nil {
return *m.Name
}
return ""
}
func (m *Modules_Library_Module) GetParameters() []*Parameter {
if m != nil {
return m.Parameters
}
return nil
}
func init() { func init() {
proto.RegisterEnum("mesosproto.StatusUpdateRecord_Type", StatusUpdateRecord_Type_name, StatusUpdateRecord_Type_value) proto.RegisterEnum("mesosproto.StatusUpdateRecord_Type", StatusUpdateRecord_Type_name, StatusUpdateRecord_Type_value)
} }

View File

@ -54,15 +54,14 @@ message Task {
// NOTE: Either both the fields must be set or both must be unset. // NOTE: Either both the fields must be set or both must be unset.
optional TaskState status_update_state = 9; optional TaskState status_update_state = 9;
optional bytes status_update_uuid = 10; optional bytes status_update_uuid = 10;
}
optional Labels labels = 11;
// Describes a role, which are used to group frameworks for allocation // Service discovery information for the task. It is not interpreted
// decisions, depending on the allocation policy being used. // or acted upon by Mesos. It is up to a service discovery system
// The weight field can be used to indicate forms of priority. // to use this information as needed and to handle tasks without
message RoleInfo { // service discovery information.
required string name = 1; optional DiscoveryInfo discovery = 12;
optional double weight = 2 [default = 1];
} }
@ -73,7 +72,11 @@ message StatusUpdate {
optional SlaveID slave_id = 3; optional SlaveID slave_id = 3;
required TaskStatus status = 4; required TaskStatus status = 4;
required double timestamp = 5; required double timestamp = 5;
required bytes uuid = 6;
// This is being deprecated in favor of TaskStatus.uuid. In 0.23.0,
// we set the TaskStatus 'uuid' in the executor driver for all
// retryable status updates.
optional bytes uuid = 6;
// This corresponds to the latest state of the task according to the // This corresponds to the latest state of the task according to the
// slave. Note that this state might be different than the state in // slave. Note that this state might be different than the state in
@ -188,7 +191,8 @@ message ReviveOffersMessage {
message RunTaskMessage { message RunTaskMessage {
required FrameworkID framework_id = 1; // TODO(karya): Remove framework_id after MESOS-2559 has shipped.
optional FrameworkID framework_id = 1 [deprecated = true];
required FrameworkInfo framework = 2; required FrameworkInfo framework = 2;
required string pid = 3; required string pid = 3;
required TaskInfo task = 4; required TaskInfo task = 4;
@ -244,6 +248,11 @@ message FrameworkErrorMessage {
message RegisterSlaveMessage { message RegisterSlaveMessage {
required SlaveInfo slave = 1; required SlaveInfo slave = 1;
// Resources that are checkpointed by the slave (e.g., persistent
// volume or dynamic reservation). Frameworks need to release
// checkpointed resources explicitly.
repeated Resource checkpointed_resources = 3;
// NOTE: This is a hack for the master to detect the slave's // NOTE: This is a hack for the master to detect the slave's
// version. If unset the slave is < 0.21.0. // version. If unset the slave is < 0.21.0.
// TODO(bmahler): Do proper versioning: MESOS-986. // TODO(bmahler): Do proper versioning: MESOS-986.
@ -252,11 +261,13 @@ message RegisterSlaveMessage {
message ReregisterSlaveMessage { message ReregisterSlaveMessage {
// TODO(bmahler): slave_id is deprecated.
// 0.21.0: Now an optional field. Always written, never read.
// 0.22.0: Remove this field.
optional SlaveID slave_id = 1;
required SlaveInfo slave = 2; required SlaveInfo slave = 2;
// Resources that are checkpointed by the slave (e.g., persistent
// volume or dynamic reservation). Frameworks need to release
// checkpointed resources explicitly.
repeated Resource checkpointed_resources = 7;
repeated ExecutorInfo executor_infos = 4; repeated ExecutorInfo executor_infos = 4;
repeated Task tasks = 3; repeated Task tasks = 3;
repeated Archive.Framework completed_frameworks = 5; repeated Archive.Framework completed_frameworks = 5;
@ -270,6 +281,7 @@ message ReregisterSlaveMessage {
message SlaveRegisteredMessage { message SlaveRegisteredMessage {
required SlaveID slave_id = 1; required SlaveID slave_id = 1;
optional MasterSlaveConnection connection = 2;
} }
@ -277,6 +289,7 @@ message SlaveReregisteredMessage {
required SlaveID slave_id = 1; required SlaveID slave_id = 1;
repeated ReconcileTasksMessage reconciliations = 2; repeated ReconcileTasksMessage reconciliations = 2;
optional MasterSlaveConnection connection = 3;
} }
@ -285,6 +298,14 @@ message UnregisterSlaveMessage {
} }
message MasterSlaveConnection {
// Product of max_slave_ping_timeouts * slave_ping_timeout.
// If no pings are received within the total timeout,
// the master will remove the slave.
optional double total_ping_timeout_seconds = 1;
}
// This message is periodically sent by the master to the slave. // This message is periodically sent by the master to the slave.
// If the slave is connected to the master, "connected" is true. // If the slave is connected to the master, "connected" is true.
message PingSlaveMessage { message PingSlaveMessage {
@ -303,9 +324,14 @@ message ShutdownFrameworkMessage {
} }
// Tells the executor to initiate a shut down by invoking // Tells a slave (and consequently executor) to shutdown an executor.
// Executor::shutdown. message ShutdownExecutorMessage {
message ShutdownExecutorMessage {} // TODO(vinod): Make these fields required. These are made optional
// for backwards compatibility between 0.23.0 slave and pre 0.23.0
// executor driver.
optional ExecutorID executor_id = 1;
optional FrameworkID framework_id = 2;
}
message UpdateFrameworkMessage { message UpdateFrameworkMessage {
@ -314,6 +340,23 @@ message UpdateFrameworkMessage {
} }
// This message is sent to the slave whenever there is an update of
// the resources that need to be checkpointed (e.g., persistent volume
// or dynamic reservation).
message CheckpointResourcesMessage {
repeated Resource resources = 1;
}
// This message is sent by the slave to the master to inform the
// master about the total amount of oversubscribed (allocated and
// allocatable) resources.
message UpdateSlaveMessage {
required SlaveID slave_id = 1;
repeated Resource oversubscribed_resources = 2;
}
message RegisterExecutorMessage { message RegisterExecutorMessage {
required FrameworkID framework_id = 1; required FrameworkID framework_id = 1;
required ExecutorID executor_id = 2; required ExecutorID executor_id = 2;
@ -361,38 +404,6 @@ message ShutdownMessage {
} }
message AuthenticateMessage {
required string pid = 1; // PID that needs to be authenticated.
}
message AuthenticationMechanismsMessage {
repeated string mechanisms = 1; // List of available SASL mechanisms.
}
message AuthenticationStartMessage {
required string mechanism = 1;
optional string data = 2;
}
message AuthenticationStepMessage {
required bytes data = 1;
}
message AuthenticationCompletedMessage {}
message AuthenticationFailedMessage {}
message AuthenticationErrorMessage {
optional string error = 1;
}
// TODO(adam-mesos): Move this to an 'archive' package. // TODO(adam-mesos): Move this to an 'archive' package.
/** /**
* Describes Completed Frameworks, etc. for archival. * Describes Completed Frameworks, etc. for archival.
@ -426,28 +437,9 @@ message TaskHealthStatus {
} }
// Collection of Modules. /**
message Modules { * Message to signal completion of an event within a module.
message Library { */
// If "file" contains a slash ("/"), then it is interpreted as a message HookExecuted {
// (relative or absolute) pathname. Otherwise a standard library optional string module = 1;
// search is performed.
optional string file = 1;
// We will add the proper prefix ("lib") and suffix (".so" for
// Linux and ".dylib" for OS X) to the "name".
optional string name = 2;
message Module {
// Module name.
optional string name = 1;
// Module-specific parameters.
repeated Parameter parameters = 2;
}
repeated Module modules = 3;
}
repeated Library libraries = 1;
} }

View File

@ -7,7 +7,7 @@ package mesosproto
import proto "github.com/gogo/protobuf/proto" import proto "github.com/gogo/protobuf/proto"
import math "math" import math "math"
// discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto/gogo.pb" // discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto"
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal
@ -86,6 +86,3 @@ func (m *Registry_Slaves) GetSlaves() []*Registry_Slave {
} }
return nil return nil
} }
func init() {
}

View File

@ -7,7 +7,7 @@ package mesosproto
import proto "github.com/gogo/protobuf/proto" import proto "github.com/gogo/protobuf/proto"
import math "math" import math "math"
// discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto/gogo.pb" // discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto"
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal
@ -18,35 +18,32 @@ var _ = math.Inf
type Event_Type int32 type Event_Type int32
const ( const (
Event_REGISTERED Event_Type = 1 Event_SUBSCRIBED Event_Type = 1
Event_REREGISTERED Event_Type = 2 Event_OFFERS Event_Type = 2
Event_OFFERS Event_Type = 3 Event_RESCIND Event_Type = 3
Event_RESCIND Event_Type = 4 Event_UPDATE Event_Type = 4
Event_UPDATE Event_Type = 5 Event_MESSAGE Event_Type = 5
Event_MESSAGE Event_Type = 6 Event_FAILURE Event_Type = 6
Event_FAILURE Event_Type = 7 Event_ERROR Event_Type = 7
Event_ERROR Event_Type = 8
) )
var Event_Type_name = map[int32]string{ var Event_Type_name = map[int32]string{
1: "REGISTERED", 1: "SUBSCRIBED",
2: "REREGISTERED", 2: "OFFERS",
3: "OFFERS", 3: "RESCIND",
4: "RESCIND", 4: "UPDATE",
5: "UPDATE", 5: "MESSAGE",
6: "MESSAGE", 6: "FAILURE",
7: "FAILURE", 7: "ERROR",
8: "ERROR",
} }
var Event_Type_value = map[string]int32{ var Event_Type_value = map[string]int32{
"REGISTERED": 1, "SUBSCRIBED": 1,
"REREGISTERED": 2, "OFFERS": 2,
"OFFERS": 3, "RESCIND": 3,
"RESCIND": 4, "UPDATE": 4,
"UPDATE": 5, "MESSAGE": 5,
"MESSAGE": 6, "FAILURE": 6,
"FAILURE": 7, "ERROR": 7,
"ERROR": 8,
} }
func (x Event_Type) Enum() *Event_Type { func (x Event_Type) Enum() *Event_Type {
@ -71,44 +68,41 @@ func (x *Event_Type) UnmarshalJSON(data []byte) error {
type Call_Type int32 type Call_Type int32
const ( const (
Call_REGISTER Call_Type = 1 Call_SUBSCRIBE Call_Type = 1
Call_REREGISTER Call_Type = 2 Call_TEARDOWN Call_Type = 2
Call_UNREGISTER Call_Type = 3 Call_ACCEPT Call_Type = 3
Call_REQUEST Call_Type = 4 Call_DECLINE Call_Type = 4
Call_DECLINE Call_Type = 5 Call_REVIVE Call_Type = 5
Call_REVIVE Call_Type = 6 Call_KILL Call_Type = 6
Call_LAUNCH Call_Type = 7 Call_SHUTDOWN Call_Type = 7
Call_KILL Call_Type = 8 Call_ACKNOWLEDGE Call_Type = 8
Call_ACKNOWLEDGE Call_Type = 9 Call_RECONCILE Call_Type = 9
Call_RECONCILE Call_Type = 10 Call_MESSAGE Call_Type = 10
Call_MESSAGE Call_Type = 11
) )
var Call_Type_name = map[int32]string{ var Call_Type_name = map[int32]string{
1: "REGISTER", 1: "SUBSCRIBE",
2: "REREGISTER", 2: "TEARDOWN",
3: "UNREGISTER", 3: "ACCEPT",
4: "REQUEST", 4: "DECLINE",
5: "DECLINE", 5: "REVIVE",
6: "REVIVE", 6: "KILL",
7: "LAUNCH", 7: "SHUTDOWN",
8: "KILL", 8: "ACKNOWLEDGE",
9: "ACKNOWLEDGE", 9: "RECONCILE",
10: "RECONCILE", 10: "MESSAGE",
11: "MESSAGE",
} }
var Call_Type_value = map[string]int32{ var Call_Type_value = map[string]int32{
"REGISTER": 1, "SUBSCRIBE": 1,
"REREGISTER": 2, "TEARDOWN": 2,
"UNREGISTER": 3, "ACCEPT": 3,
"REQUEST": 4, "DECLINE": 4,
"DECLINE": 5, "REVIVE": 5,
"REVIVE": 6, "KILL": 6,
"LAUNCH": 7, "SHUTDOWN": 7,
"KILL": 8, "ACKNOWLEDGE": 8,
"ACKNOWLEDGE": 9, "RECONCILE": 9,
"RECONCILE": 10, "MESSAGE": 10,
"MESSAGE": 11,
} }
func (x Call_Type) Enum() *Call_Type { func (x Call_Type) Enum() *Call_Type {
@ -129,22 +123,22 @@ func (x *Call_Type) UnmarshalJSON(data []byte) error {
} }
// * // *
// Low-level scheduler event API. // Scheduler event API.
// //
// An event is described using the standard protocol buffer "union" // An event is described using the standard protocol buffer "union"
// trick, see https://developers.google.com/protocol-buffers/docs/techniques#union. // trick, see:
// https://developers.google.com/protocol-buffers/docs/techniques#union.
type Event struct { type Event struct {
// Type of the event, indicates which optional field below should be // Type of the event, indicates which optional field below should be
// present if that type has a nested message definition. // present if that type has a nested message definition.
Type *Event_Type `protobuf:"varint,1,req,name=type,enum=mesosproto.Event_Type" json:"type,omitempty"` Type *Event_Type `protobuf:"varint,1,req,name=type,enum=mesosproto.Event_Type" json:"type,omitempty"`
Registered *Event_Registered `protobuf:"bytes,2,opt,name=registered" json:"registered,omitempty"` Subscribed *Event_Subscribed `protobuf:"bytes,2,opt,name=subscribed" json:"subscribed,omitempty"`
Reregistered *Event_Reregistered `protobuf:"bytes,3,opt,name=reregistered" json:"reregistered,omitempty"` Offers *Event_Offers `protobuf:"bytes,3,opt,name=offers" json:"offers,omitempty"`
Offers *Event_Offers `protobuf:"bytes,4,opt,name=offers" json:"offers,omitempty"` Rescind *Event_Rescind `protobuf:"bytes,4,opt,name=rescind" json:"rescind,omitempty"`
Rescind *Event_Rescind `protobuf:"bytes,5,opt,name=rescind" json:"rescind,omitempty"` Update *Event_Update `protobuf:"bytes,5,opt,name=update" json:"update,omitempty"`
Update *Event_Update `protobuf:"bytes,6,opt,name=update" json:"update,omitempty"` Message *Event_Message `protobuf:"bytes,6,opt,name=message" json:"message,omitempty"`
Message *Event_Message `protobuf:"bytes,7,opt,name=message" json:"message,omitempty"` Failure *Event_Failure `protobuf:"bytes,7,opt,name=failure" json:"failure,omitempty"`
Failure *Event_Failure `protobuf:"bytes,8,opt,name=failure" json:"failure,omitempty"` Error *Event_Error `protobuf:"bytes,8,opt,name=error" json:"error,omitempty"`
Error *Event_Error `protobuf:"bytes,9,opt,name=error" json:"error,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
@ -156,19 +150,12 @@ func (m *Event) GetType() Event_Type {
if m != nil && m.Type != nil { if m != nil && m.Type != nil {
return *m.Type return *m.Type
} }
return Event_REGISTERED return Event_SUBSCRIBED
} }
func (m *Event) GetRegistered() *Event_Registered { func (m *Event) GetSubscribed() *Event_Subscribed {
if m != nil { if m != nil {
return m.Registered return m.Subscribed
}
return nil
}
func (m *Event) GetReregistered() *Event_Reregistered {
if m != nil {
return m.Reregistered
} }
return nil return nil
} }
@ -215,54 +202,27 @@ func (m *Event) GetError() *Event_Error {
return nil return nil
} }
type Event_Registered struct { // First event received when the scheduler subscribes.
type Event_Subscribed struct {
FrameworkId *FrameworkID `protobuf:"bytes,1,req,name=framework_id" json:"framework_id,omitempty"` FrameworkId *FrameworkID `protobuf:"bytes,1,req,name=framework_id" json:"framework_id,omitempty"`
MasterInfo *MasterInfo `protobuf:"bytes,2,req,name=master_info" json:"master_info,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *Event_Registered) Reset() { *m = Event_Registered{} } func (m *Event_Subscribed) Reset() { *m = Event_Subscribed{} }
func (m *Event_Registered) String() string { return proto.CompactTextString(m) } func (m *Event_Subscribed) String() string { return proto.CompactTextString(m) }
func (*Event_Registered) ProtoMessage() {} func (*Event_Subscribed) ProtoMessage() {}
func (m *Event_Registered) GetFrameworkId() *FrameworkID { func (m *Event_Subscribed) GetFrameworkId() *FrameworkID {
if m != nil { if m != nil {
return m.FrameworkId return m.FrameworkId
} }
return nil return nil
} }
func (m *Event_Registered) GetMasterInfo() *MasterInfo { // Received whenever there are new resources that are offered to the
if m != nil { // scheduler. Each offer corresponds to a set of resources on a
return m.MasterInfo // slave. Until the scheduler accepts or declines an offer the
} // resources are considered allocated to the scheduler.
return nil
}
type Event_Reregistered struct {
FrameworkId *FrameworkID `protobuf:"bytes,1,req,name=framework_id" json:"framework_id,omitempty"`
MasterInfo *MasterInfo `protobuf:"bytes,2,req,name=master_info" json:"master_info,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Event_Reregistered) Reset() { *m = Event_Reregistered{} }
func (m *Event_Reregistered) String() string { return proto.CompactTextString(m) }
func (*Event_Reregistered) ProtoMessage() {}
func (m *Event_Reregistered) GetFrameworkId() *FrameworkID {
if m != nil {
return m.FrameworkId
}
return nil
}
func (m *Event_Reregistered) GetMasterInfo() *MasterInfo {
if m != nil {
return m.MasterInfo
}
return nil
}
type Event_Offers struct { type Event_Offers struct {
Offers []*Offer `protobuf:"bytes,1,rep,name=offers" json:"offers,omitempty"` Offers []*Offer `protobuf:"bytes,1,rep,name=offers" json:"offers,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -279,6 +239,10 @@ func (m *Event_Offers) GetOffers() []*Offer {
return nil return nil
} }
// Received when a particular offer is no longer valid (e.g., the
// slave corresponding to the offer has been removed) and hence
// needs to be rescinded. Any future calls ('Accept' / 'Decline') made
// by the scheduler regarding this offer will be invalid.
type Event_Rescind struct { type Event_Rescind struct {
OfferId *OfferID `protobuf:"bytes,1,req,name=offer_id" json:"offer_id,omitempty"` OfferId *OfferID `protobuf:"bytes,1,req,name=offer_id" json:"offer_id,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -295,9 +259,17 @@ func (m *Event_Rescind) GetOfferId() *OfferID {
return nil return nil
} }
// Received whenever there is a status update that is generated by
// the executor or slave or master. Status updates should be used by
// executors to reliably communicate the status of the tasks that
// they manage. It is crucial that a terminal update (see TaskState
// in mesos.proto) is sent by the executor as soon as the task
// terminates, in order for Mesos to release the resources allocated
// to the task. It is also the responsibility of the scheduler to
// explicitly acknowledge the receipt of a status update. See
// 'Acknowledge' in the 'Call' section below for the semantics.
type Event_Update struct { type Event_Update struct {
Uuid []byte `protobuf:"bytes,1,req,name=uuid" json:"uuid,omitempty"` Status *TaskStatus `protobuf:"bytes,1,req,name=status" json:"status,omitempty"`
Status *TaskStatus `protobuf:"bytes,2,req,name=status" json:"status,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
@ -305,13 +277,6 @@ func (m *Event_Update) Reset() { *m = Event_Update{} }
func (m *Event_Update) String() string { return proto.CompactTextString(m) } func (m *Event_Update) String() string { return proto.CompactTextString(m) }
func (*Event_Update) ProtoMessage() {} func (*Event_Update) ProtoMessage() {}
func (m *Event_Update) GetUuid() []byte {
if m != nil {
return m.Uuid
}
return nil
}
func (m *Event_Update) GetStatus() *TaskStatus { func (m *Event_Update) GetStatus() *TaskStatus {
if m != nil { if m != nil {
return m.Status return m.Status
@ -319,6 +284,11 @@ func (m *Event_Update) GetStatus() *TaskStatus {
return nil return nil
} }
// Received when a custom message generated by the executor is
// forwarded by the master. Note that this message is not
// interpreted by Mesos and is only forwarded (without reliability
// guarantees) to the scheduler. It is up to the executor to retry
// if the message is dropped for any reason.
type Event_Message struct { type Event_Message struct {
SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"` SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"`
ExecutorId *ExecutorID `protobuf:"bytes,2,req,name=executor_id" json:"executor_id,omitempty"` ExecutorId *ExecutorID `protobuf:"bytes,2,req,name=executor_id" json:"executor_id,omitempty"`
@ -351,6 +321,16 @@ func (m *Event_Message) GetData() []byte {
return nil return nil
} }
// Received when a slave is removed from the cluster (e.g., failed
// health checks) or when an executor is terminated. Note that, this
// event coincides with receipt of terminal UPDATE events for any
// active tasks belonging to the slave or executor and receipt of
// 'Rescind' events for any outstanding offers belonging to the
// slave. Note that there is no guaranteed order between the
// 'Failure', 'Update' and 'Rescind' events when a slave or executor
// is removed.
// TODO(vinod): Consider splitting the lost slave and terminated
// executor into separate events and ensure it's reliably generated.
type Event_Failure struct { type Event_Failure struct {
SlaveId *SlaveID `protobuf:"bytes,1,opt,name=slave_id" json:"slave_id,omitempty"` SlaveId *SlaveID `protobuf:"bytes,1,opt,name=slave_id" json:"slave_id,omitempty"`
// If this was just a failure of an executor on a slave then // If this was just a failure of an executor on a slave then
@ -386,6 +366,13 @@ func (m *Event_Failure) GetStatus() int32 {
return 0 return 0
} }
// Received when an invalid framework (e.g., unauthenticated,
// unauthorized) attempts to subscribe with the master. Error can
// also be received if scheduler sends invalid Calls (e.g., not
// properly initialized).
// TODO(vinod): Remove this once the old scheduler driver is no
// longer supported. With HTTP API all errors will be signaled via
// HTTP response codes.
type Event_Error struct { type Event_Error struct {
Message *string `protobuf:"bytes,1,req,name=message" json:"message,omitempty"` Message *string `protobuf:"bytes,1,req,name=message" json:"message,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -403,25 +390,29 @@ func (m *Event_Error) GetMessage() string {
} }
// * // *
// Low-level scheduler call API. // Scheduler call API.
// //
// Like Event, a Call is described using the standard protocol buffer // Like Event, a Call is described using the standard protocol buffer
// "union" trick (see above). // "union" trick (see above).
type Call struct { type Call struct {
// Identifies who generated this call. Always necessary, but the // Identifies who generated this call. Master assigns a framework id
// only thing that needs to be set for certain calls, e.g., // when a new scheduler subscribes for the first time. Once assigned,
// REGISTER, REREGISTER, and UNREGISTER. // the scheduler must set the 'framework_id' here and within its
FrameworkInfo *FrameworkInfo `protobuf:"bytes,1,req,name=framework_info" json:"framework_info,omitempty"` // FrameworkInfo (in any further 'Subscribe' calls). This allows the
// master to identify a scheduler correctly across disconnections,
// failovers, etc.
FrameworkId *FrameworkID `protobuf:"bytes,1,opt,name=framework_id" json:"framework_id,omitempty"`
// Type of the call, indicates which optional field below should be // Type of the call, indicates which optional field below should be
// present if that type has a nested message definition. // present if that type has a nested message definition.
Type *Call_Type `protobuf:"varint,2,req,name=type,enum=mesosproto.Call_Type" json:"type,omitempty"` Type *Call_Type `protobuf:"varint,2,req,name=type,enum=mesosproto.Call_Type" json:"type,omitempty"`
Request *Call_Request `protobuf:"bytes,3,opt,name=request" json:"request,omitempty"` Subscribe *Call_Subscribe `protobuf:"bytes,3,opt,name=subscribe" json:"subscribe,omitempty"`
Decline *Call_Decline `protobuf:"bytes,4,opt,name=decline" json:"decline,omitempty"` Accept *Call_Accept `protobuf:"bytes,4,opt,name=accept" json:"accept,omitempty"`
Launch *Call_Launch `protobuf:"bytes,5,opt,name=launch" json:"launch,omitempty"` Decline *Call_Decline `protobuf:"bytes,5,opt,name=decline" json:"decline,omitempty"`
Kill *Call_Kill `protobuf:"bytes,6,opt,name=kill" json:"kill,omitempty"` Kill *Call_Kill `protobuf:"bytes,6,opt,name=kill" json:"kill,omitempty"`
Acknowledge *Call_Acknowledge `protobuf:"bytes,7,opt,name=acknowledge" json:"acknowledge,omitempty"` Shutdown *Call_Shutdown `protobuf:"bytes,7,opt,name=shutdown" json:"shutdown,omitempty"`
Reconcile *Call_Reconcile `protobuf:"bytes,8,opt,name=reconcile" json:"reconcile,omitempty"` Acknowledge *Call_Acknowledge `protobuf:"bytes,8,opt,name=acknowledge" json:"acknowledge,omitempty"`
Message *Call_Message `protobuf:"bytes,9,opt,name=message" json:"message,omitempty"` Reconcile *Call_Reconcile `protobuf:"bytes,9,opt,name=reconcile" json:"reconcile,omitempty"`
Message *Call_Message `protobuf:"bytes,10,opt,name=message" json:"message,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
@ -429,9 +420,9 @@ func (m *Call) Reset() { *m = Call{} }
func (m *Call) String() string { return proto.CompactTextString(m) } func (m *Call) String() string { return proto.CompactTextString(m) }
func (*Call) ProtoMessage() {} func (*Call) ProtoMessage() {}
func (m *Call) GetFrameworkInfo() *FrameworkInfo { func (m *Call) GetFrameworkId() *FrameworkID {
if m != nil { if m != nil {
return m.FrameworkInfo return m.FrameworkId
} }
return nil return nil
} }
@ -440,12 +431,19 @@ func (m *Call) GetType() Call_Type {
if m != nil && m.Type != nil { if m != nil && m.Type != nil {
return *m.Type return *m.Type
} }
return Call_REGISTER return Call_SUBSCRIBE
} }
func (m *Call) GetRequest() *Call_Request { func (m *Call) GetSubscribe() *Call_Subscribe {
if m != nil { if m != nil {
return m.Request return m.Subscribe
}
return nil
}
func (m *Call) GetAccept() *Call_Accept {
if m != nil {
return m.Accept
} }
return nil return nil
} }
@ -457,16 +455,16 @@ func (m *Call) GetDecline() *Call_Decline {
return nil return nil
} }
func (m *Call) GetLaunch() *Call_Launch { func (m *Call) GetKill() *Call_Kill {
if m != nil { if m != nil {
return m.Launch return m.Kill
} }
return nil return nil
} }
func (m *Call) GetKill() *Call_Kill { func (m *Call) GetShutdown() *Call_Shutdown {
if m != nil { if m != nil {
return m.Kill return m.Shutdown
} }
return nil return nil
} }
@ -492,22 +490,105 @@ func (m *Call) GetMessage() *Call_Message {
return nil return nil
} }
type Call_Request struct { // Subscribes the scheduler with the master to receive events. A
Requests []*Request `protobuf:"bytes,1,rep,name=requests" json:"requests,omitempty"` // scheduler must send other calls only after it has received the
// SUBCRIBED event.
type Call_Subscribe struct {
// See the comments below on 'framework_id' on the semantics for
// 'framework_info.id'.
FrameworkInfo *FrameworkInfo `protobuf:"bytes,1,req,name=framework_info" json:"framework_info,omitempty"`
// 'force' field is only relevant when 'framework_info.id' is set.
// It tells the master what to do in case an instance of the
// scheduler attempts to subscribe when another instance of it is
// already connected (e.g., split brain due to network partition).
// If 'force' is true, this scheduler instance is allowed and the
// old connected scheduler instance is disconnected. If false,
// this scheduler instance is disallowed subscription in favor of
// the already connected scheduler instance.
//
// It is recommended to set this to true only when a newly elected
// scheduler instance is attempting to subscribe but not when a
// scheduler is retrying subscription (e.g., disconnection or
// master failover; see sched/sched.cpp for an example).
Force *bool `protobuf:"varint,2,opt,name=force" json:"force,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *Call_Request) Reset() { *m = Call_Request{} } func (m *Call_Subscribe) Reset() { *m = Call_Subscribe{} }
func (m *Call_Request) String() string { return proto.CompactTextString(m) } func (m *Call_Subscribe) String() string { return proto.CompactTextString(m) }
func (*Call_Request) ProtoMessage() {} func (*Call_Subscribe) ProtoMessage() {}
func (m *Call_Request) GetRequests() []*Request { func (m *Call_Subscribe) GetFrameworkInfo() *FrameworkInfo {
if m != nil { if m != nil {
return m.Requests return m.FrameworkInfo
} }
return nil return nil
} }
func (m *Call_Subscribe) GetForce() bool {
if m != nil && m.Force != nil {
return *m.Force
}
return false
}
// Accepts an offer, performing the specified operations
// in a sequential manner.
//
// E.g. Launch a task with a newly reserved persistent volume:
//
// Accept {
// offer_ids: [ ... ]
// operations: [
// { type: RESERVE,
// reserve: { resources: [ disk(role):2 ] } }
// { type: CREATE,
// create: { volumes: [ disk(role):1+persistence ] } }
// { type: LAUNCH,
// launch: { task_infos ... disk(role):1;disk(role):1+persistence } }
// ]
// }
//
// Note that any of the offers resources not used in the 'Accept'
// call (e.g., to launch a task) are considered unused and might be
// reoffered to other frameworks. In other words, the same OfferID
// cannot be used in more than one 'Accept' call.
type Call_Accept struct {
OfferIds []*OfferID `protobuf:"bytes,1,rep,name=offer_ids" json:"offer_ids,omitempty"`
Operations []*Offer_Operation `protobuf:"bytes,2,rep,name=operations" json:"operations,omitempty"`
Filters *Filters `protobuf:"bytes,3,opt,name=filters" json:"filters,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Call_Accept) Reset() { *m = Call_Accept{} }
func (m *Call_Accept) String() string { return proto.CompactTextString(m) }
func (*Call_Accept) ProtoMessage() {}
func (m *Call_Accept) GetOfferIds() []*OfferID {
if m != nil {
return m.OfferIds
}
return nil
}
func (m *Call_Accept) GetOperations() []*Offer_Operation {
if m != nil {
return m.Operations
}
return nil
}
func (m *Call_Accept) GetFilters() *Filters {
if m != nil {
return m.Filters
}
return nil
}
// Declines an offer, signaling the master to potentially reoffer
// the resources to a different framework. Note that this is same
// as sending an Accept call with no operations. See comments on
// top of 'Accept' for semantics.
type Call_Decline struct { type Call_Decline struct {
OfferIds []*OfferID `protobuf:"bytes,1,rep,name=offer_ids" json:"offer_ids,omitempty"` OfferIds []*OfferID `protobuf:"bytes,1,rep,name=offer_ids" json:"offer_ids,omitempty"`
Filters *Filters `protobuf:"bytes,2,opt,name=filters" json:"filters,omitempty"` Filters *Filters `protobuf:"bytes,2,opt,name=filters" json:"filters,omitempty"`
@ -532,40 +613,16 @@ func (m *Call_Decline) GetFilters() *Filters {
return nil return nil
} }
type Call_Launch struct { // Kills a specific task. If the scheduler has a custom executor,
TaskInfos []*TaskInfo `protobuf:"bytes,1,rep,name=task_infos" json:"task_infos,omitempty"` // the kill is forwarded to the executor and it is up to the
OfferIds []*OfferID `protobuf:"bytes,2,rep,name=offer_ids" json:"offer_ids,omitempty"` // executor to kill the task and send a TASK_KILLED (or TASK_FAILED)
Filters *Filters `protobuf:"bytes,3,opt,name=filters" json:"filters,omitempty"` // update. Note that Mesos releases the resources for a task once it
XXX_unrecognized []byte `json:"-"` // receives a terminal update (See TaskState in mesos.proto) for it.
} // If the task is unknown to the master, a TASK_LOST update is
// generated.
func (m *Call_Launch) Reset() { *m = Call_Launch{} }
func (m *Call_Launch) String() string { return proto.CompactTextString(m) }
func (*Call_Launch) ProtoMessage() {}
func (m *Call_Launch) GetTaskInfos() []*TaskInfo {
if m != nil {
return m.TaskInfos
}
return nil
}
func (m *Call_Launch) GetOfferIds() []*OfferID {
if m != nil {
return m.OfferIds
}
return nil
}
func (m *Call_Launch) GetFilters() *Filters {
if m != nil {
return m.Filters
}
return nil
}
type Call_Kill struct { type Call_Kill struct {
TaskId *TaskID `protobuf:"bytes,1,req,name=task_id" json:"task_id,omitempty"` TaskId *TaskID `protobuf:"bytes,1,req,name=task_id" json:"task_id,omitempty"`
SlaveId *SlaveID `protobuf:"bytes,2,opt,name=slave_id" json:"slave_id,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
@ -580,6 +637,49 @@ func (m *Call_Kill) GetTaskId() *TaskID {
return nil return nil
} }
func (m *Call_Kill) GetSlaveId() *SlaveID {
if m != nil {
return m.SlaveId
}
return nil
}
// Shuts down a custom executor. When the executor gets a shutdown
// event, it is expected to kill all its tasks (and send TASK_KILLED
// updates) and terminate. If the executor doesnt terminate within
// a certain timeout (configurable via
// '--executor_shutdown_grace_period' slave flag), the slave will
// forcefully destroy the container (executor and its tasks) and
// transition its active tasks to TASK_LOST.
type Call_Shutdown struct {
ExecutorId *ExecutorID `protobuf:"bytes,1,req,name=executor_id" json:"executor_id,omitempty"`
SlaveId *SlaveID `protobuf:"bytes,2,req,name=slave_id" json:"slave_id,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Call_Shutdown) Reset() { *m = Call_Shutdown{} }
func (m *Call_Shutdown) String() string { return proto.CompactTextString(m) }
func (*Call_Shutdown) ProtoMessage() {}
func (m *Call_Shutdown) GetExecutorId() *ExecutorID {
if m != nil {
return m.ExecutorId
}
return nil
}
func (m *Call_Shutdown) GetSlaveId() *SlaveID {
if m != nil {
return m.SlaveId
}
return nil
}
// Acknowledges the receipt of status update. Schedulers are
// responsible for explicitly acknowledging the receipt of status
// updates that have 'Update.status().uuid()' field set. Such status
// updates are retried by the slave until they are acknowledged by
// the scheduler.
type Call_Acknowledge struct { type Call_Acknowledge struct {
SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"` SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"`
TaskId *TaskID `protobuf:"bytes,2,req,name=task_id" json:"task_id,omitempty"` TaskId *TaskID `protobuf:"bytes,2,req,name=task_id" json:"task_id,omitempty"`
@ -612,16 +712,14 @@ func (m *Call_Acknowledge) GetUuid() []byte {
return nil return nil
} }
// Allows the framework to query the status for non-terminal tasks. // Allows the scheduler to query the status for non-terminal tasks.
// This causes the master to send back the latest task status for // This causes the master to send back the latest task status for
// each task in 'statuses', if possible. Tasks that are no longer // each task in 'tasks', if possible. Tasks that are no longer known
// known will result in a TASK_LOST update. If statuses is empty, // will result in a TASK_LOST update. If 'statuses' is empty, then
// then the master will send the latest status for each task // the master will send the latest status for each task currently
// currently known. // known.
// TODO(bmahler): Add a guiding document for reconciliation or
// document reconciliation in-depth here.
type Call_Reconcile struct { type Call_Reconcile struct {
Statuses []*TaskStatus `protobuf:"bytes,1,rep,name=statuses" json:"statuses,omitempty"` Tasks []*Call_Reconcile_Task `protobuf:"bytes,1,rep,name=tasks" json:"tasks,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
@ -629,13 +727,41 @@ func (m *Call_Reconcile) Reset() { *m = Call_Reconcile{} }
func (m *Call_Reconcile) String() string { return proto.CompactTextString(m) } func (m *Call_Reconcile) String() string { return proto.CompactTextString(m) }
func (*Call_Reconcile) ProtoMessage() {} func (*Call_Reconcile) ProtoMessage() {}
func (m *Call_Reconcile) GetStatuses() []*TaskStatus { func (m *Call_Reconcile) GetTasks() []*Call_Reconcile_Task {
if m != nil { if m != nil {
return m.Statuses return m.Tasks
} }
return nil return nil
} }
// TODO(vinod): Support arbitrary queries than just state of tasks.
type Call_Reconcile_Task struct {
TaskId *TaskID `protobuf:"bytes,1,req,name=task_id" json:"task_id,omitempty"`
SlaveId *SlaveID `protobuf:"bytes,2,opt,name=slave_id" json:"slave_id,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Call_Reconcile_Task) Reset() { *m = Call_Reconcile_Task{} }
func (m *Call_Reconcile_Task) String() string { return proto.CompactTextString(m) }
func (*Call_Reconcile_Task) ProtoMessage() {}
func (m *Call_Reconcile_Task) GetTaskId() *TaskID {
if m != nil {
return m.TaskId
}
return nil
}
func (m *Call_Reconcile_Task) GetSlaveId() *SlaveID {
if m != nil {
return m.SlaveId
}
return nil
}
// Sends arbitrary binary data to the executor. Note that Mesos
// neither interprets this data nor makes any guarantees about the
// delivery of this message to the executor.
type Call_Message struct { type Call_Message struct {
SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"` SlaveId *SlaveID `protobuf:"bytes,1,req,name=slave_id" json:"slave_id,omitempty"`
ExecutorId *ExecutorID `protobuf:"bytes,2,req,name=executor_id" json:"executor_id,omitempty"` ExecutorId *ExecutorID `protobuf:"bytes,2,req,name=executor_id" json:"executor_id,omitempty"`

View File

@ -23,54 +23,80 @@ import "github.com/gogo/protobuf/gogoproto/gogo.proto";
/** /**
* Low-level scheduler event API. * Scheduler event API.
* *
* An event is described using the standard protocol buffer "union" * An event is described using the standard protocol buffer "union"
* trick, see https://developers.google.com/protocol-buffers/docs/techniques#union. * trick, see:
* https://developers.google.com/protocol-buffers/docs/techniques#union.
*/ */
message Event { message Event {
// Possible event types, followed by message definitions if // Possible event types, followed by message definitions if
// applicable. // applicable.
enum Type { enum Type {
REGISTERED = 1; SUBSCRIBED = 1; // See 'Subscribed' below.
REREGISTERED = 2; OFFERS = 2; // See 'Offers' below.
OFFERS = 3; RESCIND = 3; // See 'Rescind' below.
RESCIND = 4; UPDATE = 4; // See 'Update' below.
UPDATE = 5; MESSAGE = 5; // See 'Message' below.
MESSAGE = 6; FAILURE = 6; // See 'Failure' below.
FAILURE = 7; ERROR = 7; // See 'Error' below.
ERROR = 8;
} }
message Registered { // First event received when the scheduler subscribes.
message Subscribed {
required FrameworkID framework_id = 1; required FrameworkID framework_id = 1;
required MasterInfo master_info = 2;
}
message Reregistered {
required FrameworkID framework_id = 1;
required MasterInfo master_info = 2;
} }
// Received whenever there are new resources that are offered to the
// scheduler. Each offer corresponds to a set of resources on a
// slave. Until the scheduler accepts or declines an offer the
// resources are considered allocated to the scheduler.
message Offers { message Offers {
repeated Offer offers = 1; repeated Offer offers = 1;
} }
// Received when a particular offer is no longer valid (e.g., the
// slave corresponding to the offer has been removed) and hence
// needs to be rescinded. Any future calls ('Accept' / 'Decline') made
// by the scheduler regarding this offer will be invalid.
message Rescind { message Rescind {
required OfferID offer_id = 1; required OfferID offer_id = 1;
} }
// Received whenever there is a status update that is generated by
// the executor or slave or master. Status updates should be used by
// executors to reliably communicate the status of the tasks that
// they manage. It is crucial that a terminal update (see TaskState
// in mesos.proto) is sent by the executor as soon as the task
// terminates, in order for Mesos to release the resources allocated
// to the task. It is also the responsibility of the scheduler to
// explicitly acknowledge the receipt of a status update. See
// 'Acknowledge' in the 'Call' section below for the semantics.
message Update { message Update {
required bytes uuid = 1; // TODO(benh): Replace with UpdateID. required TaskStatus status = 1;
required TaskStatus status = 2;
} }
// Received when a custom message generated by the executor is
// forwarded by the master. Note that this message is not
// interpreted by Mesos and is only forwarded (without reliability
// guarantees) to the scheduler. It is up to the executor to retry
// if the message is dropped for any reason.
message Message { message Message {
required SlaveID slave_id = 1; required SlaveID slave_id = 1;
required ExecutorID executor_id = 2; required ExecutorID executor_id = 2;
required bytes data = 3; required bytes data = 3;
} }
// Received when a slave is removed from the cluster (e.g., failed
// health checks) or when an executor is terminated. Note that, this
// event coincides with receipt of terminal UPDATE events for any
// active tasks belonging to the slave or executor and receipt of
// 'Rescind' events for any outstanding offers belonging to the
// slave. Note that there is no guaranteed order between the
// 'Failure', 'Update' and 'Rescind' events when a slave or executor
// is removed.
// TODO(vinod): Consider splitting the lost slave and terminated
// executor into separate events and ensure it's reliably generated.
message Failure { message Failure {
optional SlaveID slave_id = 1; optional SlaveID slave_id = 1;
@ -81,29 +107,33 @@ message Event {
optional int32 status = 3; optional int32 status = 3;
} }
// Received when an invalid framework (e.g., unauthenticated,
// unauthorized) attempts to subscribe with the master. Error can
// also be received if scheduler sends invalid Calls (e.g., not
// properly initialized).
// TODO(vinod): Remove this once the old scheduler driver is no
// longer supported. With HTTP API all errors will be signaled via
// HTTP response codes.
message Error { message Error {
required string message = 1; required string message = 1;
} }
// TODO(benh): Add a 'from' or 'sender'.
// Type of the event, indicates which optional field below should be // Type of the event, indicates which optional field below should be
// present if that type has a nested message definition. // present if that type has a nested message definition.
required Type type = 1; required Type type = 1;
optional Registered registered = 2; optional Subscribed subscribed = 2;
optional Reregistered reregistered = 3; optional Offers offers = 3;
optional Offers offers = 4; optional Rescind rescind = 4;
optional Rescind rescind = 5; optional Update update = 5;
optional Update update = 6; optional Message message = 6;
optional Message message = 7; optional Failure failure = 7;
optional Failure failure = 8; optional Error error = 8;
optional Error error = 9;
} }
/** /**
* Low-level scheduler call API. * Scheduler call API.
* *
* Like Event, a Call is described using the standard protocol buffer * Like Event, a Call is described using the standard protocol buffer
* "union" trick (see above). * "union" trick (see above).
@ -112,20 +142,19 @@ message Call {
// Possible call types, followed by message definitions if // Possible call types, followed by message definitions if
// applicable. // applicable.
enum Type { enum Type {
REGISTER = 1; SUBSCRIBE = 1; // See 'Subscribe' below.
REREGISTER = 2; TEARDOWN = 2; // Shuts down all tasks/executors and removes framework.
UNREGISTER = 3; ACCEPT = 3; // See 'Accept' below.
REQUEST = 4; DECLINE = 4; // See 'Decline' below.
DECLINE = 5; REVIVE = 5; // Removes any previous filters set via ACCEPT or DECLINE.
REVIVE = 6; KILL = 6; // See 'Kill' below.
LAUNCH = 7; SHUTDOWN = 7; // See 'Shutdown' below.
KILL = 8; ACKNOWLEDGE = 8; // See 'Acknowledge' below.
ACKNOWLEDGE = 9; RECONCILE = 9; // See 'Reconcile' below.
RECONCILE = 10; MESSAGE = 10; // See 'Message' below.
MESSAGE = 11;
// TODO(benh): Consider adding an 'ACTIVATE' and 'DEACTIVATE' for // TODO(benh): Consider adding an 'ACTIVATE' and 'DEACTIVATE' for
// already registered frameworks as a way of stopping offers from // already subscribed frameworks as a way of stopping offers from
// being generated and other events from being sent by the master. // being generated and other events from being sent by the master.
// Note that this functionality existed originally to support // Note that this functionality existed originally to support
// SchedulerDriver::abort which was only necessary to handle // SchedulerDriver::abort which was only necessary to handle
@ -133,63 +162,144 @@ message Call {
// something that is not an issue with the Event/Call API. // something that is not an issue with the Event/Call API.
} }
message Request { // Subscribes the scheduler with the master to receive events. A
repeated mesosproto.Request requests = 1; // scheduler must send other calls only after it has received the
// SUBCRIBED event.
message Subscribe {
// See the comments below on 'framework_id' on the semantics for
// 'framework_info.id'.
required FrameworkInfo framework_info = 1;
// 'force' field is only relevant when 'framework_info.id' is set.
// It tells the master what to do in case an instance of the
// scheduler attempts to subscribe when another instance of it is
// already connected (e.g., split brain due to network partition).
// If 'force' is true, this scheduler instance is allowed and the
// old connected scheduler instance is disconnected. If false,
// this scheduler instance is disallowed subscription in favor of
// the already connected scheduler instance.
//
// It is recommended to set this to true only when a newly elected
// scheduler instance is attempting to subscribe but not when a
// scheduler is retrying subscription (e.g., disconnection or
// master failover; see sched/sched.cpp for an example).
optional bool force = 2;
} }
// Accepts an offer, performing the specified operations
// in a sequential manner.
//
// E.g. Launch a task with a newly reserved persistent volume:
//
// Accept {
// offer_ids: [ ... ]
// operations: [
// { type: RESERVE,
// reserve: { resources: [ disk(role):2 ] } }
// { type: CREATE,
// create: { volumes: [ disk(role):1+persistence ] } }
// { type: LAUNCH,
// launch: { task_infos ... disk(role):1;disk(role):1+persistence } }
// ]
// }
//
// Note that any of the offers resources not used in the 'Accept'
// call (e.g., to launch a task) are considered unused and might be
// reoffered to other frameworks. In other words, the same OfferID
// cannot be used in more than one 'Accept' call.
message Accept {
repeated OfferID offer_ids = 1;
repeated Offer.Operation operations = 2;
optional Filters filters = 3;
}
// Declines an offer, signaling the master to potentially reoffer
// the resources to a different framework. Note that this is same
// as sending an Accept call with no operations. See comments on
// top of 'Accept' for semantics.
message Decline { message Decline {
repeated OfferID offer_ids = 1; repeated OfferID offer_ids = 1;
optional Filters filters = 2; optional Filters filters = 2;
} }
message Launch { // Kills a specific task. If the scheduler has a custom executor,
repeated TaskInfo task_infos = 1; // the kill is forwarded to the executor and it is up to the
repeated OfferID offer_ids = 2; // executor to kill the task and send a TASK_KILLED (or TASK_FAILED)
optional Filters filters = 3; // update. Note that Mesos releases the resources for a task once it
} // receives a terminal update (See TaskState in mesos.proto) for it.
// If the task is unknown to the master, a TASK_LOST update is
// generated.
message Kill { message Kill {
required TaskID task_id = 1; required TaskID task_id = 1;
optional SlaveID slave_id = 2;
} }
// Shuts down a custom executor. When the executor gets a shutdown
// event, it is expected to kill all its tasks (and send TASK_KILLED
// updates) and terminate. If the executor doesnt terminate within
// a certain timeout (configurable via
// '--executor_shutdown_grace_period' slave flag), the slave will
// forcefully destroy the container (executor and its tasks) and
// transition its active tasks to TASK_LOST.
message Shutdown {
required ExecutorID executor_id = 1;
required SlaveID slave_id = 2;
}
// Acknowledges the receipt of status update. Schedulers are
// responsible for explicitly acknowledging the receipt of status
// updates that have 'Update.status().uuid()' field set. Such status
// updates are retried by the slave until they are acknowledged by
// the scheduler.
message Acknowledge { message Acknowledge {
required SlaveID slave_id = 1; required SlaveID slave_id = 1;
required TaskID task_id = 2; required TaskID task_id = 2;
required bytes uuid = 3; required bytes uuid = 3;
} }
// Allows the framework to query the status for non-terminal tasks. // Allows the scheduler to query the status for non-terminal tasks.
// This causes the master to send back the latest task status for // This causes the master to send back the latest task status for
// each task in 'statuses', if possible. Tasks that are no longer // each task in 'tasks', if possible. Tasks that are no longer known
// known will result in a TASK_LOST update. If statuses is empty, // will result in a TASK_LOST update. If 'statuses' is empty, then
// then the master will send the latest status for each task // the master will send the latest status for each task currently
// currently known. // known.
// TODO(bmahler): Add a guiding document for reconciliation or
// document reconciliation in-depth here.
message Reconcile { message Reconcile {
repeated TaskStatus statuses = 1; // Should be non-terminal only. // TODO(vinod): Support arbitrary queries than just state of tasks.
message Task {
required TaskID task_id = 1;
optional SlaveID slave_id = 2;
} }
repeated Task tasks = 1;
}
// Sends arbitrary binary data to the executor. Note that Mesos
// neither interprets this data nor makes any guarantees about the
// delivery of this message to the executor.
message Message { message Message {
required SlaveID slave_id = 1; required SlaveID slave_id = 1;
required ExecutorID executor_id = 2; required ExecutorID executor_id = 2;
required bytes data = 3; required bytes data = 3;
} }
// Identifies who generated this call. Always necessary, but the // Identifies who generated this call. Master assigns a framework id
// only thing that needs to be set for certain calls, e.g., // when a new scheduler subscribes for the first time. Once assigned,
// REGISTER, REREGISTER, and UNREGISTER. // the scheduler must set the 'framework_id' here and within its
required FrameworkInfo framework_info = 1; // FrameworkInfo (in any further 'Subscribe' calls). This allows the
// master to identify a scheduler correctly across disconnections,
// failovers, etc.
optional FrameworkID framework_id = 1;
// Type of the call, indicates which optional field below should be // Type of the call, indicates which optional field below should be
// present if that type has a nested message definition. // present if that type has a nested message definition.
required Type type = 2; required Type type = 2;
optional Request request = 3; optional Subscribe subscribe = 3;
optional Decline decline = 4; optional Accept accept = 4;
optional Launch launch = 5; optional Decline decline = 5;
optional Kill kill = 6; optional Kill kill = 6;
optional Acknowledge acknowledge = 7; optional Shutdown shutdown = 7;
optional Reconcile reconcile = 8; optional Acknowledge acknowledge = 8;
optional Message message = 9; optional Reconcile reconcile = 9;
optional Message message = 10;
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -274,7 +274,7 @@ func TestMutatedHostUPid(t *testing.T) {
serverAddr := serverHost + ":" + strconv.Itoa(serverPort) serverAddr := serverHost + ":" + strconv.Itoa(serverPort)
// override the upid.Host with this listener IP // override the upid.Host with this listener IP
addr := net.ParseIP("127.0.0.2") addr := net.ParseIP("0.0.0.0")
// setup receiver (server) process // setup receiver (server) process
uPid, err := upid.Parse(fmt.Sprintf("%s@%s", serverId, serverAddr)) uPid, err := upid.Parse(fmt.Sprintf("%s@%s", serverId, serverAddr))
@ -308,7 +308,7 @@ func TestEmptyHostPortUPid(t *testing.T) {
uPid.Port = "" uPid.Port = ""
// override the upid.Host with this listener IP // override the upid.Host with this listener IP
addr := net.ParseIP("127.0.0.2") addr := net.ParseIP("0.0.0.0")
receiver := NewHTTPTransporter(uPid, addr) receiver := NewHTTPTransporter(uPid, addr)
@ -316,7 +316,7 @@ func TestEmptyHostPortUPid(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
// This should be the host that overrides as uPid.Host is empty // This should be the host that overrides as uPid.Host is empty
if receiver.upid.Host != "127.0.0.2" { if receiver.upid.Host != "0.0.0.0" {
t.Fatalf("reciever.upid.Host was expected to return %s, got %s\n", serverHost, receiver.upid.Host) t.Fatalf("reciever.upid.Host was expected to return %s, got %s\n", serverHost, receiver.upid.Host)
} }

View File

@ -78,7 +78,7 @@ type MesosMessenger struct {
// ForHostname creates a new default messenger (HTTP), using UPIDBindingAddress to // ForHostname creates a new default messenger (HTTP), using UPIDBindingAddress to
// determine the binding-address used for both the UPID.Host and Transport binding address. // determine the binding-address used for both the UPID.Host and Transport binding address.
func ForHostname(proc *process.Process, hostname string, bindingAddress net.IP, port uint16) (Messenger, error) { func ForHostname(proc *process.Process, hostname string, bindingAddress net.IP, port uint16, publishedAddress net.IP) (Messenger, error) {
upid := &upid.UPID{ upid := &upid.UPID{
ID: proc.Label(), ID: proc.Label(),
Port: strconv.Itoa(int(port)), Port: strconv.Itoa(int(port)),
@ -87,7 +87,21 @@ func ForHostname(proc *process.Process, hostname string, bindingAddress net.IP,
if err != nil { if err != nil {
return nil, err return nil, err
} }
var publishedHost string
if publishedAddress != nil {
publishedHost, err = UPIDBindingAddress(hostname, publishedAddress)
if err != nil {
return nil, err
}
}
if publishedHost != "" {
upid.Host = publishedHost
} else {
upid.Host = host upid.Host = host
}
return NewHttpWithBindingAddress(upid, bindingAddress), nil return NewHttpWithBindingAddress(upid, bindingAddress), nil
} }

View File

@ -28,7 +28,6 @@ import (
"sync" "sync"
"time" "time"
"code.google.com/p/go-uuid/uuid"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
log "github.com/golang/glog" log "github.com/golang/glog"
"github.com/mesos/mesos-go/auth" "github.com/mesos/mesos-go/auth"
@ -38,6 +37,7 @@ import (
"github.com/mesos/mesos-go/mesosutil/process" "github.com/mesos/mesos-go/mesosutil/process"
"github.com/mesos/mesos-go/messenger" "github.com/mesos/mesos-go/messenger"
"github.com/mesos/mesos-go/upid" "github.com/mesos/mesos-go/upid"
"github.com/pborman/uuid"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
@ -86,6 +86,7 @@ type DriverConfig struct {
HostnameOverride string // optional HostnameOverride string // optional
BindingAddress net.IP // optional BindingAddress net.IP // optional
BindingPort uint16 // optional BindingPort uint16 // optional
PublishedAddress net.IP // optional
NewMessenger func() (messenger.Messenger, error) // optional NewMessenger func() (messenger.Messenger, error) // optional
} }
@ -196,7 +197,7 @@ func NewMesosSchedulerDriver(config DriverConfig) (initializedDriver *MesosSched
if newMessenger == nil { if newMessenger == nil {
newMessenger = func() (messenger.Messenger, error) { newMessenger = func() (messenger.Messenger, error) {
process := process.New("scheduler") process := process.New("scheduler")
return messenger.ForHostname(process, hostname, config.BindingAddress, config.BindingPort) return messenger.ForHostname(process, hostname, config.BindingAddress, config.BindingPort, config.PublishedAddress)
} }
} }

View File

@ -0,0 +1 @@
Paul Borman <borman@google.com>

View File

@ -157,7 +157,7 @@ func newTask(c *Cluster, config *cluster.ContainerConfig, name string) (*task, e
task.Name = &name task.Name = &name
task.TaskId = &mesosproto.TaskID{Value: &id} task.TaskId = &mesosproto.TaskID{Value: &id}
task.Labels = &mesosproto.Labels{Labels: []*mesosproto.Label{{Key: proto.String("SWARM_CONTAINER_NAME"), Value: &name}}}
return task, nil return task, nil
} }