Commit 54439b5e authored by James DeFelice's avatar James DeFelice

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

parent 06996d5a
...@@ -716,43 +716,43 @@ ...@@ -716,43 +716,43 @@
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/auth", "ImportPath": "github.com/mesos/mesos-go/auth",
"Comment": "v0.0.2-5-ged907b1", "Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11" "Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/detector", "ImportPath": "github.com/mesos/mesos-go/detector",
"Comment": "v0.0.2-5-ged907b1", "Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11" "Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/executor", "ImportPath": "github.com/mesos/mesos-go/executor",
"Comment": "v0.0.2-5-ged907b1", "Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11" "Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/mesosproto", "ImportPath": "github.com/mesos/mesos-go/mesosproto",
"Comment": "v0.0.2-5-ged907b1", "Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11" "Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/mesosutil", "ImportPath": "github.com/mesos/mesos-go/mesosutil",
"Comment": "v0.0.2-5-ged907b1", "Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11" "Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/messenger", "ImportPath": "github.com/mesos/mesos-go/messenger",
"Comment": "v0.0.2-5-ged907b1", "Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11" "Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/scheduler", "ImportPath": "github.com/mesos/mesos-go/scheduler",
"Comment": "v0.0.2-5-ged907b1", "Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11" "Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
}, },
{ {
"ImportPath": "github.com/mesos/mesos-go/upid", "ImportPath": "github.com/mesos/mesos-go/upid",
"Comment": "v0.0.2-5-ged907b1", "Comment": "before-0.26-protos-14-g4a7554a",
"Rev": "ed907b10717e66325cf2894eb90a0553a89fcb11" "Rev": "4a7554aad396c70d19c9fc3469980547c9f117ae"
}, },
{ {
"ImportPath": "github.com/miekg/dns", "ImportPath": "github.com/miekg/dns",
...@@ -791,6 +791,10 @@ ...@@ -791,6 +791,10 @@
"Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4" "Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4"
}, },
{ {
"ImportPath": "github.com/pmezard/go-difflib/difflib",
"Rev": "d8ed2627bdf02c080bf22230dbb337003b7aba2d"
},
{
"ImportPath": "github.com/prometheus/client_golang/prometheus", "ImportPath": "github.com/prometheus/client_golang/prometheus",
"Comment": "0.7.0-39-g3b78d7a", "Comment": "0.7.0-39-g3b78d7a",
"Rev": "3b78d7a77f51ccbc364d4bc170920153022cfd08" "Rev": "3b78d7a77f51ccbc364d4bc170920153022cfd08"
...@@ -861,18 +865,18 @@ ...@@ -861,18 +865,18 @@
}, },
{ {
"ImportPath": "github.com/stretchr/testify/assert", "ImportPath": "github.com/stretchr/testify/assert",
"Comment": "v1.0-17-g089c718", "Comment": "v1.0-88-ge3a8ff8",
"Rev": "089c7181b8c728499929ff09b62d3fdd8df8adff" "Rev": "e3a8ff8ce36581f87a15341206f205b1da467059"
}, },
{ {
"ImportPath": "github.com/stretchr/testify/mock", "ImportPath": "github.com/stretchr/testify/mock",
"Comment": "v1.0-17-g089c718", "Comment": "v1.0-88-ge3a8ff8",
"Rev": "089c7181b8c728499929ff09b62d3fdd8df8adff" "Rev": "e3a8ff8ce36581f87a15341206f205b1da467059"
}, },
{ {
"ImportPath": "github.com/stretchr/testify/require", "ImportPath": "github.com/stretchr/testify/require",
"Comment": "v1.0-17-g089c718", "Comment": "v1.0-88-ge3a8ff8",
"Rev": "089c7181b8c728499929ff09b62d3fdd8df8adff" "Rev": "e3a8ff8ce36581f87a15341206f205b1da467059"
}, },
{ {
"ImportPath": "github.com/syndtr/gocapability/capability", "ImportPath": "github.com/syndtr/gocapability/capability",
......
...@@ -67,6 +67,7 @@ github.com/onsi/ginkgo | spdxMIT ...@@ -67,6 +67,7 @@ github.com/onsi/ginkgo | spdxMIT
github.com/onsi/gomega | spdxMIT github.com/onsi/gomega | spdxMIT
github.com/opencontainers/runc | Apache-2 github.com/opencontainers/runc | Apache-2
github.com/pborman/uuid | spdxBSD3 github.com/pborman/uuid | spdxBSD3
github.com/pmezard/go-difflib | BSD3
github.com/prometheus/client_golang | Apache-2 github.com/prometheus/client_golang | Apache-2
github.com/prometheus/client_model | Apache-2 github.com/prometheus/client_model | Apache-2
github.com/prometheus/common/expfmt | Apache-2 github.com/prometheus/common/expfmt | Apache-2
...@@ -84,7 +85,7 @@ github.com/spf13/cobra | Apache-2 ...@@ -84,7 +85,7 @@ github.com/spf13/cobra | Apache-2
github.com/spf13/pflag | spdxBSD3 github.com/spf13/pflag | spdxBSD3
github.com/ssoroka/ttime | UNKNOWN github.com/ssoroka/ttime | UNKNOWN
github.com/stretchr/objx | MIT? github.com/stretchr/objx | MIT?
github.com/stretchr/testify | MIT? github.com/stretchr/testify | spdxMIT
github.com/syndtr/gocapability | spdxBSD2 github.com/syndtr/gocapability | spdxBSD2
github.com/ugorji/go | MITname github.com/ugorji/go | MITname
github.com/vishvananda/netlink | Apache-2 github.com/vishvananda/netlink | Apache-2
......
...@@ -107,11 +107,11 @@ func (s *Standalone) Cancel() { ...@@ -107,11 +107,11 @@ func (s *Standalone) Cancel() {
s.cancelOnce.Do(func() { close(s.done) }) 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. // 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) 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) { func (s *Standalone) _poller(pf fetcherFunc) {
defer func() { defer func() {
...@@ -193,7 +193,7 @@ func (s *Standalone) _poller(pf fetcherFunc) { ...@@ -193,7 +193,7 @@ func (s *Standalone) _poller(pf fetcherFunc) {
// assumes that address is in host:port format // assumes that address is in host:port format
func (s *Standalone) _fetchPid(ctx context.Context, address string) (*upid.UPID, error) { func (s *Standalone) _fetchPid(ctx context.Context, address string) (*upid.UPID, error) {
//TODO(jdef) need SSL support //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) req, err := http.NewRequest("GET", uri, nil)
if err != nil { if err != nil {
return nil, err return nil, err
......
all: *.proto PROTO_PATH := ${GOPATH}/src:.
protoc --proto_path=${GOPATH}/src:${GOPATH}/src/github.com/gogo/protobuf/protobuf:. --gogo_out=. *.proto 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
/**
* 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;
}
/**
* 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;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
* 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;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
* 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;
}
...@@ -16,11 +16,12 @@ ...@@ -16,11 +16,12 @@
* limitations under the License. * 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"; import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option go_package = "scheduler";
option (gogoproto.gostring_all) = true; option (gogoproto.gostring_all) = true;
option (gogoproto.equal_all) = true; option (gogoproto.equal_all) = true;
option (gogoproto.verbose_equal_all) = true; option (gogoproto.verbose_equal_all) = true;
...@@ -68,17 +69,19 @@ message Event { ...@@ -68,17 +69,19 @@ message Event {
// This value will be set if the master is sending heartbeats. See // This value will be set if the master is sending heartbeats. See
// the comment above on 'HEARTBEAT' for more details. // 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; optional double heartbeat_interval_seconds = 2;
} }
// Received whenever there are new resources that are offered to the // Received whenever there are new resources that are offered to the
// scheduler. Each offer corresponds to a set of resources on a // scheduler or resources requested back from the scheduler. Each
// slave. Until the scheduler accepts or declines an offer the // offer corresponds to a set of resources on a slave. Until the
// resources are considered allocated to the scheduler. // 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 { message Offers {
repeated Offer offers = 1; repeated Offer offers = 1;
repeated InverseOffer inverse_offers = 2;
} }
// Received when a particular offer is no longer valid (e.g., the // Received when a particular offer is no longer valid (e.g., the
...@@ -179,6 +182,7 @@ message Call { ...@@ -179,6 +182,7 @@ message Call {
RECONCILE = 9; // See 'Reconcile' below. RECONCILE = 9; // See 'Reconcile' below.
MESSAGE = 10; // See 'Message' below. MESSAGE = 10; // See 'Message' below.
REQUEST = 11; // See 'Request' 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 // TODO(benh): Consider adding an 'ACTIVATE' and 'DEACTIVATE' for
// already subscribed frameworks as a way of stopping offers from // already subscribed frameworks as a way of stopping offers from
......
/**
* 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;
}
...@@ -36,6 +36,28 @@ func FilterResources(resources []*mesos.Resource, filter func(*mesos.Resource) b ...@@ -36,6 +36,28 @@ func FilterResources(resources []*mesos.Resource, filter func(*mesos.Resource) b
return result 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 { func NewScalarResource(name string, val float64) *mesos.Resource {
return &mesos.Resource{ return &mesos.Resource{
Name: proto.String(name), Name: proto.String(name),
...@@ -58,7 +80,15 @@ func NewSetResource(name string, items []string) *mesos.Resource { ...@@ -58,7 +80,15 @@ func NewSetResource(name string, items []string) *mesos.Resource {
Type: mesos.Value_SET.Enum(), Type: mesos.Value_SET.Enum(),
Set: &mesos.Value_Set{Item: items}, 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 { func NewFrameworkID(id string) *mesos.FrameworkID {
...@@ -153,3 +183,38 @@ func NewExecutorInfo(execId *mesos.ExecutorID, command *mesos.CommandInfo) *meso ...@@ -153,3 +183,38 @@ func NewExecutorInfo(execId *mesos.ExecutorID, command *mesos.CommandInfo) *meso
Command: command, 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},
}
}
package mesosutil package mesosutil
import ( import (
"os"
"os/exec" "os/exec"
"strings" "strings"
...@@ -9,15 +10,20 @@ import ( ...@@ -9,15 +10,20 @@ import (
//TODO(jdef) copied from kubernetes/pkg/util/node.go //TODO(jdef) copied from kubernetes/pkg/util/node.go
func GetHostname(hostnameOverride string) string { func GetHostname(hostnameOverride string) string {
hostname := []byte(hostnameOverride) hostname := hostnameOverride
if string(hostname) == "" { if hostname == "" {
// Note: We use exec here instead of os.Hostname() because we // Note: We use exec here instead of os.Hostname() because we
// want the FQDN, and this is the easiest way to get it. // want the FQDN, and this is the easiest way to get it.
fqdn, err := exec.Command("hostname", "-f").Output() fqdn, err := exec.Command("hostname", "-f").Output()
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 { if err != nil {
log.Fatalf("Couldn't determine hostname: %v", err) log.Fatalf("Error getting hostname: %v", err)
} }
hostname = fqdn } else {
hostname = string(fqdn)
} }
return strings.TrimSpace(string(hostname)) }
return strings.TrimSpace(hostname)
} }
...@@ -209,7 +209,7 @@ func (d *httpDecoder) buildResponseEntity(resp *Response) *bytes.Buffer { ...@@ -209,7 +209,7 @@ func (d *httpDecoder) buildResponseEntity(resp *Response) *bytes.Buffer {
// updateForRequest updates the chunked and kalive fields of the decoder to align // updateForRequest updates the chunked and kalive fields of the decoder to align
// with the header values of the request // with the header values of the request
func (d *httpDecoder) updateForRequest() { func (d *httpDecoder) updateForRequest(bootstrapping bool) {
// check "Transfer-Encoding" for "chunked" // check "Transfer-Encoding" for "chunked"
d.chunked = false d.chunked = false
for _, v := range d.req.Header["Transfer-Encoding"] { for _, v := range d.req.Header["Transfer-Encoding"] {
...@@ -219,11 +219,16 @@ func (d *httpDecoder) updateForRequest() { ...@@ -219,11 +219,16 @@ func (d *httpDecoder) updateForRequest() {
} }
} }
if !d.chunked && d.req.ContentLength < 0 { if !d.chunked && d.req.ContentLength < 0 {
if bootstrapping {
// strongly suspect that Go's internal net/http lib is stripping // strongly suspect that Go's internal net/http lib is stripping
// the Transfer-Encoding header from the initial request, so this // the Transfer-Encoding header from the initial request, so this
// workaround makes a very mesos-specific assumption: an unknown // workaround makes a very mesos-specific assumption: an unknown
// Content-Length indicates a chunked stream. // Content-Length indicates a chunked stream.
d.chunked = true d.chunked = true
} else {
// via https://tools.ietf.org/html/rfc7230#section-3.3.2
d.req.ContentLength = 0
}
} }
// check "Connection" for "Keep-Alive" // check "Connection" for "Keep-Alive"
...@@ -351,7 +356,7 @@ func limit(r *bufio.Reader, limit int64) *io.LimitedReader { ...@@ -351,7 +356,7 @@ func limit(r *bufio.Reader, limit int64) *io.LimitedReader {
func (d *httpDecoder) bootstrapState(res http.ResponseWriter) httpState { func (d *httpDecoder) bootstrapState(res http.ResponseWriter) httpState {
log.V(2).Infoln(d.idtag + "bootstrap-state") log.V(2).Infoln(d.idtag + "bootstrap-state")
d.updateForRequest() d.updateForRequest(true)
// hijack // hijack
hj, ok := res.(http.Hijacker) hj, ok := res.(http.Hijacker)
...@@ -636,6 +641,6 @@ func readHeaderState(d *httpDecoder) httpState { ...@@ -636,6 +641,6 @@ func readHeaderState(d *httpDecoder) httpState {
log.V(2).Infoln(d.idtag+"set content length", r.ContentLength) log.V(2).Infoln(d.idtag+"set content length", r.ContentLength)
} }
} }
d.updateForRequest() d.updateForRequest(false)
return d.readBodyContent() return d.readBodyContent()
} }
...@@ -590,9 +590,11 @@ func (t *HTTPTransporter) makeLibprocessRequest(msg *Message) (*http.Request, er ...@@ -590,9 +590,11 @@ func (t *HTTPTransporter) makeLibprocessRequest(msg *Message) (*http.Request, er
log.Errorf("Failed to create request: %v\n", err) log.Errorf("Failed to create request: %v\n", err)
return nil, err return nil, err
} }
if !msg.isV1API() {
req.Header.Add("Libprocess-From", t.upid.String()) req.Header.Add("Libprocess-From", t.upid.String())
req.Header.Add("Content-Type", "application/x-protobuf")
req.Header.Add("Connection", "Keep-Alive") req.Header.Add("Connection", "Keep-Alive")
}
req.Header.Add("Content-Type", "application/x-protobuf")
return req, nil return req, nil
} }
......
...@@ -36,9 +36,17 @@ type Message struct { ...@@ -36,9 +36,17 @@ type Message struct {
// RequestURI returns the request URI of the message. // RequestURI returns the request URI of the message.
func (m *Message) RequestURI() string { 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) 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. // NOTE: This should not fail or panic.
func extractNameFromRequestURI(requestURI string) string { func extractNameFromRequestURI(requestURI string) string {
return strings.Split(requestURI, "/")[2] return strings.Split(requestURI, "/")[2]
......
...@@ -28,6 +28,7 @@ import ( ...@@ -28,6 +28,7 @@ import (
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
log "github.com/golang/glog" log "github.com/golang/glog"
mesos "github.com/mesos/mesos-go/mesosproto" 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/mesosutil/process"
"github.com/mesos/mesos-go/upid" "github.com/mesos/mesos-go/upid"
"golang.org/x/net/context" "golang.org/x/net/context"
...@@ -391,5 +392,14 @@ func (m *MesosMessenger) decodeLoop() { ...@@ -391,5 +392,14 @@ func (m *MesosMessenger) decodeLoop() {
// getMessageName returns the name of the message in the mesos manner. // getMessageName returns the name of the message in the mesos manner.
func getMessageName(msg proto.Message) string { 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
} }
...@@ -18,7 +18,7 @@ func (h *CredentialHandler) Handle(callbacks ...callback.Interface) error { ...@@ -18,7 +18,7 @@ func (h *CredentialHandler) Handle(callbacks ...callback.Interface) error {
case *callback.Name: case *callback.Name:
cb.Set(h.credential.GetPrincipal()) cb.Set(h.credential.GetPrincipal())
case *callback.Password: case *callback.Password:
cb.Set(h.credential.GetSecret()) cb.Set(([]byte)(h.credential.GetSecret()))
case *callback.Interprocess: case *callback.Interprocess:
cb.Set(*(h.pid), *(h.client)) cb.Set(*(h.pid), *(h.client))
default: default:
......
...@@ -66,6 +66,11 @@ type SchedulerDriver interface { ...@@ -66,6 +66,11 @@ type SchedulerDriver interface {
// framework via Scheduler.ResourceOffers callback, asynchronously. // framework via Scheduler.ResourceOffers callback, asynchronously.
RequestResources(requests []*mesos.Request) (mesos.Status, error) 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., // Launches the given set of tasks. Any resources remaining (i.e.,
// not used by the tasks or their executors) will be considered // not used by the tasks or their executors) will be considered
// declined. The specified filters are applied on all unused // declined. The specified filters are applied on all unused
......
...@@ -34,6 +34,7 @@ import ( ...@@ -34,6 +34,7 @@ import (
"github.com/mesos/mesos-go/auth" "github.com/mesos/mesos-go/auth"
"github.com/mesos/mesos-go/detector" "github.com/mesos/mesos-go/detector"
mesos "github.com/mesos/mesos-go/mesosproto" mesos "github.com/mesos/mesos-go/mesosproto"
"github.com/mesos/mesos-go/mesosproto/scheduler"
util "github.com/mesos/mesos-go/mesosutil" util "github.com/mesos/mesos-go/mesosutil"
"github.com/mesos/mesos-go/mesosutil/process" "github.com/mesos/mesos-go/mesosutil/process"
"github.com/mesos/mesos-go/messenger" "github.com/mesos/mesos-go/messenger"
...@@ -1083,7 +1084,7 @@ func (driver *MesosSchedulerDriver) abort(cause error) (stat mesos.Status, err e ...@@ -1083,7 +1084,7 @@ func (driver *MesosSchedulerDriver) abort(cause error) (stat mesos.Status, err e
if driver.connected { if driver.connected {
_, err = driver.stop(cause, true) _, err = driver.stop(cause, true)
} else { } else {
driver.messenger.Stop() driver._stop(cause, mesos.Status_DRIVER_ABORTED)
} }
stat = mesos.Status_DRIVER_ABORTED stat = mesos.Status_DRIVER_ABORTED
...@@ -1091,6 +1092,117 @@ func (driver *MesosSchedulerDriver) abort(cause error) (stat mesos.Status, err e ...@@ -1091,6 +1092,117 @@ func (driver *MesosSchedulerDriver) abort(cause error) (stat mesos.Status, err e
return 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) { func (driver *MesosSchedulerDriver) LaunchTasks(offerIds []*mesos.OfferID, tasks []*mesos.TaskInfo, filters *mesos.Filters) (mesos.Status, error) {
driver.eventLock.Lock() driver.eventLock.Lock()
defer driver.eventLock.Unlock() defer driver.eventLock.Unlock()
......
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.
...@@ -2,8 +2,21 @@ Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell ...@@ -2,8 +2,21 @@ Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
Please consider promoting this project if you find it useful. 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,
\ No newline at end of file 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.
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.
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
// //
// } // }
// //
// if you assert many times, use the below: // if you assert many times, use the format below:
// //
// import ( // import (
// "testing" // "testing"
...@@ -42,113 +42,4 @@ ...@@ -42,113 +42,4 @@
// //
// Every assertion function also takes an optional string message as the final argument, // 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. // 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 package assert
...@@ -119,7 +119,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { ...@@ -119,7 +119,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
return True(a.t, value, msgAndArgs...) 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") // assert.False(myBool, "myBool should be false")
// //
...@@ -263,3 +263,22 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter ...@@ -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 { func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
return NotRegexp(a.t, rx, str, msgAndArgs...) 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...)
}
...@@ -10,9 +10,9 @@ import ( ...@@ -10,9 +10,9 @@ import (
// httpCode is a helper that returns HTTP code of the response. It returns -1 // httpCode is a helper that returns HTTP code of the response. It returns -1
// if building a new request fails. // 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() w := httptest.NewRecorder()
req, err := http.NewRequest(mode, url+"?"+values.Encode(), nil) req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
if err != nil { if err != nil {
return -1 return -1
} }
...@@ -25,8 +25,8 @@ func httpCode(handler http.HandlerFunc, mode, url string, values url.Values) int ...@@ -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) // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func HTTPSuccess(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values) bool { func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
code := httpCode(handler, mode, url, values) code := httpCode(handler, method, url, values)
if code == -1 { if code == -1 {
return false return false
} }
...@@ -38,8 +38,8 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, mode, url string, values ...@@ -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"}} // assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func HTTPRedirect(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values) bool { func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
code := httpCode(handler, mode, url, values) code := httpCode(handler, method, url, values)
if code == -1 { if code == -1 {
return false return false
} }
...@@ -51,8 +51,8 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, mode, url string, values ...@@ -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"}} // assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func HTTPError(t TestingT, handler http.HandlerFunc, mode, url string, values url.Values) bool { func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
code := httpCode(handler, mode, url, values) code := httpCode(handler, method, url, values)
if code == -1 { if code == -1 {
return false return false
} }
...@@ -61,9 +61,9 @@ func HTTPError(t TestingT, handler http.HandlerFunc, mode, url string, values ur ...@@ -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 // HTTPBody is a helper that returns HTTP body of the response. It returns
// empty string if building a new request fails. // 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() w := httptest.NewRecorder()
req, err := http.NewRequest(mode, url+"?"+values.Encode(), nil) req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
if err != nil { if err != nil {
return "" return ""
} }
...@@ -77,8 +77,8 @@ func HTTPBody(handler http.HandlerFunc, mode, url string, values url.Values) str ...@@ -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") // assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
// //
// Returns whether the assertion was successful (true) or not (false). // 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 { func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
body := HTTPBody(handler, mode, url, values) body := HTTPBody(handler, method, url, values)
contains := strings.Contains(body, fmt.Sprint(str)) contains := strings.Contains(body, fmt.Sprint(str))
if !contains { if !contains {
...@@ -94,8 +94,8 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, mode, url string, va ...@@ -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") // assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
// //
// Returns whether the assertion was successful (true) or not (false). // 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 { func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
body := HTTPBody(handler, mode, url, values) body := HTTPBody(handler, method, url, values)
contains := strings.Contains(body, fmt.Sprint(str)) contains := strings.Contains(body, fmt.Sprint(str))
if contains { if contains {
...@@ -114,8 +114,8 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, mode, url string, ...@@ -114,8 +114,8 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, mode, url string,
// assert.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) // assert.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, mode, url string, values url.Values) bool { func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method, url string, values url.Values) bool {
return HTTPSuccess(a.t, handler, mode, url, values) return HTTPSuccess(a.t, handler, method, url, values)
} }
// HTTPRedirect asserts that a specified handler returns a redirect status code. // 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 ...@@ -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"}} // assert.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, mode, url string, values url.Values) bool { func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method, url string, values url.Values) bool {
return HTTPRedirect(a.t, handler, mode, url, values) return HTTPRedirect(a.t, handler, method, url, values)
} }
// HTTPError asserts that a specified handler returns an error status code. // 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 ...@@ -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"}} // assert.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPError(handler http.HandlerFunc, mode, url string, values url.Values) bool { func (a *Assertions) HTTPError(handler http.HandlerFunc, method, url string, values url.Values) bool {
return HTTPError(a.t, handler, mode, url, values) return HTTPError(a.t, handler, method, url, values)
} }
// HTTPBodyContains asserts that a specified handler returns a // HTTPBodyContains asserts that a specified handler returns a
...@@ -142,8 +142,8 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, mode, url string, value ...@@ -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") // assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
// //
// Returns whether the assertion was successful (true) or not (false). // 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 { func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
return HTTPBodyContains(a.t, handler, mode, url, values, str) return HTTPBodyContains(a.t, handler, method, url, values, str)
} }
// HTTPBodyNotContains asserts that a specified handler returns a // HTTPBodyNotContains asserts that a specified handler returns a
...@@ -152,6 +152,6 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, mode, url string ...@@ -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") // assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
// //
// Returns whether the assertion was successful (true) or not (false). // 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 { func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
return HTTPBodyNotContains(a.t, handler, mode, url, values, str) return HTTPBodyNotContains(a.t, handler, method, url, values, str)
} }
...@@ -24,54 +24,4 @@ ...@@ -24,54 +24,4 @@
// //
// Every assertion function also takes an optional string message as the final argument, // 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. // 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 package require
...@@ -93,7 +93,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { ...@@ -93,7 +93,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) {
True(a.t, value, msgAndArgs...) 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") // require.False(myBool, "myBool should be false")
func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { func (a *Assertions) False(value bool, msgAndArgs ...interface{}) {
...@@ -209,3 +209,22 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter ...@@ -209,3 +209,22 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter
func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) {
NotRegexp(a.t, rx, str, msgAndArgs...) 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...)
}
package require package require
import ( import (
"encoding/json"
"time" "time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
...@@ -118,7 +119,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) { ...@@ -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") // require.False(t, myBool, "myBool should be false")
func False(t TestingT, value bool, msgAndArgs ...interface{}) { func False(t TestingT, value bool, msgAndArgs ...interface{}) {
...@@ -136,9 +137,12 @@ func NotEqual(t TestingT, expected, actual interface{}, 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"], "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{}) { func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) {
if !assert.Contains(t, s, contains, msgAndArgs...) { if !assert.Contains(t, s, contains, msgAndArgs...) {
t.FailNow() t.FailNow()
...@@ -228,6 +232,25 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf ...@@ -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 Errors
*/ */
...@@ -269,3 +292,17 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte ...@@ -269,3 +292,17 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte
t.FailNow() 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()
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment