diff --git a/mesos/builders.go b/mesos/builders.go new file mode 100644 index 0000000..229034f --- /dev/null +++ b/mesos/builders.go @@ -0,0 +1,64 @@ +package mesos + +import ( + "github.com/golang/protobuf/proto" + "github.com/mesos/mesos-go/api/v0/mesosproto" + "github.com/mesos/mesos-go/api/v0/mesosutil" +) + +// Optional attributes can be added. +func offer(id string, cpu float64, mem float64, unavailability *mesosproto.Unavailability, extra ...interface{}) *mesosproto.Offer { + attributes := []*mesosproto.Attribute{} + resources := []*mesosproto.Resource{ + mesosutil.NewScalarResource("cpus", cpu), + mesosutil.NewScalarResource("mem", mem), + } + for _, r := range extra { + switch r.(type) { + case *mesosproto.Attribute: + attributes = append(attributes, r.(*mesosproto.Attribute)) + case *mesosproto.Resource: + resources = append(resources, r.(*mesosproto.Resource)) + } + } + return &mesosproto.Offer{ + Id: &mesosproto.OfferID{ + Value: proto.String(id), + }, + FrameworkId: &mesosproto.FrameworkID{ + Value: proto.String("framework-1234"), + }, + SlaveId: &mesosproto.SlaveID{ + Value: proto.String("agent-id"), + }, + Hostname: proto.String("localhost"), + Resources: resources, + Attributes: attributes, + Unavailability: unavailability, + } +} + +func textAttribute(name string, value string) *mesosproto.Attribute { + return &mesosproto.Attribute{ + Name: proto.String(name), + Type: mesosproto.Value_TEXT.Enum(), + Text: &mesosproto.Value_Text{ + Value: proto.String(value), + }, + } +} + +func unavailability(details ...int64) *mesosproto.Unavailability { + un := mesosproto.Unavailability{} + if len(details) >= 1 { + un.Start = &mesosproto.TimeInfo{ + Nanoseconds: proto.Int64(details[0]), + } + } + if len(details) >= 2 { + un.Duration = &mesosproto.DurationInfo{ + Nanoseconds: proto.Int64(details[1]), + } + } + return &un +} diff --git a/mesos/match_test.go b/mesos/match_test.go index 7e2a47f..e337c28 100644 --- a/mesos/match_test.go +++ b/mesos/match_test.go @@ -3,112 +3,42 @@ package mesos import ( "testing" - "github.com/golang/protobuf/proto" "github.com/mesos/mesos-go/api/v0/mesosproto" - "github.com/mesos/mesos-go/api/v0/mesosutil" . "github.com/smartystreets/goconvey/convey" "github.com/eremetic-framework/eremetic" "time" ) -// Optional attributes can be added. -func offer(id string, cpu float64, mem float64, unavailability *mesosproto.Unavailability, attributes ...*mesosproto.Attribute) *mesosproto.Offer { - return &mesosproto.Offer{ - Id: &mesosproto.OfferID{ - Value: proto.String(id), - }, - FrameworkId: &mesosproto.FrameworkID{ - Value: proto.String("framework-1234"), - }, - SlaveId: &mesosproto.SlaveID{ - Value: proto.String("slave-1234"), - }, - Hostname: proto.String("localhost"), - Resources: []*mesosproto.Resource{ - mesosutil.NewScalarResource("cpus", cpu), - mesosutil.NewScalarResource("mem", mem), - }, - Attributes: attributes, - Unavailability: unavailability, - } -} - func TestMatch(t *testing.T) { offerA := offer("offer-a", 0.6, 200.0, - &mesosproto.Unavailability{}, - &mesosproto.Attribute{ - Name: proto.String("role"), - Type: mesosproto.Value_TEXT.Enum(), - Text: &mesosproto.Value_Text{ - Value: proto.String("badassmofo"), - }, - }, - &mesosproto.Attribute{ - Name: proto.String("node_name"), - Type: mesosproto.Value_TEXT.Enum(), - Text: &mesosproto.Value_Text{ - Value: proto.String("node1"), - }, - }, + unavailability(), + textAttribute("role", "badassmofo"), + textAttribute("node_name", "node1"), ) offerB := offer("offer-b", 1.8, 512.0, - &mesosproto.Unavailability{}, - &mesosproto.Attribute{ - Name: proto.String("node_name"), - Type: mesosproto.Value_TEXT.Enum(), - Text: &mesosproto.Value_Text{ - Value: proto.String("node2"), - }, - }, + unavailability(), + textAttribute("node_name", "node2"), ) offerC := offer("offer-c", 1.8, 512.0, - &mesosproto.Unavailability{ - Start: &mesosproto.TimeInfo{ - Nanoseconds: proto.Int64(time.Now().UnixNano()), - }, - Duration: &mesosproto.DurationInfo{ - Nanoseconds: proto.Int64(time.Unix(0, 0).Add(1 * time.Hour).UnixNano()), - }, - }, - &mesosproto.Attribute{ - Name: proto.String("node_name"), - Type: mesosproto.Value_TEXT.Enum(), - Text: &mesosproto.Value_Text{ - Value: proto.String("node3"), - }, - }, + unavailability( + time.Now().UnixNano(), + time.Unix(0, 0).Add(1*time.Hour).UnixNano(), + ), + textAttribute("node_name", "node3"), ) offerD := offer("offer-d", 1.8, 512.0, - &mesosproto.Unavailability{ - Start: &mesosproto.TimeInfo{ - Nanoseconds: proto.Int64(time.Now().UnixNano()), - }, - }, - &mesosproto.Attribute{ - Name: proto.String("node_name"), - Type: mesosproto.Value_TEXT.Enum(), - Text: &mesosproto.Value_Text{ - Value: proto.String("node4"), - }, - }, + unavailability( + time.Now().UnixNano(), + ), + textAttribute("node_name", "node4"), ) offerE := offer("offer-e", 1.8, 512.0, - &mesosproto.Unavailability{ - Start: &mesosproto.TimeInfo{ - Nanoseconds: proto.Int64(time.Now().Add(-2 * time.Hour).UnixNano()), - }, - Duration: &mesosproto.DurationInfo{ - Nanoseconds: proto.Int64(time.Unix(0, 0).Add(1 * time.Hour).UnixNano()), - }, - }, - &mesosproto.Attribute{ - Name: proto.String("node_name"), - Type: mesosproto.Value_TEXT.Enum(), - Text: &mesosproto.Value_Text{ - Value: proto.String("node3"), - }, - }, + unavailability( + time.Now().Add(-2*time.Hour).UnixNano(), + time.Unix(0, 0).Add(1*time.Hour).UnixNano(), + ), + textAttribute("node_name", "node3"), ) Convey("CPUAvailable", t, func() { @@ -271,13 +201,13 @@ func TestMatch(t *testing.T) { Convey("Match slave with mulitple attributes", func() { // Build two new offers, both with the same role as offerA. offerC := offer("offer-c", 0.6, 200.0, - &mesosproto.Unavailability{}, - &mesosproto.Attribute{Name: proto.String("role"), Type: mesosproto.Value_TEXT.Enum(), Text: &mesosproto.Value_Text{Value: proto.String("badassmofo")}}, - &mesosproto.Attribute{Name: proto.String("node_name"), Type: mesosproto.Value_TEXT.Enum(), Text: &mesosproto.Value_Text{Value: proto.String("node3")}}, + unavailability(), + textAttribute("role", "badassmofo"), + textAttribute("node_name", "node3"), ) offerD := offer("offer-d", 0.6, 200.0, - &mesosproto.Unavailability{}, - &mesosproto.Attribute{Name: proto.String("role"), Type: mesosproto.Value_TEXT.Enum(), Text: &mesosproto.Value_Text{Value: proto.String("badassmofo")}}, + unavailability(), + textAttribute("role", "badassmofo"), ) task := eremetic.Task{ diff --git a/mesos/task_test.go b/mesos/task_test.go index 2adf628..3289e9c 100644 --- a/mesos/task_test.go +++ b/mesos/task_test.go @@ -5,7 +5,6 @@ import ( "time" "github.com/eremetic-framework/eremetic" - "github.com/golang/protobuf/proto" "github.com/mesos/mesos-go/api/v0/mesosproto" "github.com/mesos/mesos-go/api/v0/mesosutil" . "github.com/smartystreets/goconvey/convey" @@ -31,28 +30,18 @@ func TestTask(t *testing.T) { Name: "Eremetic task 17", } - portres := "ports" - offer := mesosproto.Offer{ - FrameworkId: &mesosproto.FrameworkID{ - Value: proto.String("framework-id"), - }, - SlaveId: &mesosproto.SlaveID{ - Value: proto.String("agent-id"), - }, - Hostname: proto.String("hostname"), - Resources: []*mesosproto.Resource{&mesosproto.Resource{ - Name: &portres, - Type: mesosproto.Value_RANGES.Enum(), - Ranges: &mesosproto.Value_Ranges{ - Range: []*mesosproto.Value_Range{ - mesosutil.NewValueRange(31000, 31010), - }, + offer := offer("offer-1", 1.0, 500.0, + &mesosproto.Unavailability{}, + mesosutil.NewRangesResource( + "ports", + []*mesosproto.Value_Range{ + mesosutil.NewValueRange(31000, 31010), }, - }}, - } + ), + ) Convey("No volume or environment specified", func() { - net, taskInfo := createTaskInfo(eremeticTask, &offer) + net, taskInfo := createTaskInfo(eremeticTask, offer) So(taskInfo.TaskId.GetValue(), ShouldEqual, eremeticTask.ID) So(taskInfo.GetName(), ShouldEqual, eremeticTask.Name) @@ -67,7 +56,7 @@ func TestTask(t *testing.T) { Convey("Given no Command", func() { eremeticTask.Command = "" - _, taskInfo := createTaskInfo(eremeticTask, &offer) + _, taskInfo := createTaskInfo(eremeticTask, offer) So(taskInfo.Command.GetValue(), ShouldBeEmpty) So(taskInfo.Command.GetShell(), ShouldBeFalse) @@ -85,7 +74,7 @@ func TestTask(t *testing.T) { eremeticTask.Environment = environment eremeticTask.Volumes = volumes - _, taskInfo := createTaskInfo(eremeticTask, &offer) + _, taskInfo := createTaskInfo(eremeticTask, offer) So(taskInfo.TaskId.GetValue(), ShouldEqual, eremeticTask.ID) So(taskInfo.Container.Volumes[0].GetContainerPath(), ShouldEqual, volumes[0].ContainerPath) @@ -97,7 +86,7 @@ func TestTask(t *testing.T) { }) Convey("Given no network", func() { - _, taskInfo := createTaskInfo(eremeticTask, &offer) + _, taskInfo := createTaskInfo(eremeticTask, offer) So(taskInfo.TaskId.GetValue(), ShouldEqual, eremeticTask.ID) So(taskInfo.Container.Docker.Network.String(), ShouldEqual, "BRIDGE") @@ -105,7 +94,7 @@ func TestTask(t *testing.T) { Convey("Given network", func() { eremeticTask.Network = "HOST" - _, taskInfo := createTaskInfo(eremeticTask, &offer) + _, taskInfo := createTaskInfo(eremeticTask, offer) So(taskInfo.TaskId.GetValue(), ShouldEqual, eremeticTask.ID) So(taskInfo.Container.Docker.Network.String(), ShouldEqual, "HOST") @@ -124,7 +113,7 @@ func TestTask(t *testing.T) { eremeticTask.Ports = ports - _, taskInfo := createTaskInfo(eremeticTask, &offer) + _, taskInfo := createTaskInfo(eremeticTask, offer) So(len(taskInfo.Container.Docker.PortMappings), ShouldEqual, 1) So(taskInfo.Container.Docker.GetPortMappings()[0].GetContainerPort(), ShouldEqual, ports[0].ContainerPort) @@ -163,7 +152,7 @@ func TestTask(t *testing.T) { eremeticTask.Ports = ports - _, taskInfo := createTaskInfo(eremeticTask, &offer) + _, taskInfo := createTaskInfo(eremeticTask, offer) So(len(taskInfo.Container.Docker.PortMappings), ShouldEqual, 1) So(taskInfo.Container.Docker.GetPortMappings()[0].GetContainerPort(), ShouldEqual, 31000) @@ -196,7 +185,7 @@ func TestTask(t *testing.T) { Extract: true, }} eremeticTask.FetchURIs = URI - _, taskInfo := createTaskInfo(eremeticTask, &offer) + _, taskInfo := createTaskInfo(eremeticTask, offer) So(taskInfo.TaskId.GetValue(), ShouldEqual, eremeticTask.ID) So(taskInfo.Command.Uris, ShouldHaveLength, 1) @@ -213,7 +202,7 @@ func TestTask(t *testing.T) { Cache: true, }} eremeticTask.FetchURIs = URI - _, taskInfo := createTaskInfo(eremeticTask, &offer) + _, taskInfo := createTaskInfo(eremeticTask, offer) So(taskInfo.TaskId.GetValue(), ShouldEqual, eremeticTask.ID) So(taskInfo.Command.Uris, ShouldHaveLength, 1) @@ -228,7 +217,7 @@ func TestTask(t *testing.T) { URI: "http://foobar.local/cats.jpeg", }} eremeticTask.FetchURIs = URI - _, taskInfo := createTaskInfo(eremeticTask, &offer) + _, taskInfo := createTaskInfo(eremeticTask, offer) So(taskInfo.TaskId.GetValue(), ShouldEqual, eremeticTask.ID) So(taskInfo.Command.Uris, ShouldHaveLength, 1) @@ -244,7 +233,7 @@ func TestTask(t *testing.T) { Executable: true, }} eremeticTask.FetchURIs = URI - _, taskInfo := createTaskInfo(eremeticTask, &offer) + _, taskInfo := createTaskInfo(eremeticTask, offer) So(taskInfo.TaskId.GetValue(), ShouldEqual, eremeticTask.ID) So(taskInfo.Command.Uris, ShouldHaveLength, 1) @@ -256,7 +245,7 @@ func TestTask(t *testing.T) { Convey("Force pull of docker image", func() { eremeticTask.ForcePullImage = true - _, taskInfo := createTaskInfo(eremeticTask, &offer) + _, taskInfo := createTaskInfo(eremeticTask, offer) So(taskInfo.TaskId.GetValue(), ShouldEqual, eremeticTask.ID) So(taskInfo.Container.Docker.GetForcePullImage(), ShouldBeTrue)