updated mesos-go, stretchr/testify, and dependent godeps

pull/6/head
James DeFelice 2016-01-27 17:08:21 +00:00
parent 06996d5af9
commit 54439b5edc
42 changed files with 10968 additions and 16654 deletions

48
Godeps/Godeps.json generated
View File

@ -716,43 +716,43 @@
},
{
"ImportPath": "github.com/mesos/mesos-go/auth",
"Comment": "v0.0.2-5-ged907b1",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11"
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
},
{
"ImportPath": "github.com/mesos/mesos-go/detector",
"Comment": "v0.0.2-5-ged907b1",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11"
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
},
{
"ImportPath": "github.com/mesos/mesos-go/executor",
"Comment": "v0.0.2-5-ged907b1",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11"
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
},
{
"ImportPath": "github.com/mesos/mesos-go/mesosproto",
"Comment": "v0.0.2-5-ged907b1",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11"
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
},
{
"ImportPath": "github.com/mesos/mesos-go/mesosutil",
"Comment": "v0.0.2-5-ged907b1",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11"
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
},
{
"ImportPath": "github.com/mesos/mesos-go/messenger",
"Comment": "v0.0.2-5-ged907b1",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11"
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
},
{
"ImportPath": "github.com/mesos/mesos-go/scheduler",
"Comment": "v0.0.2-5-ged907b1",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11"
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
},
{
"ImportPath": "github.com/mesos/mesos-go/upid",
"Comment": "v0.0.2-5-ged907b1",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11"
"Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
},
{
"ImportPath": "github.com/miekg/dns",
@ -790,6 +790,10 @@
"ImportPath": "github.com/pborman/uuid",
"Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4"
},
{
"ImportPath": "github.com/pmezard/go-difflib/difflib",
"Rev": "d8ed2627bdf02c080bf22230dbb337003b7aba2d"
},
{
"ImportPath": "github.com/prometheus/client_golang/prometheus",
"Comment": "0.7.0-39-g3b78d7a",
@ -861,18 +865,18 @@
},
{
"ImportPath": "github.com/stretchr/testify/assert",
"Comment": "v1.0-17-g089c718",
"Rev": "089c7181b8c728499929ff09b62d3fdd8df8adff"
"Comment": "v1.0-88-ge3a8ff8",
"Rev": "e3a8ff8ce36581f87a15341206f205b1da467059"
},
{
"ImportPath": "github.com/stretchr/testify/mock",
"Comment": "v1.0-17-g089c718",
"Rev": "089c7181b8c728499929ff09b62d3fdd8df8adff"
"Comment": "v1.0-88-ge3a8ff8",
"Rev": "e3a8ff8ce36581f87a15341206f205b1da467059"
},
{
"ImportPath": "github.com/stretchr/testify/require",
"Comment": "v1.0-17-g089c718",
"Rev": "089c7181b8c728499929ff09b62d3fdd8df8adff"
"Comment": "v1.0-88-ge3a8ff8",
"Rev": "e3a8ff8ce36581f87a15341206f205b1da467059"
},
{
"ImportPath": "github.com/syndtr/gocapability/capability",

3
Godeps/LICENSES.md generated
View File

@ -67,6 +67,7 @@ github.com/onsi/ginkgo | spdxMIT
github.com/onsi/gomega | spdxMIT
github.com/opencontainers/runc | Apache-2
github.com/pborman/uuid | spdxBSD3
github.com/pmezard/go-difflib | BSD3
github.com/prometheus/client_golang | Apache-2
github.com/prometheus/client_model | Apache-2
github.com/prometheus/common/expfmt | Apache-2
@ -84,7 +85,7 @@ github.com/spf13/cobra | Apache-2
github.com/spf13/pflag | spdxBSD3
github.com/ssoroka/ttime | UNKNOWN
github.com/stretchr/objx | MIT?
github.com/stretchr/testify | MIT?
github.com/stretchr/testify | spdxMIT
github.com/syndtr/gocapability | spdxBSD2
github.com/ugorji/go | MITname
github.com/vishvananda/netlink | Apache-2

View File

@ -107,11 +107,11 @@ func (s *Standalone) Cancel() {
s.cancelOnce.Do(func() { close(s.done) })
}
// poll for changes to master leadership via current leader's /state.json endpoint.
// poll for changes to master leadership via current leader's /state endpoint.
// we poll the `initial` leader, aborting if none was specified.
//
// TODO(jdef) follow the leader: change who we poll based on the prior leader
// TODO(jdef) somehow determine all masters in cluster from the state.json?
// TODO(jdef) somehow determine all masters in cluster from the /state?
//
func (s *Standalone) _poller(pf fetcherFunc) {
defer func() {
@ -193,7 +193,7 @@ func (s *Standalone) _poller(pf fetcherFunc) {
// assumes that address is in host:port format
func (s *Standalone) _fetchPid(ctx context.Context, address string) (*upid.UPID, error) {
//TODO(jdef) need SSL support
uri := fmt.Sprintf("http://%s/state.json", address)
uri := fmt.Sprintf("http://%s/state", address)
req, err := http.NewRequest("GET", uri, nil)
if err != nil {
return nil, err

View File

@ -1,2 +1,9 @@
all: *.proto
protoc --proto_path=${GOPATH}/src:${GOPATH}/src/github.com/gogo/protobuf/protobuf:. --gogo_out=. *.proto
PROTO_PATH := ${GOPATH}/src:.
PROTO_PATH := ${PROTO_PATH}:${GOPATH}/src/github.com/gogo/protobuf/protobuf
PROTO_PATH := ${PROTO_PATH}:${GOPATH}/src/github.com/gogo/protobuf/gogoproto
.PHONY: all
all:
protoc --proto_path=${PROTO_PATH} --gogo_out=. *.proto
protoc --proto_path=${PROTO_PATH} --gogo_out=. ./scheduler/*.proto

View File

@ -7,15 +7,9 @@ Package mesosproto is a generated protocol buffer package.
It is generated from these files:
authentication.proto
authorizer.proto
containerizer.proto
internal.proto
log.proto
mesos.proto
messages.proto
registry.proto
scheduler.proto
state.proto
It has these top-level messages:
AuthenticateMessage
@ -25,37 +19,22 @@ It has these top-level messages:
AuthenticationCompletedMessage
AuthenticationFailedMessage
AuthenticationErrorMessage
ACL
ACLs
Launch
Update
Wait
Destroy
Usage
Termination
Containers
InternalMasterChangeDetected
InternalTryAuthentication
InternalAuthenticationResult
Promise
Action
Metadata
Record
PromiseRequest
PromiseResponse
WriteRequest
WriteResponse
LearnedMessage
RecoverRequest
RecoverResponse
FrameworkID
OfferID
SlaveID
TaskID
ExecutorID
ContainerID
TimeInfo
DurationInfo
Address
URL
Unavailability
MachineID
MachineInfo
FrameworkInfo
HealthCheck
CommandInfo
@ -71,6 +50,7 @@ It has these top-level messages:
PerfStatistics
Request
Offer
InverseOffer
TaskInfo
TaskStatus
Filters
@ -83,7 +63,9 @@ It has these top-level messages:
RateLimits
Image
Volume
NetworkInfo
ContainerInfo
ContainerStatus
Labels
Label
Port
@ -138,11 +120,6 @@ It has these top-level messages:
Archive
TaskHealthStatus
HookExecuted
Registry
Event
Call
Entry
Operation
*/
package mesosproto
@ -1394,8 +1371,12 @@ func (m *AuthenticateMessage) Unmarshal(data []byte) error {
l := len(data)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthentication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
@ -1408,6 +1389,12 @@ func (m *AuthenticateMessage) Unmarshal(data []byte) error {
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthenticateMessage: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthenticateMessage: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
@ -1415,6 +1402,9 @@ func (m *AuthenticateMessage) Unmarshal(data []byte) error {
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthentication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
@ -1438,15 +1428,7 @@ func (m *AuthenticateMessage) Unmarshal(data []byte) error {
iNdEx = postIndex
hasFields[0] |= uint64(0x00000001)
default:
var sizeOfWire int
for {
sizeOfWire++
wire >>= 7
if wire == 0 {
break
}
}
iNdEx -= sizeOfWire
iNdEx = preIndex
skippy, err := skipAuthentication(data[iNdEx:])
if err != nil {
return err
@ -1465,14 +1447,21 @@ func (m *AuthenticateMessage) Unmarshal(data []byte) error {
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("pid")
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthenticationMechanismsMessage) Unmarshal(data []byte) error {
l := len(data)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthentication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
@ -1485,6 +1474,12 @@ func (m *AuthenticationMechanismsMessage) Unmarshal(data []byte) error {
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthenticationMechanismsMessage: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthenticationMechanismsMessage: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
@ -1492,6 +1487,9 @@ func (m *AuthenticationMechanismsMessage) Unmarshal(data []byte) error {
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthentication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
@ -1513,15 +1511,7 @@ func (m *AuthenticationMechanismsMessage) Unmarshal(data []byte) error {
m.Mechanisms = append(m.Mechanisms, string(data[iNdEx:postIndex]))
iNdEx = postIndex
default:
var sizeOfWire int
for {
sizeOfWire++
wire >>= 7
if wire == 0 {
break
}
}
iNdEx -= sizeOfWire
iNdEx = preIndex
skippy, err := skipAuthentication(data[iNdEx:])
if err != nil {
return err
@ -1537,6 +1527,9 @@ func (m *AuthenticationMechanismsMessage) Unmarshal(data []byte) error {
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthenticationStartMessage) Unmarshal(data []byte) error {
@ -1544,8 +1537,12 @@ func (m *AuthenticationStartMessage) Unmarshal(data []byte) error {
l := len(data)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthentication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
@ -1558,6 +1555,12 @@ func (m *AuthenticationStartMessage) Unmarshal(data []byte) error {
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthenticationStartMessage: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthenticationStartMessage: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
@ -1565,6 +1568,9 @@ func (m *AuthenticationStartMessage) Unmarshal(data []byte) error {
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthentication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
@ -1593,6 +1599,9 @@ func (m *AuthenticationStartMessage) Unmarshal(data []byte) error {
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthentication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
@ -1613,15 +1622,7 @@ func (m *AuthenticationStartMessage) Unmarshal(data []byte) error {
m.Data = append([]byte{}, data[iNdEx:postIndex]...)
iNdEx = postIndex
default:
var sizeOfWire int
for {
sizeOfWire++
wire >>= 7
if wire == 0 {
break
}
}
iNdEx -= sizeOfWire
iNdEx = preIndex
skippy, err := skipAuthentication(data[iNdEx:])
if err != nil {
return err
@ -1640,6 +1641,9 @@ func (m *AuthenticationStartMessage) Unmarshal(data []byte) error {
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("mechanism")
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthenticationStepMessage) Unmarshal(data []byte) error {
@ -1647,8 +1651,12 @@ func (m *AuthenticationStepMessage) Unmarshal(data []byte) error {
l := len(data)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthentication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
@ -1661,6 +1669,12 @@ func (m *AuthenticationStepMessage) Unmarshal(data []byte) error {
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthenticationStepMessage: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthenticationStepMessage: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
@ -1668,6 +1682,9 @@ func (m *AuthenticationStepMessage) Unmarshal(data []byte) error {
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthentication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
@ -1689,15 +1706,7 @@ func (m *AuthenticationStepMessage) Unmarshal(data []byte) error {
iNdEx = postIndex
hasFields[0] |= uint64(0x00000001)
default:
var sizeOfWire int
for {
sizeOfWire++
wire >>= 7
if wire == 0 {
break
}
}
iNdEx -= sizeOfWire
iNdEx = preIndex
skippy, err := skipAuthentication(data[iNdEx:])
if err != nil {
return err
@ -1716,104 +1725,21 @@ func (m *AuthenticationStepMessage) Unmarshal(data []byte) error {
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("data")
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthenticationCompletedMessage) Unmarshal(data []byte) error {
l := len(data)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if iNdEx >= l {
return io.ErrUnexpectedEOF
if shift >= 64 {
return ErrIntOverflowAuthentication
}
b := data[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
switch fieldNum {
default:
var sizeOfWire int
for {
sizeOfWire++
wire >>= 7
if wire == 0 {
break
}
}
iNdEx -= sizeOfWire
skippy, err := skipAuthentication(data[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthAuthentication
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
return nil
}
func (m *AuthenticationFailedMessage) Unmarshal(data []byte) error {
l := len(data)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
switch fieldNum {
default:
var sizeOfWire int
for {
sizeOfWire++
wire >>= 7
if wire == 0 {
break
}
}
iNdEx -= sizeOfWire
skippy, err := skipAuthentication(data[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthAuthentication
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
return nil
}
func (m *AuthenticationErrorMessage) Unmarshal(data []byte) error {
l := len(data)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
@ -1826,6 +1752,114 @@ func (m *AuthenticationErrorMessage) Unmarshal(data []byte) error {
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthenticationCompletedMessage: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthenticationCompletedMessage: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipAuthentication(data[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthAuthentication
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthenticationFailedMessage) Unmarshal(data []byte) error {
l := len(data)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthentication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthenticationFailedMessage: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthenticationFailedMessage: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipAuthentication(data[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthAuthentication
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *AuthenticationErrorMessage) Unmarshal(data []byte) error {
l := len(data)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthentication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: AuthenticationErrorMessage: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AuthenticationErrorMessage: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
@ -1833,6 +1867,9 @@ func (m *AuthenticationErrorMessage) Unmarshal(data []byte) error {
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAuthentication
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
@ -1855,15 +1892,7 @@ func (m *AuthenticationErrorMessage) Unmarshal(data []byte) error {
m.Error = &s
iNdEx = postIndex
default:
var sizeOfWire int
for {
sizeOfWire++
wire >>= 7
if wire == 0 {
break
}
}
iNdEx -= sizeOfWire
iNdEx = preIndex
skippy, err := skipAuthentication(data[iNdEx:])
if err != nil {
return err
@ -1879,6 +1908,9 @@ func (m *AuthenticationErrorMessage) Unmarshal(data []byte) error {
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipAuthentication(data []byte) (n int, err error) {
@ -1887,6 +1919,9 @@ func skipAuthentication(data []byte) (n int, err error) {
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAuthentication
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
@ -1900,7 +1935,10 @@ func skipAuthentication(data []byte) (n int, err error) {
wireType := int(wire & 0x7)
switch wireType {
case 0:
for {
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAuthentication
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
@ -1916,6 +1954,9 @@ func skipAuthentication(data []byte) (n int, err error) {
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAuthentication
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
@ -1936,6 +1977,9 @@ func skipAuthentication(data []byte) (n int, err error) {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAuthentication
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
@ -1971,4 +2015,5 @@ func skipAuthentication(data []byte) (n int, err error) {
var (
ErrInvalidLengthAuthentication = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowAuthentication = fmt.Errorf("proto: integer overflow")
)

File diff suppressed because it is too large Load Diff

View File

@ -1,108 +0,0 @@
/**
* 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 "github.com/gogo/protobuf/gogoproto/gogo.proto";
option (gogoproto.gostring_all) = true;
option (gogoproto.equal_all) = true;
option (gogoproto.verbose_equal_all) = true;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
option (gogoproto.populate_all) = true;
option (gogoproto.testgen_all) = true;
option (gogoproto.benchgen_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;
option (gogoproto.unmarshaler_all) = true;
/**
* ACLs used for local authorization (See authorization.md file in the
* docs).
*/
message ACL {
// Entity is used to describe a subject(s) or an object(s) of an ACL.
// NOTE:
// To allow everyone access to an Entity set its type to 'ANY'.
// To deny access to an Entity set its type to 'NONE'.
message Entity {
enum Type {
SOME = 0;
ANY = 1;
NONE = 2;
}
optional Type type = 1 [default = SOME];
repeated string values = 2; // Ignored for ANY/NONE.
}
// ACLs.
message RegisterFramework {
// Subjects.
required Entity principals = 1; // Framework principals.
// Objects.
required Entity roles = 2; // Roles for resource offers.
}
message RunTask {
// Subjects.
required Entity principals = 1; // Framework principals.
// Objects.
required Entity users = 2; // Users to run the tasks/executors as.
}
// Which principals are authorized to shutdown frameworks of other
// principals.
message ShutdownFramework {
// Subjects.
required Entity principals = 1;
// Objects.
required Entity framework_principals = 2;
}
}
/**
* Collection of ACL.
*
* Each authorization request is evaluated against the ACLs in the order
* they are defined.
*
* For simplicity, the ACLs for a given action are not aggregated even
* when they have the same subjects or objects. The first ACL that
* matches the request determines whether that request should be
* permitted or not. An ACL matches iff both the subjects
* (e.g., clients, principals) and the objects (e.g., urls, users,
* roles) of the ACL match the request.
*
* If none of the ACLs match the request, the 'permissive' field
* determines whether the request should be permitted or not.
*
* TODO(vinod): Do aggregation of ACLs when possible.
*
*/
message ACLs {
optional bool permissive = 1 [default = true];
repeated ACL.RegisterFramework register_frameworks = 2;
repeated ACL.RunTask run_tasks = 3;
repeated ACL.ShutdownFramework shutdown_frameworks = 4;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,112 +0,0 @@
/**
* 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";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option (gogoproto.gostring_all) = true;
option (gogoproto.equal_all) = true;
option (gogoproto.verbose_equal_all) = true;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
option (gogoproto.populate_all) = true;
option (gogoproto.testgen_all) = true;
option (gogoproto.benchgen_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;
option (gogoproto.unmarshaler_all) = true;
/**
* Encodes the launch command sent to the external containerizer
* program.
*/
message Launch {
required ContainerID container_id = 1;
optional TaskInfo task_info = 2;
optional ExecutorInfo executor_info = 3;
optional string directory = 4;
optional string user = 5;
optional SlaveID slave_id = 6;
optional string slave_pid = 7;
optional bool checkpoint = 8;
}
/**
* Encodes the update command sent to the external containerizer
* program.
*/
message Update {
required ContainerID container_id = 1;
repeated Resource resources = 2;
}
/**
* Encodes the wait command sent to the external containerizer
* program.
*/
message Wait {
required ContainerID container_id = 1;
}
/**
* Encodes the destroy command sent to the external containerizer
* program.
*/
message Destroy {
required ContainerID container_id = 1;
}
/**
* Encodes the usage command sent to the external containerizer
* program.
*/
message Usage {
required ContainerID container_id = 1;
}
/**
* Information about a container termination, returned by the
* containerizer to the slave.
*/
message Termination {
// A container may be killed if it exceeds its resources; this will
// 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 string message = 2;
// Exit status of the process.
optional int32 status = 3;
}
/**
* Information on all active containers returned by the containerizer
* to the slave.
*/
message Containers {
repeated ContainerID containers = 1;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,209 +0,0 @@
/**
* 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 "github.com/gogo/protobuf/gogoproto/gogo.proto";
option (gogoproto.gostring_all) = true;
option (gogoproto.equal_all) = true;
option (gogoproto.verbose_equal_all) = true;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
option (gogoproto.populate_all) = true;
option (gogoproto.testgen_all) = true;
option (gogoproto.benchgen_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;
option (gogoproto.unmarshaler_all) = true;
// Represents a "promise" that a replica has made. A promise is
// *implicitly* valid for _all_ future actions that get performed on
// the replicated log (provided the action comes from the same
// proposer), until a new promise is made to a proposer with a higher
// proposal number. Each replica writes every promise it makes as a
// log record so that it can recover this information after a failure.
// TODO(benh): Does the promise actually need to be written to stable
// storage? Can we get away with looking at the last written action
// and using it's promised value? In this case, what happens if we
// make a promise but don't receive an action from that coordinator?
message Promise {
required uint64 proposal = 1;
}
// Represents an "action" performed on the log. Each action has an
// associated position in the log. In addition, each action (i.e.,
// position) will have been "promised" to a specific proposer
// (implicitly or explicitly) and may have been "performed" from a
// specific proposer. An action may also be "learned" to have reached
// consensus. There are three types of possible actions that can be
// performed on the log: nop (no action), append, and truncate.
message Action {
required uint64 position = 1;
required uint64 promised = 2;
optional uint64 performed = 3;
optional bool learned = 4;
enum Type {
NOP = 1;
APPEND = 2;
TRUNCATE = 3;
}
message Nop {}
message Append {
required bytes bytes = 1;
optional bytes cksum = 2;
}
message Truncate {
required uint64 to = 1; // All positions before and exclusive of 'to'.
}
optional Type type = 5; // Set iff performed is set.
optional Nop nop = 6;
optional Append append = 7;
optional Truncate truncate = 8;
}
// The metadata of a replica. It has to be persisted on the disk. We
// store the current status of the replica as well as the implicit
// promise that a replica has made. This message is intended to
// replace the old Promise message to support catch-up.
message Metadata {
enum Status {
VOTING = 1; // Normal voting member in Paxos group.
RECOVERING = 2; // In the process of catching up.
STARTING = 3; // The log has been initialized.
EMPTY = 4; // The log is empty and is not initialized.
}
required Status status = 1 [default = EMPTY];
required uint64 promised = 2 [default = 0];
}
// Represents a log record written to the local filesystem by a
// replica. A log record may store a promise (DEPRECATED), an action
// or metadata (defined above).
message Record {
enum Type {
PROMISE = 1; // DEPRECATED!
ACTION = 2;
METADATA = 3;
}
required Type type = 1;
optional Promise promise = 2; // DEPRECATED!
optional Action action = 3;
optional Metadata metadata = 4;
}
////////////////////////////////////////////////////
// Replicated log request/responses and messages. //
////////////////////////////////////////////////////
// Represents a "promise" request from a proposer with the specified
// 'proposal' to a replica. If the proposer is a coordinator, most
// such requests will occur after a coordinator has failed and a new
// coordinator is elected. In such a case, the position that the
// coordinator is asking the replica to promise is implicitly *all*
// positions that the replica has made no promises (thus the position
// field is not be used). In other instances, however, a proposer
// might be explicitly trying to request that a replica promise a
// specific position in the log (such as when trying to fill holes
// discovered during a client read), and then the 'position' field
// will be present.
message PromiseRequest {
required uint64 proposal = 1;
optional uint64 position = 2;
}
// Represents a "promise" response from a replica back to a proposer.
// A replica represents a NACK (because it has promised a proposer
// with a higher proposal number) by setting the okay field to false.
// The 'proposal' is either the aforementioned higher proposal number
// when the response is a NACK, or the corresponding request's
// proposal number if it is an ACK. The replica either sends back the
// highest position it has recorded in the log (using the 'position'
// field) or the specific action (if any) it has at the position
// requested in PromiseRequest (using the 'action' field).
message PromiseResponse {
required bool okay = 1;
required uint64 proposal = 2;
optional uint64 position = 4;
optional Action action = 3;
}
// Represents a write request for a specific type of action. Note that
// we deliberately do not include the entire Action as it contains
// fields that are not relevant to a write request (e.g., promised,
// performed) and rather than ignore them we exclude them for safety.
message WriteRequest {
required uint64 proposal = 1;
required uint64 position = 2;
optional bool learned = 3;
required Action.Type type = 4;
optional Action.Nop nop = 5;
optional Action.Append append = 6;
optional Action.Truncate truncate = 7;
}
// Represents a write response corresponding to a write request. A
// replica represents a NACK (because it has promised a proposer with
// a higher proposal number) by setting the okay field to false. If
// the proposer is a coordinator, then it has been demoted. The
// 'position' should always correspond to the position set in the
// request. The 'proposal' is either the same proposal number set in
// the request in the case of an ACK, or the higher proposal number
// this position has been promised to in the case of a NACK.
message WriteResponse {
required bool okay = 1;
required uint64 proposal = 2;
required uint64 position = 3;
}
// Represents a "learned" event, that is, when a particular action has
// been agreed upon (reached consensus).
message LearnedMessage {
required Action action = 1;
}
// Represents a recover request. A recover request is used to initiate
// the recovery (by broadcasting it).
message RecoverRequest {}
// When a replica receives a RecoverRequest, it will reply with its
// current status, and the begin and the end of its current log.
message RecoverResponse {
required Metadata.Status status = 1;
optional uint64 begin = 2;
optional uint64 end = 3;
}

File diff suppressed because it is too large Load Diff

View File

@ -103,6 +103,24 @@ message ContainerID {
}
/**
* Represents time since the epoch, in nanoseconds.
*/
message TimeInfo {
required int64 nanoseconds = 1;
// TODO(josephw): Add time zone information, if necessary.
}
/**
* Represents duration in nanoseconds.
*/
message DurationInfo {
required int64 nanoseconds = 1;
}
/**
* A network address.
*
@ -129,6 +147,71 @@ message URL {
}
/**
* Represents an interval, from a given start time over a given duration.
* This interval pertains to an unavailability event, such as maintenance,
* and is not a generic interval.
*/
message Unavailability {
required TimeInfo start = 1;
// When added to `start`, this represents the end of the interval.
// If unspecified, the duration is assumed to be infinite.
optional DurationInfo duration = 2;
// TODO(josephw): Add additional fields for expressing the purpose and
// urgency of the unavailability event.
}
/**
* Represents a single machine, which may hold one or more slaves.
*
* NOTE: In order to match a slave to a machine, both the `hostname` and
* `ip` must match the values advertised by the slave to the master.
* Hostname is not case-sensitive.
*/
message MachineID {
optional string hostname = 1;
optional string ip = 2;
}
/**
* Holds information about a single machine, its `mode`, and any other
* relevant information which may affect the behavior of the machine.
*/
message MachineInfo {
// Describes the several states that a machine can be in. A `Mode`
// applies to a machine and to all associated slaves on the machine.
enum Mode {
// In this mode, a machine is behaving normally;
// offering resources, executing tasks, etc.
UP = 1;
// In this mode, all slaves on the machine are expected to cooperate with
// frameworks to drain resources. In general, draining is done ahead of
// a pending `unavailability`. The resources should be drained so as to
// maximize utilization prior to the maintenance but without knowingly
// violating the frameworks' requirements.
DRAINING = 2;
// In this mode, a machine is not running any tasks and will not offer
// any of its resources. Slaves on the machine will not be allowed to
// register with the master.
DOWN = 3;
}
required MachineID id = 1;
optional Mode mode = 2;
// Signifies that the machine may be unavailable during the given interval.
// See comments in `Unavailability` and for the `unavailability` fields
// in `Offer` and `InverseOffer` for more information.
optional Unavailability unavailability = 3;
}
/**
* Describes a framework.
*/
@ -732,6 +815,9 @@ message ResourceUsage {
// Current resource usage. If absent, the containerizer
// cannot provide resource usage.
optional ResourceStatistics statistics = 3;
// The container id for the executor specified in the executor_info field.
required ContainerID container_id = 4;
}
repeated Executor executors = 1;
@ -846,6 +932,18 @@ message Offer {
repeated Attribute attributes = 7;
repeated ExecutorID executor_ids = 6;
// Signifies that the resources in this Offer may be unavailable during
// the given interval. Any tasks launched using these resources may be
// killed when the interval arrives. For example, these resources may be
// part of a planned maintenance schedule.
//
// This field only provides information about a planned unavailability.
// The unavailability interval may not necessarily start at exactly this
// interval, nor last for exactly the duration of this interval.
// The unavailability may also be forever! See comments in
// `Unavailability` for more details.
optional Unavailability unavailability = 9;
// Defines an operation that can be performed against offers.
message Operation {
enum Type {
@ -886,6 +984,57 @@ message Offer {
}
/**
* A request to return some resources occupied by a framework.
*/
message InverseOffer {
// This is the same OfferID as found in normal offers, which allows
// re-use of some of the OfferID-only messages.
required OfferID id = 1;
// URL for reaching the slave running on the host. This enables some
// optimizations as described in MESOS-3012, such as allowing the
// scheduler driver to bypass the master and talk directly with a slave.
optional URL url = 2;
// The framework that should release its resources.
// If no specifics are provided (i.e. which slave), all the framework's
// resources are requested back.
required FrameworkID framework_id = 3;
// Specified if the resources need to be released from a particular slave.
// All the framework's resources on this slave are requested back,
// unless further qualified by the `resources` field.
optional SlaveID slave_id = 4;
// This InverseOffer represents a planned unavailability event in the
// specified interval. Any tasks running on the given framework or slave
// may be killed when the interval arrives. Therefore, frameworks should
// aim to gracefully terminate tasks prior to the arrival of the interval.
//
// For reserved resources, the resources are expected to be returned to the
// framework after the unavailability interval. This is an expectation,
// not a guarantee. For example, if the unavailability duration is not set,
// the resources may be removed permanently.
//
// For other resources, there is no guarantee that requested resources will
// be returned after the unavailability interval. The allocator has no
// obligation to re-offer these resources to the prior framework after
// the unavailability.
required Unavailability unavailability = 5;
// A list of resources being requested back from the framework,
// on the slave identified by `slave_id`. If no resources are specified
// then all resources are being requested back. For the purpose of
// maintenance, this field is always empty (maintenance always requests
// all resources back).
repeated Resource resources = 6;
// TODO(josephw): Add additional options for narrowing down the resources
// being requested back. Such as specific executors, tasks, etc.
}
/**
* Describes a task. Passed from the scheduler all the way to an
* executor (see SchedulerDriver::launchTasks and
@ -958,8 +1107,19 @@ message TaskStatus {
// TODO(bmahler): Differentiate between slave removal reasons
// (e.g. unhealthy vs. unregistered for maintenance).
enum Reason {
// TODO(jieyu): The default value when a caller doesn't check for
// presence is 0 and so ideally the 0 reason is not a valid one.
// Since this is not used anywhere, consider removing this reason.
REASON_COMMAND_EXECUTOR_FAILED = 0;
REASON_EXECUTOR_PREEMPTED = 17;
REASON_CONTAINER_LAUNCH_FAILED = 21;
REASON_CONTAINER_LIMITATION = 19;
REASON_CONTAINER_LIMITATION_DISK = 20;
REASON_CONTAINER_LIMITATION_MEMORY = 8;
REASON_CONTAINER_PREEMPTED = 17;
REASON_CONTAINER_UPDATE_FAILED = 22;
REASON_EXECUTOR_REGISTRATION_TIMEOUT = 23;
REASON_EXECUTOR_REREGISTRATION_TIMEOUT = 24;
REASON_EXECUTOR_TERMINATED = 1;
REASON_EXECUTOR_UNREGISTERED = 2;
REASON_FRAMEWORK_REMOVED = 3;
@ -967,7 +1127,6 @@ message TaskStatus {
REASON_INVALID_FRAMEWORKID = 5;
REASON_INVALID_OFFERS = 6;
REASON_MASTER_DISCONNECTED = 7;
REASON_MEMORY_LIMIT = 8;
REASON_RECONCILIATION = 9;
REASON_RESOURCES_UNKNOWN = 18;
REASON_SLAVE_DISCONNECTED = 10;
@ -1012,6 +1171,10 @@ message TaskStatus {
// labels should be used to tag TaskStatus message with light-weight
// meta-data.
optional Labels labels = 12;
// Container related information that is resolved dynamically such as
// network address.
optional ContainerStatus container_status = 13;
}
@ -1073,7 +1236,7 @@ message Parameters {
*/
message Credential {
required string principal = 1;
optional bytes secret = 2;
optional string secret = 2;
}
@ -1098,7 +1261,7 @@ message RateLimit {
optional double qps = 1;
// Principal of framework(s) to be throttled. Should match
// FrameworkInfo.princpal and Credential.principal (if using authentication).
// FrameworkInfo.principal and Credential.principal (if using authentication).
required string principal = 2;
// Max number of outstanding messages from frameworks of this principal
@ -1145,7 +1308,7 @@ message Image {
// Protobuf for specifying an Appc container image. See:
// https://github.com/appc/spec/blob/master/spec/aci.md
message AppC {
message Appc {
// The name of the image.
required string name = 1;
@ -1160,7 +1323,10 @@ message Image {
}
message Docker {
// The name of the image. Expected in format repository[:tag].
// The name of the image. Expected format:
// [REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG|@TYPE:DIGEST]
//
// See: https://docs.docker.com/reference/commandline/pull/
required string name = 1;
}
@ -1168,7 +1334,7 @@ message Image {
// Only one of the following image messages should be set to match
// the type.
optional AppC appc = 2;
optional Appc appc = 2;
optional Docker docker = 3;
}
@ -1204,6 +1370,90 @@ message Volume {
}
/**
* Describes a network request from a framework as well as network resolution
* provided by Mesos.
*
* A Framework may request the network isolator on the Agent to isolate the
* container in a network namespace and create a virtual network interface.
* The `NetworkInfo` message describes the properties of that virtual
* interface, including the IP addresses and network isolation policy
* (network group membership).
*
* The NetworkInfo message is not interpreted by the Master or Agent and is
* intended to be used by Agent and Master modules implementing network
* isolation. If the modules are missing, the message is simply ignored. In
* future, the task launch will fail if there is no module providing the
* network isolation capabilities (MESOS-3390).
*
* An executor, Agent, or an Agent module may append NetworkInfos inside
* TaskStatus::container_status to provide information such as the container IP
* address and isolation groups.
*/
message NetworkInfo {
enum Protocol {
IPv4 = 1;
IPv6 = 2;
}
// Specifies a request for an IP address, or reports the assigned container
// IP address.
//
// Users can request an automatically assigned IP (for example, via an
// IPAM service) or a specific IP by adding a NetworkInfo to the
// ContainerInfo for a task. On a request, specifying neither `protocol`
// nor `ip_address` means that any available address may be assigned.
message IPAddress {
// Specify IP address requirement. Set protocol to the desired value to
// request the network isolator on the Agent to assign an IP address to the
// container being launched. If a specific IP address is specified in
// ip_address, this field should not be set.
optional Protocol protocol = 1;
// Statically assigned IP provided by the Framework. This IP will be
// assigned to the container by the network isolator module on the Agent.
// This field should not be used with the protocol field above.
//
// If an explicit address is requested but is unavailable, the network
// isolator should fail the task.
optional string ip_address = 2;
}
// When included in a ContainerInfo, each of these represent a
// request for an IP address. Each request can specify an explicit address
// or the IP protocol to use.
//
// When included in a TaskStatus message, these inform the framework
// scheduler about the IP addresses that are bound to the container
// interface. When there are no custom network isolator modules installed,
// this field is filled in automatically with the Agent IP address.
repeated IPAddress ip_addresses = 5;
// Specify IP address requirement. Set protocol to the desired value to
// request the network isolator on the Agent to assign an IP address to the
// container being launched. If a specific IP address is specified in
// ip_address, this field should not be set.
optional Protocol protocol = 1 [deprecated = true]; // Since 0.26.0
// Statically assigned IP provided by the Framework. This IP will be assigned
// to the container by the network isolator module on the Agent. This field
// should not be used with the protocol field above.
// NOTE: It is up to the networking 'provider' (IPAM/Isolator) to interpret
// this either as a hint of as a requirement for assigning the IP.
optional string ip_address = 2 [deprecated = true]; // Since 0.26.0
// A group is the name given to a set of logically-related interfaces that
// are allowed to communicate among themselves. Network traffic is allowed
// between two container interfaces that share at least one network group.
// For example, one might want to create separate groups for isolating dev,
// testing, qa and prod deployment environments.
repeated string groups = 3;
// To tag certain metadata to be used by Isolator/IPAM, e.g., rack, etc.
optional Labels labels = 4;
};
/**
* Describes a container configuration and allows extensible
* configurations for different container implementations.
@ -1263,6 +1513,20 @@ message ContainerInfo {
// the type.
optional DockerInfo docker = 3;
optional MesosInfo mesos = 5;
// A list of network requests. A framework can request multiple IP addresses
// for the container.
repeated NetworkInfo network_infos = 7;
}
/**
* Container related information that is resolved during container setup. The
* information is sent back to the framework as part of the TaskStatus message.
*/
message ContainerStatus {
// This field can be reliably used to identify the container IP address.
repeated NetworkInfo network_infos = 1;
}
@ -1290,6 +1554,7 @@ message Port {
required uint32 number = 1;
optional string name = 2;
optional string protocol = 3;
optional DiscoveryInfo.Visibility visibility = 4;
}
@ -1303,15 +1568,16 @@ message Ports {
/**
* 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
* The visibility field restricts discovery within a framework (FRAMEWORK),
* within a Mesos cluster (CLUSTER), or places no restrictions (EXTERNAL).
* Each port in the ports field also has an optional visibility field.
* If visibility is specified for a port, it overrides the default service-wide
* DiscoveryInfo.visibility for that port.
* 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 {

File diff suppressed because it is too large Load Diff

View File

@ -33,24 +33,28 @@ option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;
option (gogoproto.unmarshaler_all) = true;
// TODO(benh): Provide comments for each of these messages. Also,
// consider splitting these messages into different "packages" which
// represent which messages get handled by which components (e.g., the
// "mesos.executor" package includes messages that the
// executor handles).
// TODO(benh): Consider splitting these messages into different "packages"
// which represent which messages get handled by which components (e.g., the
// "mesos.executor" package includes messages that the executor handles).
// TODO(benh): It would be great if this could just be a
// TaskInfo wherever it gets used! However, doing so would
// require adding the framework_id field, the executor_id field, and
// the state field into TaskInfo though (or send them another
// way). Also, one performance reason why we don't do that now is
// because storing whatever data is coupled with a TaskInfo
// could be large and unnecessary.
// TODO(bmahler): Add executor_uuid here, and send it to the master. This will
// allow us to expose executor work directories for tasks in the webui when
// looking from the master level. Currently only the slave knows which run the
// task belongs to.
/**
* Describes a task, similar to `TaskInfo`.
*
* `Task` is used in some of the Mesos messages found below.
* `Task` is used instead of `TaskInfo` if:
* 1) we need additional IDs, such as a specific
* framework, executor, or agent; or
* 2) we do not need the additional data, such as the command run by the
* task, the specific containerization, or health checks. These
* additional fields may be large and unnecessary for some Mesos messages.
*
* `Task` is generally constructed from a `TaskInfo`. See protobuf::createTask.
*/
message Task {
required string name = 1;
required TaskID task_id = 2;
@ -78,20 +82,31 @@ message Task {
// TODO(vinod): Create a new UUID message type.
/**
* Describes a task's status.
*
* `StatusUpdate` is used in some of the Mesos messages found below.
* The master and agent use `StatusUpdate` to wrap a `TaskStatus` when
* passing it from the agent to the framework that spawned the task.
*
* See protobuf::createStatusUpdate.
*/
message StatusUpdate {
required FrameworkID framework_id = 1;
optional ExecutorID executor_id = 2;
optional SlaveID slave_id = 3;
// Since 0.23.0 we set 'status.uuid' in the executor
// driver for all retryable status updates.
required TaskStatus status = 4;
required double timestamp = 5;
// 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.
// Since 0.26.0 this is deprecated in favor of 'status.uuid'.
optional bytes uuid = 6;
// This corresponds to the latest state of the task according to the
// slave. Note that this state might be different than the state in
// agent. Note that this state might be different than the state in
// 'status' because status update manager queues updates. In other
// words, 'status' corresponds to the update at top of the queue and
// 'latest_state' corresponds to the update at bottom of the queue.
@ -99,32 +114,48 @@ message StatusUpdate {
}
// This message encapsulates how we checkpoint a status update to disk.
// NOTE: If type == UPDATE, the 'update' field is required.
// NOTE: If type == ACK, the 'uuid' field is required.
/**
* Encapsulates how we checkpoint a `StatusUpdate` to disk.
*
* See the StatusUpdateManager and slave/state.cpp.
*/
message StatusUpdateRecord {
enum Type {
UPDATE = 0;
ACK = 1;
}
required Type type = 1;
// Required if type == UPDATE.
optional StatusUpdate update = 2;
// Required if type == ACK.
optional bytes uuid = 3;
}
// TODO(josephw): Check if this can be removed. This appears to be
// for backwards compatibility with very early versions of Mesos.
message SubmitSchedulerRequest
{
required string name = 1;
}
// TODO(josephw): Remove for the same reason as `SubmitSchedulerRequest`.
message SubmitSchedulerResponse
{
required bool okay = 1;
}
/**
* Sends a free-form message from the executor to the framework.
* Mesos forwards the message, if necessary, via the agents and the master.
*
* See scheduler::Event::Message.
*/
message ExecutorToFrameworkMessage {
required SlaveID slave_id = 1;
required FrameworkID framework_id = 2;
@ -133,6 +164,12 @@ message ExecutorToFrameworkMessage {
}
/**
* Sends a free-form message from the framework to the executor.
* Mesos forwards the message, if necessary, via the agents and the master.
*
* See scheduler::Call::Message.
*/
message FrameworkToExecutorMessage {
required SlaveID slave_id = 1;
required FrameworkID framework_id = 2;
@ -141,49 +178,115 @@ message FrameworkToExecutorMessage {
}
/**
* Subscribes the framework with the master to receive events.
*
* Used by the pre-Event/Call Mesos scheduler driver.
* See scheduler::Call::Subscribe.
*/
message RegisterFrameworkMessage {
required FrameworkInfo framework = 1;
}
/**
* Subscribes the framework with the master to receive events.
* This is used when the framework has previously registered and
* the master changes to a newly elected master.
*
* Used by the pre-Event/Call Mesos scheduler driver.
* See scheduler::Call::Subscribe.
*/
message ReregisterFrameworkMessage {
required FrameworkInfo framework = 2;
required bool failover = 3;
}
/**
* Notifies the framework that the master has registered it.
* The `framework_id` holds a unique ID for distinguishing this framework.
*
* See scheduler::Event::Subscribed.
*/
message FrameworkRegisteredMessage {
required FrameworkID framework_id = 1;
required MasterInfo master_info = 2;
}
/**
* Notifies the framework that the master has reregistered it.
* This message is used in the same conditions as `ReregisterFrameworkMessage`.
*
* See scheduler::Event::Subscribed.
*/
message FrameworkReregisteredMessage {
required FrameworkID framework_id = 1;
required MasterInfo master_info = 2;
}
/**
* Stops the framework and shuts down all its tasks and executors.
*
* Used by the pre-Event/Call Mesos scheduler driver.
* See scheduler::Call::Teardown.
*/
message UnregisterFrameworkMessage {
required FrameworkID framework_id = 1;
}
/**
* Aborts the scheduler driver and prevents further callbacks to the driver.
*
* Used exclusively by the pre-Event/Call Mesos scheduler driver.
*/
message DeactivateFrameworkMessage {
required FrameworkID framework_id = 1;
}
/**
* Requests specific resources from Mesos's allocator.
* If the allocator supports resource requests, any corresponding
* resources will be sent like a normal resource offer.
*
* Used by the pre-Event/Call Mesos scheduler driver.
* See scheduler::Call::Request.
*/
message ResourceRequestMessage {
required FrameworkID framework_id = 1;
repeated Request requests = 2;
}
/**
* Sends resources offers to the scheduler.
*
* See scheduler::Event::Offers.
*/
message ResourceOffersMessage {
repeated Offer offers = 1;
repeated string pids = 2;
// The `inverse_offers` field is added here because we currently use it in
// `master.cpp` when constructing the message to send to schedulers. We use
// the original version of the proto API until we do a full refactor of all
// the messages being sent.
// It is not fully implemented in the old scheduler; only the V1 scheduler
// currently implements inverse offers.
repeated InverseOffer inverse_offers = 3;
}
/**
* Launches tasks using resources from the specified offers.
*
* Used by the pre-Event/Call Mesos scheduler driver.
* See scheduler::Call::Accept and scheduler::Call::Decline.
*/
message LaunchTasksMessage {
required FrameworkID framework_id = 1;
repeated TaskInfo tasks = 3;
@ -192,16 +295,35 @@ message LaunchTasksMessage {
}
/**
* Notifies the scheduler that a particular offer is not longer valid.
*
* See scheduler::Event::Rescind.
*/
message RescindResourceOfferMessage {
required OfferID offer_id = 1;
}
/**
* Removes all filters previously set by the scheduler.
*
* Used by the pre-Event/Call Mesos scheduler driver.
* See scheduler::Call::Revive.
*/
message ReviveOffersMessage {
required FrameworkID framework_id = 1;
}
/**
* Depending on the `TaskInfo`, this message either notifies an existing
* executor to run the task, or starts a new executor and runs the task.
* This message is sent when scheduler::Call::Accept is sent with
* Offer::Operation::Launch.
*
* See executor::Event::Launch.
*/
message RunTaskMessage {
// TODO(karya): Remove framework_id after MESOS-2559 has shipped.
optional FrameworkID framework_id = 1 [deprecated = true];
@ -211,29 +333,47 @@ message RunTaskMessage {
// The pid of the framework. This was moved to 'optional' in
// 0.24.0 to support schedulers using the HTTP API. For now, we
// continue to always set pid since it was required in 0.23.x.
// When 'pid' is unset, or set to empty string, the slave will
// When 'pid' is unset, or set to empty string, the agent will
// forward executor messages through the master. For schedulers
// still using the driver, this will remain set.
optional string pid = 3;
}
/**
* Kills a specific task.
*
* See scheduler::Call::Kill and executor::Event::Kill.
*/
message KillTaskMessage {
// TODO(bmahler): Include the SlaveID here to improve the Master's
// ability to respond for non-activated slaves.
// ability to respond for non-activated agents.
required FrameworkID framework_id = 1;
required TaskID task_id = 2;
}
// NOTE: If 'pid' is present, scheduler driver sends an
// acknowledgement to the pid.
/**
* Sends a task status update to the scheduler.
*
* See scheduler::Event::Update.
*/
message StatusUpdateMessage {
required StatusUpdate update = 1;
// If present, scheduler driver automatically sends an acknowledgement
// to the `pid`. This only applies to the pre-Event/Call Mesos
// scheduler driver.
optional string pid = 2;
}
/**
* This message is used by the scheduler to acknowledge the receipt of a status
* update. Mesos forwards the acknowledgement to the executor running the task.
*
* See scheduler::Call::Acknowledge and executor::Event::Acknowledged.
*/
message StatusUpdateAcknowledgementMessage {
required SlaveID slave_id = 1;
required FrameworkID framework_id = 2;
@ -242,47 +382,73 @@ message StatusUpdateAcknowledgementMessage {
}
/**
* Notifies the scheduler that the agent was lost.
*
* See scheduler::Event::Failure.
*/
message LostSlaveMessage {
required SlaveID slave_id = 1;
}
// Allows the framework to query the status for non-terminal tasks.
// This causes the master to send back the latest task status for
// each task in 'statuses', if possible. Tasks that are no longer
// known will result in a TASK_LOST update. If statuses is empty,
// then the master will send the latest status for each task
// currently known.
/**
* Allows the scheduler to query the status for non-terminal tasks.
* This causes the master to send back the latest task status for
* each task in `statuses`, if possible. Tasks that are no longer
* known will result in a `TASK_LOST` update. If `statuses` is empty,
* then the master will send the latest status for each task
* currently known.
*/
message ReconcileTasksMessage {
required FrameworkID framework_id = 1;
repeated TaskStatus statuses = 2; // Should be non-terminal only.
}
/**
* Notifies the framework about errors during registration.
*
* See scheduler::Event::Error.
*/
message FrameworkErrorMessage {
required string message = 2;
}
/**
* Registers the agent with the master.
*
* If registration fails, a `ShutdownMessage` is sent to the agent.
* Failure conditions are documented inline in Master::registerSlave.
*/
message RegisterSlaveMessage {
required SlaveInfo slave = 1;
// Resources that are checkpointed by the slave (e.g., persistent
// Resources that are checkpointed by the agent (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
// version. If unset the slave is < 0.21.0.
// NOTE: This is a hack for the master to detect the agent's
// version. If unset the agent is < 0.21.0.
// TODO(bmahler): Do proper versioning: MESOS-986.
optional string version = 2;
}
/**
* Registers the agent with the master.
* This is used when the agent has previously registered and
* the master changes to a newly elected master.
*
* If registration fails, a `ShutdownMessage` is sent to the agent.
* Failure conditions are documented inline in Master::reregisterSlave.
*/
message ReregisterSlaveMessage {
required SlaveInfo slave = 2;
// Resources that are checkpointed by the slave (e.g., persistent
// Resources that are checkpointed by the agent (e.g., persistent
// volume or dynamic reservation). Frameworks need to release
// checkpointed resources explicitly.
repeated Resource checkpointed_resources = 7;
@ -291,68 +457,98 @@ message ReregisterSlaveMessage {
repeated Task tasks = 3;
repeated Archive.Framework completed_frameworks = 5;
// NOTE: This is a hack for the master to detect the slave's
// version. If unset the slave is < 0.21.0.
// NOTE: This is a hack for the master to detect the agent's
// version. If unset the agent is < 0.21.0.
// TODO(bmahler): Do proper versioning: MESOS-986.
optional string version = 6;
}
/**
* Notifies the agent that the master has registered it.
* The `slave_id` holds a unique ID for distinguishing this agent.
*/
message SlaveRegisteredMessage {
required SlaveID slave_id = 1;
optional MasterSlaveConnection connection = 2;
}
/**
* Notifies the agent that the master has reregistered it.
* This message is used in the same conditions as `ReregisterSlaveMessage`.
*/
message SlaveReregisteredMessage {
required SlaveID slave_id = 1;
// Contains a list of non-terminal tasks that the master believes to
// be running on the agent. The agent should respond `TASK_LOST` to
// any tasks that are unknown, so the master knows to remove those.
repeated ReconcileTasksMessage reconciliations = 2;
optional MasterSlaveConnection connection = 3;
}
/**
* This message is sent by the agent to the master during agent shutdown.
* The master updates its state to reflect the removed agent.
*/
message UnregisterSlaveMessage {
required SlaveID slave_id = 1;
}
/**
* Describes the connection between the master and agent.
*/
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.
// the master will remove the agent.
optional double total_ping_timeout_seconds = 1;
}
// This message is periodically sent by the master to the slave.
// If the slave is connected to the master, "connected" is true.
/**
* This message is periodically sent by the master to the agent.
* If the agent is connected to the master, "connected" is true.
*/
message PingSlaveMessage {
required bool connected = 1;
}
// This message is sent by the slave to the master in response to the
// PingSlaveMessage.
/**
* This message is sent by the agent to the master in response to the
* `PingSlaveMessage`.
*/
message PongSlaveMessage {}
// Tells a slave to shut down all executors of the given framework.
/**
* Tells an agent to shut down all executors of the given framework.
*/
message ShutdownFrameworkMessage {
required FrameworkID framework_id = 1;
}
// Tells a slave (and consequently executor) to shutdown an executor.
/**
* Tells an agent (and consequently the executor) to shutdown an executor.
*/
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
// for backwards compatibility between 0.23.0 agent and pre 0.23.0
// executor driver.
optional ExecutorID executor_id = 1;
optional FrameworkID framework_id = 2;
}
/**
* Broadcasts updated framework information from master to all agents.
*/
message UpdateFrameworkMessage {
required FrameworkID framework_id = 1;
@ -361,29 +557,43 @@ 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).
/**
* This message is sent to the agent 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.
/**
* This message is sent by the agent 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;
}
/**
* Subscribes the executor with the agent to receive events.
*
* See executor::Call::Subscribe.
*/
message RegisterExecutorMessage {
required FrameworkID framework_id = 1;
required ExecutorID executor_id = 2;
}
/**
* Notifies the executor that the agent has registered it.
*
* See executor::Event::Subscribed.
*/
message ExecutorRegisteredMessage {
required ExecutorInfo executor_info = 2;
required FrameworkID framework_id = 3;
@ -393,12 +603,22 @@ message ExecutorRegisteredMessage {
}
/**
* Notifies the executor that the agent has reregistered it.
*
* See executor::Event::Subscribed.
*/
message ExecutorReregisteredMessage {
required SlaveID slave_id = 1;
required SlaveInfo slave_info = 2;
}
/**
* Notifies the scheduler about terminated executors.
*
* See scheduler::Event::Failure.
*/
message ExitedExecutorMessage {
required SlaveID slave_id = 1;
required FrameworkID framework_id = 2;
@ -407,11 +627,22 @@ message ExitedExecutorMessage {
}
/**
* Reestablishes the connection between executor and agent after agent failover.
* This message originates from the agent.
*/
message ReconnectExecutorMessage {
required SlaveID slave_id = 1;
}
/**
* Subscribes the executor with the agent to receive events.
* This is used after a disconnection. The executor must include
* any unacknowledged tasks or updates.
*
* See executor::Call::Subscribe.
*/
message ReregisterExecutorMessage {
required ExecutorID executor_id = 1;
required FrameworkID framework_id = 2;
@ -420,6 +651,11 @@ message ReregisterExecutorMessage {
}
/**
* Sends a free-form message from the master to an agent.
* The agent should gracefully terminate in response, which includes
* shutting down all executors and tasks on the agent.
*/
message ShutdownMessage {
optional string message = 1;
}
@ -438,12 +674,15 @@ message Archive {
repeated Framework frameworks = 1;
}
// Message describing task current health status that is sent by
// the task health checker to the command executor.
// The command executor reports the task status back to the
// on each receive. If the health checker configured faiure
// condition meets, then kill_task flag will be set to true which
// the executor on message receive will kill the task.
/**
* Message describing task current health status that is sent by
* the task health checker to the command executor.
* The command executor reports the task status back to the
* on each receive. If the health checker configured failure
* condition meets, then `kill_task` flag will be set to true which
* the executor on message receive will kill the task.
*/
message TaskHealthStatus {
required TaskID task_id = 1;

File diff suppressed because it is too large Load Diff

View File

@ -1,54 +0,0 @@
/**
* 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";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option (gogoproto.gostring_all) = true;
option (gogoproto.equal_all) = true;
option (gogoproto.verbose_equal_all) = true;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
option (gogoproto.populate_all) = true;
option (gogoproto.testgen_all) = true;
option (gogoproto.benchgen_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;
option (gogoproto.unmarshaler_all) = true;
message Registry {
message Master {
required MasterInfo info = 1;
}
message Slave {
required SlaveInfo info = 1;
}
message Slaves {
repeated Slave slaves = 1;
}
// Most recent leading master.
optional Master master = 1;
// All admitted slaves.
optional Slaves slaves = 2;
}

View File

@ -16,11 +16,12 @@
* limitations under the License.
*/
package mesosproto;
package mesosproto.scheduler;
import "mesos.proto";
import "github.com/mesos/mesos-go/mesosproto/mesos.proto";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option go_package = "scheduler";
option (gogoproto.gostring_all) = true;
option (gogoproto.equal_all) = true;
option (gogoproto.verbose_equal_all) = true;
@ -68,17 +69,19 @@ message Event {
// This value will be set if the master is sending heartbeats. See
// the comment above on 'HEARTBEAT' for more details.
// TODO(vinod): Implement heartbeats in the master once the master
// can send HTTP events.
optional double heartbeat_interval_seconds = 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.
// scheduler or resources requested back from 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. Accepting or Declining an
// inverse offer informs the allocator of the scheduler's ability to
// release the resources without violating an SLA.
message Offers {
repeated Offer offers = 1;
repeated InverseOffer inverse_offers = 2;
}
// Received when a particular offer is no longer valid (e.g., the
@ -179,6 +182,7 @@ message Call {
RECONCILE = 9; // See 'Reconcile' below.
MESSAGE = 10; // See 'Message' below.
REQUEST = 11; // See 'Request' below.
SUPPRESS = 12; // Inform master to stop sending offers to the framework.
// TODO(benh): Consider adding an 'ACTIVATE' and 'DEACTIVATE' for
// already subscribed frameworks as a way of stopping offers from

File diff suppressed because it is too large Load Diff

View File

@ -1,72 +0,0 @@
/**
* 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 "github.com/gogo/protobuf/gogoproto/gogo.proto";
option (gogoproto.gostring_all) = true;
option (gogoproto.equal_all) = true;
option (gogoproto.verbose_equal_all) = true;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
option (gogoproto.populate_all) = true;
option (gogoproto.testgen_all) = true;
option (gogoproto.benchgen_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;
option (gogoproto.unmarshaler_all) = true;
// Describes a state entry, a versioned (via a UUID) key/value pair.
message Entry {
required string name = 1;
required bytes uuid = 2;
required bytes value = 3;
}
// Describes an operation used in the log storage implementation.
message Operation {
enum Type {
SNAPSHOT = 1;
DIFF = 3;
EXPUNGE = 2;
}
// Describes a "snapshot" operation.
message Snapshot {
required Entry entry = 1;
}
// Describes a "diff" operation where the 'value' of the entry is
// just the diff itself, but the 'uuid' represents the UUID of the
// entry after applying this diff.
message Diff {
required Entry entry = 1;
}
// Describes an "expunge" operation.
message Expunge {
required string name = 1;
}
required Type type = 1;
optional Snapshot snapshot = 2;
optional Diff diff = 4;
optional Expunge expunge = 3;
}

View File

@ -36,6 +36,28 @@ func FilterResources(resources []*mesos.Resource, filter func(*mesos.Resource) b
return result
}
func AddResourceReservation(resource *mesos.Resource, principal string, role string) *mesos.Resource {
resource.Reservation = &mesos.Resource_ReservationInfo{Principal: proto.String(principal)}
resource.Role = proto.String(role)
return resource
}
func NewScalarResourceWithReservation(name string, value float64, principal string, role string) *mesos.Resource {
return AddResourceReservation(NewScalarResource(name, value), principal, role)
}
func NewRangesResourceWithReservation(name string, ranges []*mesos.Value_Range, principal string, role string) *mesos.Resource {
return AddResourceReservation(NewRangesResource(name, ranges), principal, role)
}
func NewSetResourceWithReservation(name string, items []string, principal string, role string) *mesos.Resource {
return AddResourceReservation(NewSetResource(name, items), principal, role)
}
func NewVolumeResourceWithReservation(val float64, containerPath string, persistenceId string, mode *mesos.Volume_Mode, principal string, role string) *mesos.Resource {
return AddResourceReservation(NewVolumeResource(val, containerPath, persistenceId, mode), principal, role)
}
func NewScalarResource(name string, val float64) *mesos.Resource {
return &mesos.Resource{
Name: proto.String(name),
@ -58,7 +80,15 @@ func NewSetResource(name string, items []string) *mesos.Resource {
Type: mesos.Value_SET.Enum(),
Set: &mesos.Value_Set{Item: items},
}
}
func NewVolumeResource(val float64, containerPath string, persistenceId string, mode *mesos.Volume_Mode) *mesos.Resource {
resource := NewScalarResource("disk", val)
resource.Disk = &mesos.Resource_DiskInfo{
Persistence: &mesos.Resource_DiskInfo_Persistence{Id: proto.String(persistenceId)},
Volume: &mesos.Volume{ContainerPath: proto.String(containerPath), Mode: mode},
}
return resource
}
func NewFrameworkID(id string) *mesos.FrameworkID {
@ -153,3 +183,38 @@ func NewExecutorInfo(execId *mesos.ExecutorID, command *mesos.CommandInfo) *meso
Command: command,
}
}
func NewCreateOperation(volumes []*mesos.Resource) *mesos.Offer_Operation {
return &mesos.Offer_Operation{
Type: mesos.Offer_Operation_CREATE.Enum(),
Create: &mesos.Offer_Operation_Create{Volumes: volumes},
}
}
func NewDestroyOperation(volumes []*mesos.Resource) *mesos.Offer_Operation {
return &mesos.Offer_Operation{
Type: mesos.Offer_Operation_DESTROY.Enum(),
Destroy: &mesos.Offer_Operation_Destroy{Volumes: volumes},
}
}
func NewReserveOperation(resources []*mesos.Resource) *mesos.Offer_Operation {
return &mesos.Offer_Operation{
Type: mesos.Offer_Operation_RESERVE.Enum(),
Reserve: &mesos.Offer_Operation_Reserve{Resources: resources},
}
}
func NewUnreserveOperation(resources []*mesos.Resource) *mesos.Offer_Operation {
return &mesos.Offer_Operation{
Type: mesos.Offer_Operation_UNRESERVE.Enum(),
Unreserve: &mesos.Offer_Operation_Unreserve{Resources: resources},
}
}
func NewLaunchOperation(tasks []*mesos.TaskInfo) *mesos.Offer_Operation {
return &mesos.Offer_Operation{
Type: mesos.Offer_Operation_LAUNCH.Enum(),
Launch: &mesos.Offer_Operation_Launch{TaskInfos: tasks},
}
}

View File

@ -1,6 +1,7 @@
package mesosutil
import (
"os"
"os/exec"
"strings"
@ -9,15 +10,20 @@ import (
//TODO(jdef) copied from kubernetes/pkg/util/node.go
func GetHostname(hostnameOverride string) string {
hostname := []byte(hostnameOverride)
if string(hostname) == "" {
hostname := hostnameOverride
if hostname == "" {
// Note: We use exec here instead of os.Hostname() because we
// want the FQDN, and this is the easiest way to get it.
fqdn, err := exec.Command("hostname", "-f").Output()
if err != nil {
log.Fatalf("Couldn't determine hostname: %v", err)
if err != nil || len(fqdn) == 0 {
log.Errorf("Couldn't determine hostname fqdn, failing back to hostname: %v", err)
hostname, err = os.Hostname()
if err != nil {
log.Fatalf("Error getting hostname: %v", err)
}
} else {
hostname = string(fqdn)
}
hostname = fqdn
}
return strings.TrimSpace(string(hostname))
return strings.TrimSpace(hostname)
}

View File

@ -209,7 +209,7 @@ func (d *httpDecoder) buildResponseEntity(resp *Response) *bytes.Buffer {
// updateForRequest updates the chunked and kalive fields of the decoder to align
// with the header values of the request
func (d *httpDecoder) updateForRequest() {
func (d *httpDecoder) updateForRequest(bootstrapping bool) {
// check "Transfer-Encoding" for "chunked"
d.chunked = false
for _, v := range d.req.Header["Transfer-Encoding"] {
@ -219,11 +219,16 @@ func (d *httpDecoder) updateForRequest() {
}
}
if !d.chunked && d.req.ContentLength < 0 {
// strongly suspect that Go's internal net/http lib is stripping
// the Transfer-Encoding header from the initial request, so this
// workaround makes a very mesos-specific assumption: an unknown
// Content-Length indicates a chunked stream.
d.chunked = true
if bootstrapping {
// strongly suspect that Go's internal net/http lib is stripping
// the Transfer-Encoding header from the initial request, so this
// workaround makes a very mesos-specific assumption: an unknown
// Content-Length indicates a chunked stream.
d.chunked = true
} else {
// via https://tools.ietf.org/html/rfc7230#section-3.3.2
d.req.ContentLength = 0
}
}
// check "Connection" for "Keep-Alive"
@ -351,7 +356,7 @@ func limit(r *bufio.Reader, limit int64) *io.LimitedReader {
func (d *httpDecoder) bootstrapState(res http.ResponseWriter) httpState {
log.V(2).Infoln(d.idtag + "bootstrap-state")
d.updateForRequest()
d.updateForRequest(true)
// hijack
hj, ok := res.(http.Hijacker)
@ -636,6 +641,6 @@ func readHeaderState(d *httpDecoder) httpState {
log.V(2).Infoln(d.idtag+"set content length", r.ContentLength)
}
}
d.updateForRequest()
d.updateForRequest(false)
return d.readBodyContent()
}

View File

@ -590,9 +590,11 @@ func (t *HTTPTransporter) makeLibprocessRequest(msg *Message) (*http.Request, er
log.Errorf("Failed to create request: %v\n", err)
return nil, err
}
req.Header.Add("Libprocess-From", t.upid.String())
if !msg.isV1API() {
req.Header.Add("Libprocess-From", t.upid.String())
req.Header.Add("Connection", "Keep-Alive")
}
req.Header.Add("Content-Type", "application/x-protobuf")
req.Header.Add("Connection", "Keep-Alive")
return req, nil
}

View File

@ -36,9 +36,17 @@ type Message struct {
// RequestURI returns the request URI of the message.
func (m *Message) RequestURI() string {
if m.isV1API() {
return fmt.Sprintf("/api/v1/%s", m.Name)
}
return fmt.Sprintf("/%s/%s", m.UPID.ID, m.Name)
}
func (m *Message) isV1API() bool {
return !strings.HasPrefix(m.Name, "mesos.internal")
}
// NOTE: This should not fail or panic.
func extractNameFromRequestURI(requestURI string) string {
return strings.Split(requestURI, "/")[2]

View File

@ -28,6 +28,7 @@ import (
"github.com/gogo/protobuf/proto"
log "github.com/golang/glog"
mesos "github.com/mesos/mesos-go/mesosproto"
"github.com/mesos/mesos-go/mesosproto/scheduler"
"github.com/mesos/mesos-go/mesosutil/process"
"github.com/mesos/mesos-go/upid"
"golang.org/x/net/context"
@ -391,5 +392,14 @@ func (m *MesosMessenger) decodeLoop() {
// getMessageName returns the name of the message in the mesos manner.
func getMessageName(msg proto.Message) string {
return fmt.Sprintf("%v.%v", "mesos.internal", reflect.TypeOf(msg).Elem().Name())
var msgName string
switch msg := msg.(type) {
case *scheduler.Call:
msgName = "scheduler"
default:
msgName = fmt.Sprintf("%v.%v", "mesos.internal", reflect.TypeOf(msg).Elem().Name())
}
return msgName
}

View File

@ -18,7 +18,7 @@ func (h *CredentialHandler) Handle(callbacks ...callback.Interface) error {
case *callback.Name:
cb.Set(h.credential.GetPrincipal())
case *callback.Password:
cb.Set(h.credential.GetSecret())
cb.Set(([]byte)(h.credential.GetSecret()))
case *callback.Interprocess:
cb.Set(*(h.pid), *(h.client))
default:

View File

@ -66,6 +66,11 @@ type SchedulerDriver interface {
// framework via Scheduler.ResourceOffers callback, asynchronously.
RequestResources(requests []*mesos.Request) (mesos.Status, error)
// AcceptOffers utilizes the new HTTP API to send a Scheduler Call Message
// to the Mesos Master. Valid operation types are LAUNCH, RESERVE, UNRESERVE,
// CREATE, DESTROY, and more.
AcceptOffers(offerIDs []*mesos.OfferID, operations []*mesos.Offer_Operation, filters *mesos.Filters) (mesos.Status, error)
// Launches the given set of tasks. Any resources remaining (i.e.,
// not used by the tasks or their executors) will be considered
// declined. The specified filters are applied on all unused

View File

@ -34,6 +34,7 @@ import (
"github.com/mesos/mesos-go/auth"
"github.com/mesos/mesos-go/detector"
mesos "github.com/mesos/mesos-go/mesosproto"
"github.com/mesos/mesos-go/mesosproto/scheduler"
util "github.com/mesos/mesos-go/mesosutil"
"github.com/mesos/mesos-go/mesosutil/process"
"github.com/mesos/mesos-go/messenger"
@ -1083,7 +1084,7 @@ func (driver *MesosSchedulerDriver) abort(cause error) (stat mesos.Status, err e
if driver.connected {
_, err = driver.stop(cause, true)
} else {
driver.messenger.Stop()
driver._stop(cause, mesos.Status_DRIVER_ABORTED)
}
stat = mesos.Status_DRIVER_ABORTED
@ -1091,6 +1092,117 @@ func (driver *MesosSchedulerDriver) abort(cause error) (stat mesos.Status, err e
return
}
func (driver *MesosSchedulerDriver) AcceptOffers(offerIds []*mesos.OfferID, operations []*mesos.Offer_Operation, filters *mesos.Filters) (mesos.Status, error) {
driver.eventLock.Lock()
defer driver.eventLock.Unlock()
if stat := driver.status; stat != mesos.Status_DRIVER_RUNNING {
return stat, fmt.Errorf("Unable to AcceptOffers, expected driver status %s, but got %s", mesos.Status_DRIVER_RUNNING, stat)
}
if !driver.connected {
err := fmt.Errorf("Not connected to master.")
for _, operation := range operations {
if *operation.Type == mesos.Offer_Operation_LAUNCH {
for _, task := range operation.Launch.TaskInfos {
driver.pushLostTask(task, "Unable to launch tasks: "+err.Error())
}
}
}
log.Errorf("Failed to send LaunchTask message: %v\n", err)
return driver.status, err
}
okOperations := make([]*mesos.Offer_Operation, 0, len(operations))
for _, offerId := range offerIds {
for _, operation := range operations {
// Keep only the slave PIDs where we run tasks so we can send
// framework messages directly.
if !driver.cache.containsOffer(offerId) {
log.Warningf("Attempting to accept offers with unknown offer %s\n", offerId.GetValue())
continue
}
// Validate
switch *operation.Type {
case mesos.Offer_Operation_LAUNCH:
tasks := []*mesos.TaskInfo{}
// Set TaskInfo.executor.framework_id, if it's missing.
for _, task := range operation.Launch.TaskInfos {
newTask := *task
if newTask.Executor != nil && newTask.Executor.FrameworkId == nil {
newTask.Executor.FrameworkId = driver.frameworkInfo.Id
}
tasks = append(tasks, &newTask)
}
for _, task := range tasks {
if driver.cache.getOffer(offerId).offer.SlaveId.Equal(task.SlaveId) {
// cache the tasked slave, for future communication
pid := driver.cache.getOffer(offerId).slavePid
driver.cache.putSlavePid(task.SlaveId, pid)
} else {
log.Warningf("Attempting to launch task %s with the wrong slaveId offer %s\n", task.TaskId.GetValue(), task.SlaveId.GetValue())
}
}
operation.Launch.TaskInfos = tasks
okOperations = append(okOperations, operation)
case mesos.Offer_Operation_RESERVE:
// Only send reserved resources
filtered := util.FilterResources(operation.Reserve.Resources, func(res *mesos.Resource) bool { return res.Reservation != nil })
operation.Reserve.Resources = filtered
okOperations = append(okOperations, operation)
case mesos.Offer_Operation_UNRESERVE:
// Only send reserved resources
filtered := util.FilterResources(operation.Unreserve.Resources, func(res *mesos.Resource) bool { return res.Reservation != nil })
operation.Unreserve.Resources = filtered
okOperations = append(okOperations, operation)
case mesos.Offer_Operation_CREATE:
// Only send reserved resources disks with volumes
filtered := util.FilterResources(operation.Create.Volumes, func(res *mesos.Resource) bool {
return res.Reservation != nil && res.Disk != nil && res.GetName() == "disk"
})
operation.Create.Volumes = filtered
okOperations = append(okOperations, operation)
case mesos.Offer_Operation_DESTROY:
// Only send reserved resources disks with volumes
filtered := util.FilterResources(operation.Destroy.Volumes, func(res *mesos.Resource) bool {
return res.Reservation != nil && res.Disk != nil && res.GetName() == "disk"
})
operation.Destroy.Volumes = filtered
okOperations = append(okOperations, operation)
}
}
driver.cache.removeOffer(offerId) // if offer
}
// Accept Offers
message := &scheduler.Call{
FrameworkId: driver.frameworkInfo.Id,
Type: scheduler.Call_ACCEPT.Enum(),
Accept: &scheduler.Call_Accept{
OfferIds: offerIds,
Operations: okOperations,
Filters: filters,
},
}
if err := driver.send(driver.masterPid, message); err != nil {
for _, operation := range operations {
if *operation.Type == mesos.Offer_Operation_LAUNCH {
for _, task := range operation.Launch.TaskInfos {
driver.pushLostTask(task, "Unable to launch tasks: "+err.Error())
}
}
}
log.Errorf("Failed to send LaunchTask message: %v\n", err)
return driver.status, err
}
return driver.status, nil
}
func (driver *MesosSchedulerDriver) LaunchTasks(offerIds []*mesos.OfferID, tasks []*mesos.TaskInfo, filters *mesos.Filters) (mesos.Status, error) {
driver.eventLock.Lock()
defer driver.eventLock.Unlock()

View File

@ -0,0 +1,27 @@
Copyright (c) 2013, Patrick Mezard
All rights reserved.
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.
The names of its contributors may not 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
HOLDER 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.

View File

@ -0,0 +1,758 @@
// Package difflib is a partial port of Python difflib module.
//
// It provides tools to compare sequences of strings and generate textual diffs.
//
// The following class and functions have been ported:
//
// - SequenceMatcher
//
// - unified_diff
//
// - context_diff
//
// Getting unified diffs was the main goal of the port. Keep in mind this code
// is mostly suitable to output text differences in a human friendly way, there
// are no guarantees generated diffs are consumable by patch(1).
package difflib
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
)
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func calculateRatio(matches, length int) float64 {
if length > 0 {
return 2.0 * float64(matches) / float64(length)
}
return 1.0
}
type Match struct {
A int
B int
Size int
}
type OpCode struct {
Tag byte
I1 int
I2 int
J1 int
J2 int
}
// SequenceMatcher compares sequence of strings. The basic
// algorithm predates, and is a little fancier than, an algorithm
// published in the late 1980's by Ratcliff and Obershelp under the
// hyperbolic name "gestalt pattern matching". The basic idea is to find
// the longest contiguous matching subsequence that contains no "junk"
// elements (R-O doesn't address junk). The same idea is then applied
// recursively to the pieces of the sequences to the left and to the right
// of the matching subsequence. This does not yield minimal edit
// sequences, but does tend to yield matches that "look right" to people.
//
// SequenceMatcher tries to compute a "human-friendly diff" between two
// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the
// longest *contiguous* & junk-free matching subsequence. That's what
// catches peoples' eyes. The Windows(tm) windiff has another interesting
// notion, pairing up elements that appear uniquely in each sequence.
// That, and the method here, appear to yield more intuitive difference
// reports than does diff. This method appears to be the least vulnerable
// to synching up on blocks of "junk lines", though (like blank lines in
// ordinary text files, or maybe "<P>" lines in HTML files). That may be
// because this is the only method of the 3 that has a *concept* of
// "junk" <wink>.
//
// Timing: Basic R-O is cubic time worst case and quadratic time expected
// case. SequenceMatcher is quadratic time for the worst case and has
// expected-case behavior dependent in a complicated way on how many
// elements the sequences have in common; best case time is linear.
type SequenceMatcher struct {
a []string
b []string
b2j map[string][]int
IsJunk func(string) bool
autoJunk bool
bJunk map[string]struct{}
matchingBlocks []Match
fullBCount map[string]int
bPopular map[string]struct{}
opCodes []OpCode
}
func NewMatcher(a, b []string) *SequenceMatcher {
m := SequenceMatcher{autoJunk: true}
m.SetSeqs(a, b)
return &m
}
func NewMatcherWithJunk(a, b []string, autoJunk bool,
isJunk func(string) bool) *SequenceMatcher {
m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
m.SetSeqs(a, b)
return &m
}
// Set two sequences to be compared.
func (m *SequenceMatcher) SetSeqs(a, b []string) {
m.SetSeq1(a)
m.SetSeq2(b)
}
// Set the first sequence to be compared. The second sequence to be compared is
// not changed.
//
// SequenceMatcher computes and caches detailed information about the second
// sequence, so if you want to compare one sequence S against many sequences,
// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
// sequences.
//
// See also SetSeqs() and SetSeq2().
func (m *SequenceMatcher) SetSeq1(a []string) {
if &a == &m.a {
return
}
m.a = a
m.matchingBlocks = nil
m.opCodes = nil
}
// Set the second sequence to be compared. The first sequence to be compared is
// not changed.
func (m *SequenceMatcher) SetSeq2(b []string) {
if &b == &m.b {
return
}
m.b = b
m.matchingBlocks = nil
m.opCodes = nil
m.fullBCount = nil
m.chainB()
}
func (m *SequenceMatcher) chainB() {
// Populate line -> index mapping
b2j := map[string][]int{}
for i, s := range m.b {
indices := b2j[s]
indices = append(indices, i)
b2j[s] = indices
}
// Purge junk elements
m.bJunk = map[string]struct{}{}
if m.IsJunk != nil {
junk := m.bJunk
for s, _ := range b2j {
if m.IsJunk(s) {
junk[s] = struct{}{}
}
}
for s, _ := range junk {
delete(b2j, s)
}
}
// Purge remaining popular elements
popular := map[string]struct{}{}
n := len(m.b)
if m.autoJunk && n >= 200 {
ntest := n/100 + 1
for s, indices := range b2j {
if len(indices) > ntest {
popular[s] = struct{}{}
}
}
for s, _ := range popular {
delete(b2j, s)
}
}
m.bPopular = popular
m.b2j = b2j
}
func (m *SequenceMatcher) isBJunk(s string) bool {
_, ok := m.bJunk[s]
return ok
}
// Find longest matching block in a[alo:ahi] and b[blo:bhi].
//
// If IsJunk is not defined:
//
// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
// alo <= i <= i+k <= ahi
// blo <= j <= j+k <= bhi
// and for all (i',j',k') meeting those conditions,
// k >= k'
// i <= i'
// and if i == i', j <= j'
//
// In other words, of all maximal matching blocks, return one that
// starts earliest in a, and of all those maximal matching blocks that
// start earliest in a, return the one that starts earliest in b.
//
// If IsJunk is defined, first the longest matching block is
// determined as above, but with the additional restriction that no
// junk element appears in the block. Then that block is extended as
// far as possible by matching (only) junk elements on both sides. So
// the resulting block never matches on junk except as identical junk
// happens to be adjacent to an "interesting" match.
//
// If no blocks match, return (alo, blo, 0).
func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
// CAUTION: stripping common prefix or suffix would be incorrect.
// E.g.,
// ab
// acab
// Longest matching block is "ab", but if common prefix is
// stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
// strip, so ends up claiming that ab is changed to acab by
// inserting "ca" in the middle. That's minimal but unintuitive:
// "it's obvious" that someone inserted "ac" at the front.
// Windiff ends up at the same place as diff, but by pairing up
// the unique 'b's and then matching the first two 'a's.
besti, bestj, bestsize := alo, blo, 0
// find longest junk-free match
// during an iteration of the loop, j2len[j] = length of longest
// junk-free match ending with a[i-1] and b[j]
j2len := map[int]int{}
for i := alo; i != ahi; i++ {
// look at all instances of a[i] in b; note that because
// b2j has no junk keys, the loop is skipped if a[i] is junk
newj2len := map[int]int{}
for _, j := range m.b2j[m.a[i]] {
// a[i] matches b[j]
if j < blo {
continue
}
if j >= bhi {
break
}
k := j2len[j-1] + 1
newj2len[j] = k
if k > bestsize {
besti, bestj, bestsize = i-k+1, j-k+1, k
}
}
j2len = newj2len
}
// Extend the best by non-junk elements on each end. In particular,
// "popular" non-junk elements aren't in b2j, which greatly speeds
// the inner loop above, but also means "the best" match so far
// doesn't contain any junk *or* popular non-junk elements.
for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
m.a[besti-1] == m.b[bestj-1] {
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
}
for besti+bestsize < ahi && bestj+bestsize < bhi &&
!m.isBJunk(m.b[bestj+bestsize]) &&
m.a[besti+bestsize] == m.b[bestj+bestsize] {
bestsize += 1
}
// Now that we have a wholly interesting match (albeit possibly
// empty!), we may as well suck up the matching junk on each
// side of it too. Can't think of a good reason not to, and it
// saves post-processing the (possibly considerable) expense of
// figuring out what to do with it. In the case of an empty
// interesting match, this is clearly the right thing to do,
// because no other kind of match is possible in the regions.
for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
m.a[besti-1] == m.b[bestj-1] {
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
}
for besti+bestsize < ahi && bestj+bestsize < bhi &&
m.isBJunk(m.b[bestj+bestsize]) &&
m.a[besti+bestsize] == m.b[bestj+bestsize] {
bestsize += 1
}
return Match{A: besti, B: bestj, Size: bestsize}
}
// Return list of triples describing matching subsequences.
//
// Each triple is of the form (i, j, n), and means that
// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
// adjacent triples in the list, and the second is not the last triple in the
// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
// adjacent equal blocks.
//
// The last triple is a dummy, (len(a), len(b), 0), and is the only
// triple with n==0.
func (m *SequenceMatcher) GetMatchingBlocks() []Match {
if m.matchingBlocks != nil {
return m.matchingBlocks
}
var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
match := m.findLongestMatch(alo, ahi, blo, bhi)
i, j, k := match.A, match.B, match.Size
if match.Size > 0 {
if alo < i && blo < j {
matched = matchBlocks(alo, i, blo, j, matched)
}
matched = append(matched, match)
if i+k < ahi && j+k < bhi {
matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
}
}
return matched
}
matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
// It's possible that we have adjacent equal blocks in the
// matching_blocks list now.
nonAdjacent := []Match{}
i1, j1, k1 := 0, 0, 0
for _, b := range matched {
// Is this block adjacent to i1, j1, k1?
i2, j2, k2 := b.A, b.B, b.Size
if i1+k1 == i2 && j1+k1 == j2 {
// Yes, so collapse them -- this just increases the length of
// the first block by the length of the second, and the first
// block so lengthened remains the block to compare against.
k1 += k2
} else {
// Not adjacent. Remember the first block (k1==0 means it's
// the dummy we started with), and make the second block the
// new block to compare against.
if k1 > 0 {
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
}
i1, j1, k1 = i2, j2, k2
}
}
if k1 > 0 {
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
}
nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
m.matchingBlocks = nonAdjacent
return m.matchingBlocks
}
// Return list of 5-tuples describing how to turn a into b.
//
// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple
// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
// tuple preceding it, and likewise for j1 == the previous j2.
//
// The tags are characters, with these meanings:
//
// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2]
//
// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.
//
// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
//
// 'e' (equal): a[i1:i2] == b[j1:j2]
func (m *SequenceMatcher) GetOpCodes() []OpCode {
if m.opCodes != nil {
return m.opCodes
}
i, j := 0, 0
matching := m.GetMatchingBlocks()
opCodes := make([]OpCode, 0, len(matching))
for _, m := range matching {
// invariant: we've pumped out correct diffs to change
// a[:i] into b[:j], and the next matching block is
// a[ai:ai+size] == b[bj:bj+size]. So we need to pump
// out a diff to change a[i:ai] into b[j:bj], pump out
// the matching block, and move (i,j) beyond the match
ai, bj, size := m.A, m.B, m.Size
tag := byte(0)
if i < ai && j < bj {
tag = 'r'
} else if i < ai {
tag = 'd'
} else if j < bj {
tag = 'i'
}
if tag > 0 {
opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
}
i, j = ai+size, bj+size
// the list of matching blocks is terminated by a
// sentinel with size 0
if size > 0 {
opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
}
}
m.opCodes = opCodes
return m.opCodes
}
// Isolate change clusters by eliminating ranges with no changes.
//
// Return a generator of groups with up to n lines of context.
// Each group is in the same format as returned by GetOpCodes().
func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
if n < 0 {
n = 3
}
codes := m.GetOpCodes()
if len(codes) == 0 {
codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
}
// Fixup leading and trailing groups if they show no changes.
if codes[0].Tag == 'e' {
c := codes[0]
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
}
if codes[len(codes)-1].Tag == 'e' {
c := codes[len(codes)-1]
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
}
nn := n + n
groups := [][]OpCode{}
group := []OpCode{}
for _, c := range codes {
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
// End the current group and start a new one whenever
// there is a large range with no changes.
if c.Tag == 'e' && i2-i1 > nn {
group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
j1, min(j2, j1+n)})
groups = append(groups, group)
group = []OpCode{}
i1, j1 = max(i1, i2-n), max(j1, j2-n)
}
group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
}
if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
groups = append(groups, group)
}
return groups
}
// Return a measure of the sequences' similarity (float in [0,1]).
//
// Where T is the total number of elements in both sequences, and
// M is the number of matches, this is 2.0*M / T.
// Note that this is 1 if the sequences are identical, and 0 if
// they have nothing in common.
//
// .Ratio() is expensive to compute if you haven't already computed
// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
// want to try .QuickRatio() or .RealQuickRation() first to get an
// upper bound.
func (m *SequenceMatcher) Ratio() float64 {
matches := 0
for _, m := range m.GetMatchingBlocks() {
matches += m.Size
}
return calculateRatio(matches, len(m.a)+len(m.b))
}
// Return an upper bound on ratio() relatively quickly.
//
// This isn't defined beyond that it is an upper bound on .Ratio(), and
// is faster to compute.
func (m *SequenceMatcher) QuickRatio() float64 {
// viewing a and b as multisets, set matches to the cardinality
// of their intersection; this counts the number of matches
// without regard to order, so is clearly an upper bound
if m.fullBCount == nil {
m.fullBCount = map[string]int{}
for _, s := range m.b {
m.fullBCount[s] = m.fullBCount[s] + 1
}
}
// avail[x] is the number of times x appears in 'b' less the
// number of times we've seen it in 'a' so far ... kinda
avail := map[string]int{}
matches := 0
for _, s := range m.a {
n, ok := avail[s]
if !ok {
n = m.fullBCount[s]
}
avail[s] = n - 1
if n > 0 {
matches += 1
}
}
return calculateRatio(matches, len(m.a)+len(m.b))
}
// Return an upper bound on ratio() very quickly.
//
// This isn't defined beyond that it is an upper bound on .Ratio(), and
// is faster to compute than either .Ratio() or .QuickRatio().
func (m *SequenceMatcher) RealQuickRatio() float64 {
la, lb := len(m.a), len(m.b)
return calculateRatio(min(la, lb), la+lb)
}
// Convert range to the "ed" format
func formatRangeUnified(start, stop int) string {
// Per the diff spec at http://www.unix.org/single_unix_specification/
beginning := start + 1 // lines start numbering with one
length := stop - start
if length == 1 {
return fmt.Sprintf("%d", beginning)
}
if length == 0 {
beginning -= 1 // empty ranges begin at line just before the range
}
return fmt.Sprintf("%d,%d", beginning, length)
}
// Unified diff parameters
type UnifiedDiff struct {
A []string // First sequence lines
FromFile string // First file name
FromDate string // First file time
B []string // Second sequence lines
ToFile string // Second file name
ToDate string // Second file time
Eol string // Headers end of line, defaults to LF
Context int // Number of context lines
}
// Compare two sequences of lines; generate the delta as a unified diff.
//
// Unified diffs are a compact way of showing line changes and a few
// lines of context. The number of context lines is set by 'n' which
// defaults to three.
//
// By default, the diff control lines (those with ---, +++, or @@) are
// created with a trailing newline. This is helpful so that inputs
// created from file.readlines() result in diffs that are suitable for
// file.writelines() since both the inputs and outputs have trailing
// newlines.
//
// For inputs that do not have trailing newlines, set the lineterm
// argument to "" so that the output will be uniformly newline free.
//
// The unidiff format normally has a header for filenames and modification
// times. Any or all of these may be specified using strings for
// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
// The modification times are normally expressed in the ISO 8601 format.
func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
buf := bufio.NewWriter(writer)
defer buf.Flush()
w := func(format string, args ...interface{}) error {
_, err := buf.WriteString(fmt.Sprintf(format, args...))
return err
}
if len(diff.Eol) == 0 {
diff.Eol = "\n"
}
started := false
m := NewMatcher(diff.A, diff.B)
for _, g := range m.GetGroupedOpCodes(diff.Context) {
if !started {
started = true
fromDate := ""
if len(diff.FromDate) > 0 {
fromDate = "\t" + diff.FromDate
}
toDate := ""
if len(diff.ToDate) > 0 {
toDate = "\t" + diff.ToDate
}
err := w("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
if err != nil {
return err
}
err = w("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
if err != nil {
return err
}
}
first, last := g[0], g[len(g)-1]
range1 := formatRangeUnified(first.I1, last.I2)
range2 := formatRangeUnified(first.J1, last.J2)
if err := w("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
return err
}
for _, c := range g {
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
if c.Tag == 'e' {
for _, line := range diff.A[i1:i2] {
if err := w(" " + line); err != nil {
return err
}
}
continue
}
if c.Tag == 'r' || c.Tag == 'd' {
for _, line := range diff.A[i1:i2] {
if err := w("-" + line); err != nil {
return err
}
}
}
if c.Tag == 'r' || c.Tag == 'i' {
for _, line := range diff.B[j1:j2] {
if err := w("+" + line); err != nil {
return err
}
}
}
}
}
return nil
}
// Like WriteUnifiedDiff but returns the diff a string.
func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
w := &bytes.Buffer{}
err := WriteUnifiedDiff(w, diff)
return string(w.Bytes()), err
}
// Convert range to the "ed" format.
func formatRangeContext(start, stop int) string {
// Per the diff spec at http://www.unix.org/single_unix_specification/
beginning := start + 1 // lines start numbering with one
length := stop - start
if length == 0 {
beginning -= 1 // empty ranges begin at line just before the range
}
if length <= 1 {
return fmt.Sprintf("%d", beginning)
}
return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
}
type ContextDiff UnifiedDiff
// Compare two sequences of lines; generate the delta as a context diff.
//
// Context diffs are a compact way of showing line changes and a few
// lines of context. The number of context lines is set by diff.Context
// which defaults to three.
//
// By default, the diff control lines (those with *** or ---) are
// created with a trailing newline.
//
// For inputs that do not have trailing newlines, set the diff.Eol
// argument to "" so that the output will be uniformly newline free.
//
// The context diff format normally has a header for filenames and
// modification times. Any or all of these may be specified using
// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
// The modification times are normally expressed in the ISO 8601 format.
// If not specified, the strings default to blanks.
func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
buf := bufio.NewWriter(writer)
defer buf.Flush()
var diffErr error
w := func(format string, args ...interface{}) {
_, err := buf.WriteString(fmt.Sprintf(format, args...))
if diffErr == nil && err != nil {
diffErr = err
}
}
if len(diff.Eol) == 0 {
diff.Eol = "\n"
}
prefix := map[byte]string{
'i': "+ ",
'd': "- ",
'r': "! ",
'e': " ",
}
started := false
m := NewMatcher(diff.A, diff.B)
for _, g := range m.GetGroupedOpCodes(diff.Context) {
if !started {
started = true
fromDate := ""
if len(diff.FromDate) > 0 {
fromDate = "\t" + diff.FromDate
}
toDate := ""
if len(diff.ToDate) > 0 {
toDate = "\t" + diff.ToDate
}
w("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
w("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
}
first, last := g[0], g[len(g)-1]
w("***************" + diff.Eol)
range1 := formatRangeContext(first.I1, last.I2)
w("*** %s ****%s", range1, diff.Eol)
for _, c := range g {
if c.Tag == 'r' || c.Tag == 'd' {
for _, cc := range g {
if cc.Tag == 'i' {
continue
}
for _, line := range diff.A[cc.I1:cc.I2] {
w(prefix[cc.Tag] + line)
}
}
break
}
}
range2 := formatRangeContext(first.J1, last.J2)
w("--- %s ----%s", range2, diff.Eol)
for _, c := range g {
if c.Tag == 'r' || c.Tag == 'i' {
for _, cc := range g {
if cc.Tag == 'd' {
continue
}
for _, line := range diff.B[cc.J1:cc.J2] {
w(prefix[cc.Tag] + line)
}
}
break
}
}
}
return diffErr
}
// Like WriteContextDiff but returns the diff a string.
func GetContextDiffString(diff ContextDiff) (string, error) {
w := &bytes.Buffer{}
err := WriteContextDiff(w, diff)
return string(w.Bytes()), err
}
// Split a string on "\n" while preserving them. The output can be used
// as input for UnifiedDiff and ContextDiff structures.
func SplitLines(s string) []string {
lines := strings.SplitAfter(s, "\n")
lines[len(lines)-1] += "\n"
return lines
}

View File

@ -2,8 +2,21 @@ Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
Please consider promoting this project if you find it useful.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,22 @@
Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
Please consider promoting this project if you find it useful.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -3,6 +3,7 @@ package assert
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"math"
"reflect"
@ -12,6 +13,9 @@ import (
"time"
"unicode"
"unicode/utf8"
"github.com/davecgh/go-spew/spew"
"github.com/pmezard/go-difflib/difflib"
)
// TestingT is an interface wrapper around *testing.T
@ -35,11 +39,7 @@ func ObjectsAreEqual(expected, actual interface{}) bool {
return expected == actual
}
if reflect.DeepEqual(expected, actual) {
return true
}
return false
return reflect.DeepEqual(expected, actual)
}
@ -51,12 +51,13 @@ func ObjectsAreEqualValues(expected, actual interface{}) bool {
}
actualType := reflect.TypeOf(actual)
if actualType == nil {
return false
}
expectedValue := reflect.ValueOf(expected)
if expectedValue.Type().ConvertibleTo(actualType) {
if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
// Attempt comparison after type conversion
if reflect.DeepEqual(actual, expectedValue.Convert(actualType).Interface()) {
return true
}
return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual)
}
return false
@ -213,7 +214,7 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg
interfaceType := reflect.TypeOf(interfaceObject).Elem()
if !reflect.TypeOf(object).Implements(interfaceType) {
return Fail(t, fmt.Sprintf("Object must implement %v", interfaceType), msgAndArgs...)
return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...)
}
return true
@ -238,8 +239,9 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs
func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
if !ObjectsAreEqual(expected, actual) {
diff := diff(expected, actual)
return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+
" != %#v (actual)", expected, actual), msgAndArgs...)
" != %#v (actual)%s", expected, actual, diff), msgAndArgs...)
}
return true
@ -274,7 +276,7 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
bType := reflect.TypeOf(actual)
if aType != bType {
return Fail(t, "Types expected to match exactly", "%v != %v", aType, bType)
return Fail(t, fmt.Sprintf("Types expected to match exactly\n\r\t%v != %v", aType, bType), msgAndArgs...)
}
return Equal(t, expected, actual, msgAndArgs...)
@ -287,24 +289,10 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
//
// Returns whether the assertion was successful (true) or not (false).
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
success := true
if object == nil {
success = false
} else {
value := reflect.ValueOf(object)
kind := value.Kind()
if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
success = false
}
if !isNil(object) {
return true
}
if !success {
Fail(t, "Expected value not to be nil.", msgAndArgs...)
}
return success
return Fail(t, "Expected value not to be nil.", msgAndArgs...)
}
// isNil checks if a specified object is nil or not, without Failing.
@ -334,7 +322,7 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
}
var zeros = []interface{}{
var numericZeros = []interface{}{
int(0),
int8(0),
int16(0),
@ -360,7 +348,7 @@ func isEmpty(object interface{}) bool {
return true
}
for _, v := range zeros {
for _, v := range numericZeros {
if object == v {
return true
}
@ -377,6 +365,9 @@ func isEmpty(object interface{}) bool {
}
case reflect.Ptr:
{
if objValue.IsNil() {
return true
}
switch object.(type) {
case *time.Time:
return object.(*time.Time).IsZero()
@ -391,7 +382,7 @@ func isEmpty(object interface{}) bool {
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// assert.Empty(t, obj)
// assert.Empty(t, obj)
//
// Returns whether the assertion was successful (true) or not (false).
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
@ -408,9 +399,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// if assert.NotEmpty(t, obj) {
// assert.Equal(t, "two", obj[1])
// }
// if assert.NotEmpty(t, obj) {
// assert.Equal(t, "two", obj[1])
// }
//
// Returns whether the assertion was successful (true) or not (false).
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
@ -469,7 +460,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
}
// False asserts that the specified value is true.
// False asserts that the specified value is false.
//
// assert.False(t, myBool, "myBool should be false")
//
@ -492,7 +483,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
if ObjectsAreEqual(expected, actual) {
return Fail(t, "Should not be equal", msgAndArgs...)
return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...)
}
return true
@ -518,6 +509,16 @@ func includeElement(list interface{}, element interface{}) (ok, found bool) {
return true, strings.Contains(listValue.String(), elementValue.String())
}
if reflect.TypeOf(list).Kind() == reflect.Map {
mapKeys := listValue.MapKeys()
for i := 0; i < len(mapKeys); i++ {
if ObjectsAreEqual(mapKeys[i].Interface(), element) {
return true, true
}
}
return true, false
}
for i := 0; i < listValue.Len(); i++ {
if ObjectsAreEqual(listValue.Index(i).Interface(), element) {
return true, true
@ -527,11 +528,12 @@ func includeElement(list interface{}, element interface{}) (ok, found bool) {
}
// Contains asserts that the specified string or list(array, slice...) contains the
// Contains asserts that the specified string, list(array, slice...) or map contains the
// specified substring or element.
//
// assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'")
// assert.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'")
// assert.Contains(t, {"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'")
//
// Returns whether the assertion was successful (true) or not (false).
func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
@ -548,11 +550,12 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
}
// NotContains asserts that the specified string or list(array, slice...) does NOT contain the
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
// specified substring or element.
//
// assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
// assert.NotContains(t, ["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'")
// assert.NotContains(t, {"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'")
//
// Returns whether the assertion was successful (true) or not (false).
func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
@ -808,7 +811,7 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
return true
}
return Fail(t, fmt.Sprintf("No error is expected but got %v", err), msgAndArgs...)
return Fail(t, fmt.Sprintf("Received unexpected error %q", err), msgAndArgs...)
}
// Error asserts that a function returned an error (i.e. not `nil`).
@ -842,7 +845,7 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte
return false
}
s := "An error with value \"%s\" is expected but got \"%s\". %s"
return Equal(t, theError.Error(), errString,
return Equal(t, errString, theError.Error(),
s, errString, theError.Error(), message)
}
@ -893,3 +896,84 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf
return !match
}
// Zero asserts that i is the zero value for its type and returns the truth.
func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...)
}
return true
}
// NotZero asserts that i is not the zero value for its type and returns the truth.
func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...)
}
return true
}
// JSONEq asserts that two JSON strings are equivalent.
//
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
//
// Returns whether the assertion was successful (true) or not (false).
func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
var expectedJSONAsInterface, actualJSONAsInterface interface{}
if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil {
return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...)
}
if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil {
return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...)
}
return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...)
}
func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) {
t := reflect.TypeOf(v)
k := t.Kind()
if k == reflect.Ptr {
t = t.Elem()
k = t.Kind()
}
return t, k
}
// diff returns a diff of both values as long as both are of the same type and
// are a struct, map, slice or array. Otherwise it returns an empty string.
func diff(expected interface{}, actual interface{}) string {
if expected == nil || actual == nil {
return ""
}
et, ek := typeAndKind(expected)
at, _ := typeAndKind(actual)
if et != at {
return ""
}
if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array {
return ""
}
spew.Config.SortKeys = true
e := spew.Sdump(expected)
a := spew.Sdump(actual)
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(e),
B: difflib.SplitLines(a),
FromFile: "Expected",
FromDate: "",
ToFile: "Actual",
ToDate: "",
Context: 1,
})
return "\n\nDiff:\n" + diff
}

View File

@ -17,7 +17,7 @@
//
// }
//
// if you assert many times, use the below:
// if you assert many times, use the format below:
//
// import (
// "testing"
@ -42,113 +42,4 @@
//
// Every assertion function also takes an optional string message as the final argument,
// allowing custom error messages to be appended to the message the assertion method outputs.
//
// Here is an overview of the assert functions:
//
// assert.Equal(t, expected, actual [, message [, format-args]])
//
// assert.EqualValues(t, expected, actual [, message [, format-args]])
//
// assert.NotEqual(t, notExpected, actual [, message [, format-args]])
//
// assert.True(t, actualBool [, message [, format-args]])
//
// assert.False(t, actualBool [, message [, format-args]])
//
// assert.Nil(t, actualObject [, message [, format-args]])
//
// assert.NotNil(t, actualObject [, message [, format-args]])
//
// assert.Empty(t, actualObject [, message [, format-args]])
//
// assert.NotEmpty(t, actualObject [, message [, format-args]])
//
// assert.Len(t, actualObject, expectedLength, [, message [, format-args]])
//
// assert.Error(t, errorObject [, message [, format-args]])
//
// assert.NoError(t, errorObject [, message [, format-args]])
//
// assert.EqualError(t, theError, errString [, message [, format-args]])
//
// assert.Implements(t, (*MyInterface)(nil), new(MyObject) [,message [, format-args]])
//
// assert.IsType(t, expectedObject, actualObject [, message [, format-args]])
//
// assert.Contains(t, stringOrSlice, substringOrElement [, message [, format-args]])
//
// assert.NotContains(t, stringOrSlice, substringOrElement [, message [, format-args]])
//
// assert.Panics(t, func(){
//
// // call code that should panic
//
// } [, message [, format-args]])
//
// assert.NotPanics(t, func(){
//
// // call code that should not panic
//
// } [, message [, format-args]])
//
// assert.WithinDuration(t, timeA, timeB, deltaTime, [, message [, format-args]])
//
// assert.InDelta(t, numA, numB, delta, [, message [, format-args]])
//
// assert.InEpsilon(t, numA, numB, epsilon, [, message [, format-args]])
//
// assert package contains Assertions object. it has assertion methods.
//
// Here is an overview of the assert functions:
// assert.Equal(expected, actual [, message [, format-args]])
//
// assert.EqualValues(expected, actual [, message [, format-args]])
//
// assert.NotEqual(notExpected, actual [, message [, format-args]])
//
// assert.True(actualBool [, message [, format-args]])
//
// assert.False(actualBool [, message [, format-args]])
//
// assert.Nil(actualObject [, message [, format-args]])
//
// assert.NotNil(actualObject [, message [, format-args]])
//
// assert.Empty(actualObject [, message [, format-args]])
//
// assert.NotEmpty(actualObject [, message [, format-args]])
//
// assert.Len(actualObject, expectedLength, [, message [, format-args]])
//
// assert.Error(errorObject [, message [, format-args]])
//
// assert.NoError(errorObject [, message [, format-args]])
//
// assert.EqualError(theError, errString [, message [, format-args]])
//
// assert.Implements((*MyInterface)(nil), new(MyObject) [,message [, format-args]])
//
// assert.IsType(expectedObject, actualObject [, message [, format-args]])
//
// assert.Contains(stringOrSlice, substringOrElement [, message [, format-args]])
//
// assert.NotContains(stringOrSlice, substringOrElement [, message [, format-args]])
//
// assert.Panics(func(){
//
// // call code that should panic
//
// } [, message [, format-args]])
//
// assert.NotPanics(func(){
//
// // call code that should not panic
//
// } [, message [, format-args]])
//
// assert.WithinDuration(timeA, timeB, deltaTime, [, message [, format-args]])
//
// assert.InDelta(numA, numB, delta, [, message [, format-args]])
//
// assert.InEpsilon(numA, numB, epsilon, [, message [, format-args]])
package assert

View File

@ -81,7 +81,7 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or a
// slice with len == 0.
//
// assert.Empty(obj)
// assert.Empty(obj)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
@ -91,9 +91,9 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or a
// slice with len == 0.
//
// if assert.NotEmpty(obj) {
// assert.Equal("two", obj[1])
// }
// if assert.NotEmpty(obj) {
// assert.Equal("two", obj[1])
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool {
@ -119,7 +119,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
return True(a.t, value, msgAndArgs...)
}
// False asserts that the specified value is true.
// False asserts that the specified value is false.
//
// assert.False(myBool, "myBool should be false")
//
@ -263,3 +263,22 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter
func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
return NotRegexp(a.t, rx, str, msgAndArgs...)
}
// Zero asserts that i is the zero value for its type and returns the truth.
func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool {
return Zero(a.t, i, msgAndArgs...)
}
// NotZero asserts that i is not the zero value for its type and returns the truth.
func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool {
return NotZero(a.t, i, msgAndArgs...)
}
// JSONEq asserts that two JSON strings are equivalent.
//
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool {
return JSONEq(a.t, expected, actual, msgAndArgs...)
}

View File

@ -10,9 +10,9 @@ import (
// httpCode is a helper that returns HTTP code of the response. It returns -1
// if building a new request fails.
func httpCode(handler http.HandlerFunc, mode, url string, values url.Values) int {
func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int {
w := httptest.NewRecorder()
req, err := http.NewRequest(mode, url+"?"+values.Encode(), nil)
req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
if err != nil {
return -1
}
@ -25,8 +25,8 @@ func httpCode(handler http.HandlerFunc, mode, url string, values url.Values) int
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPSuccess(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values) bool {
code := httpCode(handler, mode, url, values)
func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
code := httpCode(handler, method, url, values)
if code == -1 {
return false
}
@ -38,8 +38,8 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, mode, url string, values
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPRedirect(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values) bool {
code := httpCode(handler, mode, url, values)
func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
code := httpCode(handler, method, url, values)
if code == -1 {
return false
}
@ -51,8 +51,8 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, mode, url string, values
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPError(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values) bool {
code := httpCode(handler, mode, url, values)
func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
code := httpCode(handler, method, url, values)
if code == -1 {
return false
}
@ -61,9 +61,9 @@ func HTTPError(t TestingT, handler http.HandlerFunc, mode, url string, values ur
// HTTPBody is a helper that returns HTTP body of the response. It returns
// empty string if building a new request fails.
func HTTPBody(handler http.HandlerFunc, mode, url string, values url.Values) string {
func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string {
w := httptest.NewRecorder()
req, err := http.NewRequest(mode, url+"?"+values.Encode(), nil)
req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
if err != nil {
return ""
}
@ -77,8 +77,8 @@ func HTTPBody(handler http.HandlerFunc, mode, url string, values url.Values) str
// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values, str interface{}) bool {
body := HTTPBody(handler, mode, url, values)
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
body := HTTPBody(handler, method, url, values)
contains := strings.Contains(body, fmt.Sprint(str))
if !contains {
@ -94,8 +94,8 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, mode, url string, va
// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values, str interface{}) bool {
body := HTTPBody(handler, mode, url, values)
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
body := HTTPBody(handler, method, url, values)
contains := strings.Contains(body, fmt.Sprint(str))
if contains {
@ -114,8 +114,8 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, mode, url string,
// assert.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, mode, url string, values url.Values) bool {
return HTTPSuccess(a.t, handler, mode, url, values)
func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method, url string, values url.Values) bool {
return HTTPSuccess(a.t, handler, method, url, values)
}
// HTTPRedirect asserts that a specified handler returns a redirect status code.
@ -123,8 +123,8 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, mode, url string, val
// assert.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, mode, url string, values url.Values) bool {
return HTTPRedirect(a.t, handler, mode, url, values)
func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method, url string, values url.Values) bool {
return HTTPRedirect(a.t, handler, method, url, values)
}
// HTTPError asserts that a specified handler returns an error status code.
@ -132,8 +132,8 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, mode, url string, va
// assert.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPError(handler http.HandlerFunc, mode, url string, values url.Values) bool {
return HTTPError(a.t, handler, mode, url, values)
func (a *Assertions) HTTPError(handler http.HandlerFunc, method, url string, values url.Values) bool {
return HTTPError(a.t, handler, method, url, values)
}
// HTTPBodyContains asserts that a specified handler returns a
@ -142,8 +142,8 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, mode, url string, value
// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, mode, url string, values url.Values, str interface{}) bool {
return HTTPBodyContains(a.t, handler, mode, url, values, str)
func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
return HTTPBodyContains(a.t, handler, method, url, values, str)
}
// HTTPBodyNotContains asserts that a specified handler returns a
@ -152,6 +152,6 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, mode, url string
// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, mode, url string, values url.Values, str interface{}) bool {
return HTTPBodyNotContains(a.t, handler, mode, url, values, str)
func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
return HTTPBodyNotContains(a.t, handler, method, url, values, str)
}

View File

@ -3,6 +3,7 @@ package mock
import (
"fmt"
"reflect"
"regexp"
"runtime"
"strings"
"sync"
@ -25,6 +26,7 @@ type TestingT interface {
// Call represents a method call and is used for setting expectations,
// as well as recording activity.
type Call struct {
Parent *Mock
// The name of the method that was or will be called.
Method string
@ -47,24 +49,113 @@ type Call struct {
// Holds a handler used to manipulate arguments content that are passed by
// reference. It's useful when mocking methods such as unmarshalers or
// decoders.
Run func(Arguments)
RunFn func(Arguments)
}
func newCall(parent *Mock, methodName string, methodArguments ...interface{}) *Call {
return &Call{
Parent: parent,
Method: methodName,
Arguments: methodArguments,
ReturnArguments: make([]interface{}, 0),
Repeatability: 0,
WaitFor: nil,
RunFn: nil,
}
}
func (self *Call) lock() {
self.Parent.mutex.Lock()
}
func (self *Call) unlock() {
self.Parent.mutex.Unlock()
}
func (self *Call) Return(returnArguments ...interface{}) *Call {
self.lock()
defer self.unlock()
self.ReturnArguments = returnArguments
return self
}
// Once indicates that that the mock should only return the value once.
//
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
func (self *Call) Once() *Call {
return self.Times(1)
}
// Twice indicates that that the mock should only return the value twice.
//
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
func (self *Call) Twice() *Call {
return self.Times(2)
}
// Times indicates that that the mock should only return the indicated number
// of times.
//
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
func (self *Call) Times(i int) *Call {
self.lock()
defer self.unlock()
self.Repeatability = i
return self
}
// WaitUntil sets the channel that will block the mock's return until its closed
// or a message is received.
//
// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second))
func (self *Call) WaitUntil(w <-chan time.Time) *Call {
self.lock()
defer self.unlock()
self.WaitFor = w
return self
}
// After sets how long to block until the call returns
//
// Mock.On("MyMethod", arg1, arg2).After(time.Second)
func (self *Call) After(d time.Duration) *Call {
return self.WaitUntil(time.After(d))
}
// Run sets a handler to be called before returning. It can be used when
// mocking a method such as unmarshalers that takes a pointer to a struct and
// sets properties in such struct
//
// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}").Return().Run(function(args Arguments) {
// arg := args.Get(0).(*map[string]interface{})
// arg["foo"] = "bar"
// })
func (self *Call) Run(fn func(Arguments)) *Call {
self.lock()
defer self.unlock()
self.RunFn = fn
return self
}
// On chains a new expectation description onto the mocked interface. This
// allows syntax like.
//
// Mock.
// On("MyMethod", 1).Return(nil).
// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))
func (self *Call) On(methodName string, arguments ...interface{}) *Call {
return self.Parent.On(methodName, arguments...)
}
// Mock is the workhorse used to track activity on another object.
// For an example of its usage, refer to the "Example Usage" section at the top of this document.
// For an example of its usage, refer to the "Example Usage" section at the top
// of this document.
type Mock struct {
// The method name that is currently
// being referred to by the On method.
onMethodName string
// An array of the arguments that are
// currently being referred to by the On method.
onMethodArguments Arguments
// Represents the calls that are expected of
// an object.
ExpectedCalls []Call
ExpectedCalls []*Call
// Holds the calls that were made to this mocked object.
Calls []Call
@ -95,103 +186,33 @@ func (m *Mock) TestData() objx.Map {
// being called.
//
// Mock.On("MyMethod", arg1, arg2)
func (m *Mock) On(methodName string, arguments ...interface{}) *Mock {
m.onMethodName = methodName
m.onMethodArguments = arguments
func (self *Mock) On(methodName string, arguments ...interface{}) *Call {
for _, arg := range arguments {
if v := reflect.ValueOf(arg); v.Kind() == reflect.Func {
panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg))
}
}
return m
self.mutex.Lock()
defer self.mutex.Unlock()
c := newCall(self, methodName, arguments...)
self.ExpectedCalls = append(self.ExpectedCalls, c)
return c
}
// Return finishes a description of an expectation of the method (and arguments)
// specified in the most recent On method call.
//
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2)
func (m *Mock) Return(returnArguments ...interface{}) *Mock {
m.mutex.Lock()
defer m.mutex.Unlock()
m.ExpectedCalls = append(m.ExpectedCalls, Call{m.onMethodName, m.onMethodArguments, returnArguments, 0, nil, nil})
return m
}
// Once indicates that that the mock should only return the value once.
//
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
func (m *Mock) Once() {
m.mutex.Lock()
m.ExpectedCalls[len(m.ExpectedCalls)-1].Repeatability = 1
m.mutex.Unlock()
}
// Twice indicates that that the mock should only return the value twice.
//
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
func (m *Mock) Twice() {
m.mutex.Lock()
m.ExpectedCalls[len(m.ExpectedCalls)-1].Repeatability = 2
m.mutex.Unlock()
}
// Times indicates that that the mock should only return the indicated number
// of times.
//
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
func (m *Mock) Times(i int) {
m.mutex.Lock()
m.ExpectedCalls[len(m.ExpectedCalls)-1].Repeatability = i
m.mutex.Unlock()
}
// WaitUntil sets the channel that will block the mock's return until its closed
// or a message is received.
//
// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second))
func (m *Mock) WaitUntil(w <-chan time.Time) *Mock {
m.mutex.Lock()
m.ExpectedCalls[len(m.ExpectedCalls)-1].WaitFor = w
m.mutex.Unlock()
return m
}
// After sets how long to block until the call returns
//
// Mock.On("MyMethod", arg1, arg2).After(time.Second)
func (m *Mock) After(d time.Duration) *Mock {
return m.WaitUntil(time.After(d))
}
// Run sets a handler to be called before returning. It can be used when
// mocking a method such as unmarshalers that takes a pointer to a struct and
// sets properties in such struct
//
// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}").Return().Run(function(args Arguments) {
// arg := args.Get(0).(*map[string]interface{})
// arg["foo"] = "bar"
// })
func (m *Mock) Run(fn func(Arguments)) *Mock {
m.mutex.Lock()
m.ExpectedCalls[len(m.ExpectedCalls)-1].Run = fn
m.mutex.Unlock()
return m
}
/*
Recording and responding to activity
*/
// /*
// Recording and responding to activity
// */
func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) {
for i, call := range m.expectedCalls() {
m.mutex.Lock()
defer m.mutex.Unlock()
for i, call := range m.ExpectedCalls {
if call.Method == method && call.Repeatability > -1 {
_, diffCount := call.Arguments.Diff(arguments)
if diffCount == 0 {
return i, &call
return i, call
}
}
@ -209,7 +230,7 @@ func (m *Mock) findClosestCall(method string, arguments ...interface{}) (bool, *
_, tempDiffCount := call.Arguments.Diff(arguments)
if tempDiffCount < diffCount || diffCount == 0 {
diffCount = tempDiffCount
closestCall = &call
closestCall = call
}
}
@ -228,7 +249,7 @@ func callString(method string, arguments Arguments, includeArgumentValues bool)
if includeArgumentValues {
var argVals []string
for argIndex, arg := range arguments {
argVals = append(argVals, fmt.Sprintf("%d: %v", argIndex, arg))
argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg))
}
argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t"))
}
@ -247,6 +268,14 @@ func (m *Mock) Called(arguments ...interface{}) Arguments {
panic("Couldn't get the caller information")
}
functionPath := runtime.FuncForPC(pc).Name()
//Next four lines are required to use GCCGO function naming conventions.
//For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock
//uses inteface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree
//With GCCGO we need to remove interface information starting from pN<dd>.
re := regexp.MustCompile("\\.pN\\d+_")
if re.MatchString(functionPath) {
functionPath = re.Split(functionPath, -1)[0]
}
parts := strings.Split(functionPath, ".")
functionName := parts[len(parts)-1]
@ -272,17 +301,16 @@ func (m *Mock) Called(arguments ...interface{}) Arguments {
switch {
case call.Repeatability == 1:
call.Repeatability = -1
m.ExpectedCalls[found] = *call
case call.Repeatability > 1:
call.Repeatability -= 1
m.ExpectedCalls[found] = *call
}
m.mutex.Unlock()
}
// add the call
m.mutex.Lock()
m.Calls = append(m.Calls, Call{functionName, arguments, make([]interface{}, 0), 0, nil, nil})
m.Calls = append(m.Calls, *newCall(m, functionName, arguments...))
m.mutex.Unlock()
// block if specified
@ -290,12 +318,11 @@ func (m *Mock) Called(arguments ...interface{}) Arguments {
<-call.WaitFor
}
if call.Run != nil {
call.Run(arguments)
if call.RunFn != nil {
call.RunFn(arguments)
}
return call.ReturnArguments
}
/*
@ -324,16 +351,19 @@ func (m *Mock) AssertExpectations(t TestingT) bool {
// iterate through each expectation
expectedCalls := m.expectedCalls()
for _, expectedCall := range expectedCalls {
switch {
case !m.methodWasCalled(expectedCall.Method, expectedCall.Arguments):
if !m.methodWasCalled(expectedCall.Method, expectedCall.Arguments) {
somethingMissing = true
failedExpectations++
t.Logf("\u274C\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String())
case expectedCall.Repeatability > 0:
somethingMissing = true
failedExpectations++
default:
t.Logf("\u2705\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String())
} else {
m.mutex.Lock()
if expectedCall.Repeatability > 0 {
somethingMissing = true
failedExpectations++
} else {
t.Logf("\u2705\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String())
}
m.mutex.Unlock()
}
}
@ -352,7 +382,7 @@ func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls
actualCalls++
}
}
return assert.Equal(t, actualCalls, expectedCalls, fmt.Sprintf("Expected number of calls (%d) does not match the actual number of calls (%d).", expectedCalls, actualCalls))
return assert.Equal(t, expectedCalls, actualCalls, fmt.Sprintf("Expected number of calls (%d) does not match the actual number of calls (%d).", expectedCalls, actualCalls))
}
// AssertCalled asserts that the method was called.
@ -390,10 +420,10 @@ func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool {
return false
}
func (m *Mock) expectedCalls() []Call {
func (m *Mock) expectedCalls() []*Call {
m.mutex.Lock()
defer m.mutex.Unlock()
return append([]Call{}, m.ExpectedCalls...)
return append([]*Call{}, m.ExpectedCalls...)
}
func (m *Mock) calls() []Call {

View File

@ -24,54 +24,4 @@
//
// Every assertion function also takes an optional string message as the final argument,
// allowing custom error messages to be appended to the message the assertion method outputs.
//
// Here is an overview of the assert functions:
//
// require.Equal(t, expected, actual [, message [, format-args])
//
// require.NotEqual(t, notExpected, actual [, message [, format-args]])
//
// require.True(t, actualBool [, message [, format-args]])
//
// require.False(t, actualBool [, message [, format-args]])
//
// require.Nil(t, actualObject [, message [, format-args]])
//
// require.NotNil(t, actualObject [, message [, format-args]])
//
// require.Empty(t, actualObject [, message [, format-args]])
//
// require.NotEmpty(t, actualObject [, message [, format-args]])
//
// require.Error(t, errorObject [, message [, format-args]])
//
// require.NoError(t, errorObject [, message [, format-args]])
//
// require.EqualError(t, theError, errString [, message [, format-args]])
//
// require.Implements(t, (*MyInterface)(nil), new(MyObject) [,message [, format-args]])
//
// require.IsType(t, expectedObject, actualObject [, message [, format-args]])
//
// require.Contains(t, string, substring [, message [, format-args]])
//
// require.NotContains(t, string, substring [, message [, format-args]])
//
// require.Panics(t, func(){
//
// // call code that should panic
//
// } [, message [, format-args]])
//
// require.NotPanics(t, func(){
//
// // call code that should not panic
//
// } [, message [, format-args]])
//
// require.WithinDuration(t, timeA, timeB, deltaTime, [, message [, format-args]])
//
// require.InDelta(t, numA, numB, delta, [, message [, format-args]])
//
// require.InEpsilon(t, numA, numB, epsilon, [, message [, format-args]])
package require

View File

@ -93,7 +93,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) {
True(a.t, value, msgAndArgs...)
}
// False asserts that the specified value is true.
// False asserts that the specified value is false.
//
// require.False(myBool, "myBool should be false")
func (a *Assertions) False(value bool, msgAndArgs ...interface{}) {
@ -209,3 +209,22 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter
func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) {
NotRegexp(a.t, rx, str, msgAndArgs...)
}
// Zero asserts that i is the zero value for its type and returns the truth.
func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) {
Zero(a.t, i, msgAndArgs...)
}
// NotZero asserts that i is not the zero value for its type and returns the truth.
func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) {
NotZero(a.t, i, msgAndArgs...)
}
// JSONEq asserts that two JSON strings are equivalent.
//
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) {
JSONEq(a.t, expected, actual, msgAndArgs...)
}

View File

@ -1,6 +1,7 @@
package require
import (
"encoding/json"
"time"
"github.com/stretchr/testify/assert"
@ -118,7 +119,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) {
}
}
// False asserts that the specified value is true.
// False asserts that the specified value is false.
//
// require.False(t, myBool, "myBool should be false")
func False(t TestingT, value bool, msgAndArgs ...interface{}) {
@ -136,9 +137,12 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{
}
}
// Contains asserts that the specified string contains the specified substring.
// Contains asserts that the specified string, list(array, slice...) or map contains the
// specified substring or element.
//
// require.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'")
// require.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'")
// require.Contains(t, {"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'")
func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) {
if !assert.Contains(t, s, contains, msgAndArgs...) {
t.FailNow()
@ -228,6 +232,25 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf
}
}
// JSONEq asserts that two JSON strings are equivalent.
//
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
//
// Returns whether the assertion was successful (true) or not (false).
func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) {
var expectedJSONAsInterface, actualJSONAsInterface interface{}
if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil {
t.FailNow()
}
if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil {
t.FailNow()
}
Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...)
}
/*
Errors
*/
@ -269,3 +292,17 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte
t.FailNow()
}
}
// Zero asserts that i is the zero value for its type and returns the truth.
func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) {
if !assert.Zero(t, i, msgAndArgs...) {
t.FailNow()
}
}
// NotZero asserts that i is not the zero value for its type and returns the truth.
func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) {
if !assert.NotZero(t, i, msgAndArgs...) {
t.FailNow()
}
}