Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Marathon 1.5 #335

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[![Build Status](https://travis-ci.org/gambol99/go-marathon.svg?branch=master)](https://travis-ci.org/gambol99/go-marathon)
[![GoDoc](http://godoc.org/github.com/gambol99/go-marathon?status.png)](http://godoc.org/github.com/gambol99/go-marathon)
[![Go Report Card](https://goreportcard.com/badge/github.com/katallaxie/go-marathon)](https://goreportcard.com/report/github.com/katallaxie/go-marathon)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This points at the wrong repo. But really, I'd rather have this added by a separate PR as well.

Remember that if we have to revert the final commit that will end up in master for whatever functional reason, all drive-by changes will be gone as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets keep it for now :)

[![Coverage Status](https://coveralls.io/repos/github/gambol99/go-marathon/badge.svg?branch=master)](https://coveralls.io/github/gambol99/go-marathon?branch=master)

# Go-Marathon
Expand Down
37 changes: 35 additions & 2 deletions application.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ type Port struct {
Protocol string `json:"protocol,omitempty"`
}

// Network provides info about application networking
type Network struct {
Name string `json:"name,omitempty"`
Mode string `json:"mode,omitempty"`
}

// Application is the definition for an application in marathon
type Application struct {
ID string `json:"id,omitempty"`
Expand All @@ -64,6 +70,8 @@ type Application struct {
CPUs float64 `json:"cpus,omitempty"`
GPUs *float64 `json:"gpus,omitempty"`
Disk *float64 `json:"disk,omitempty"`
Networks *[]Network `json:"networks,omitempty"`

// Contains non-secret environment variables. Secrets environment variables are part of the Secrets map.
Env *map[string]string `json:"-"`
Executor *string `json:"executor,omitempty"`
Expand Down Expand Up @@ -495,7 +503,10 @@ func (r *Application) CheckHTTP(path string, port, interval int) (*Application,
// step: get the port index
portIndex, err := r.Container.Docker.ServicePortIndex(port)
if err != nil {
return nil, err
portIndex, err = r.Container.ServicePortIndex(port)
if err != nil {
return nil, err
}
}
health := NewDefaultHealthCheck()
health.IntervalSeconds = interval
Expand All @@ -518,7 +529,10 @@ func (r *Application) CheckTCP(port, interval int) (*Application, error) {
// step: get the port index
portIndex, err := r.Container.Docker.ServicePortIndex(port)
if err != nil {
return nil, err
portIndex, err = r.Container.ServicePortIndex(port)
if err != nil {
return nil, err
}
}
health := NewDefaultHealthCheck()
health.Protocol = "TCP"
Expand Down Expand Up @@ -956,3 +970,22 @@ func (d *Discovery) AddPort(port Port) *Discovery {
d.Ports = &ports
return d
}

// EmptyNetworks explicitly empties networks
func (r *Application) EmptyNetworks() *Application {
r.Networks = &[]Network{}
return r
}

// SetNetwork sets the networking mode
func (r *Application) SetNetwork(name string, mode string) *Application {
if r.Networks == nil {
r.EmptyNetworks()
}

network := Network{Name: name, Mode: mode}
networks := *r.Networks
networks = append(networks, network)
r.Networks = &networks
return r
}
222 changes: 162 additions & 60 deletions application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,31 +43,70 @@ func TestApplicationMemory(t *testing.T) {
}

func TestApplicationString(t *testing.T) {
app := NewDockerApplication().
Name("my-app").
CPU(0.1).
Memory(64).
Storage(0.0).
Count(2).
AddArgs("/usr/sbin/apache2ctl", "-D", "FOREGROUND").
AddEnv("NAME", "frontend_http").
AddEnv("SERVICE_80_NAME", "test_http")
app.
Container.Docker.Container("quay.io/gambol99/apache-php:latest").
Bridged().
Expose(80).
Expose(443)
app, err := app.CheckHTTP("/health", 80, 5)
assert.Nil(t, err)

expectedAppJSONBytes, err := ioutil.ReadFile("tests/app-definitions/TestApplicationString-output.json")
if err != nil {
panic(err)
type test struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use an anonymous struct here as the type will never be reused.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, it was just for sake of clarity.

name string
app *Application
expectedAppJSONPath string
setup func(*Application)
}
expectedAppJSON := strings.TrimSpace(string(expectedAppJSONBytes))
assert.Equal(t, expectedAppJSON, app.String())
}

tests := []test{
{
name: "marathon < 1.5",
app: NewDockerApplication().
Name("my-app").
CPU(0.1).
Memory(64).
Storage(0.0).
Count(2).
AddArgs("/usr/sbin/apache2ctl", "-D", "FOREGROUND").
AddEnv("NAME", "frontend_http").
AddEnv("SERVICE_80_NAME", "test_http"),
expectedAppJSONPath: "tests/app-definitions/TestApplicationString-output.json",
setup: func(app *Application) {
app.
Container.Docker.Container("quay.io/gambol99/apache-php:latest").
Bridged().
Expose(80).
Expose(443)
},
},
{
name: "marathon > 1.5",
app: NewDockerApplication().
Name("my-app").
CPU(0.1).
Memory(64).
Storage(0.0).
Count(2).
SetNetwork("", "container/bridge").
AddArgs("/usr/sbin/apache2ctl", "-D", "FOREGROUND").
AddEnv("NAME", "frontend_http").
AddEnv("SERVICE_80_NAME", "test_http"),
expectedAppJSONPath: "tests/app-definitions/TestApplicationString-1.5-output.json",
setup: func(app *Application) {
app.
Container.Expose(80).Expose(443).
Docker.Container("quay.io/gambol99/apache-php:latest")
},
},
}

for _, test := range tests {
label := fmt.Sprintf("test: %s", test.name)

test.setup(test.app)
_, err := test.app.CheckHTTP("/health", 80, 5)
assert.Nil(t, err)

expectedAppJSONBytes, err := ioutil.ReadFile(test.expectedAppJSONPath)
if err != nil {
panic(err)
}
expectedAppJSON := strings.TrimSpace(string(expectedAppJSONBytes))
assert.Equal(t, expectedAppJSON, test.app.String(), label)
}
}
func TestApplicationCount(t *testing.T) {
app := NewDockerApplication()
assert.Nil(t, app.Instances)
Expand Down Expand Up @@ -290,45 +329,87 @@ func TestApplicationPortDefinitions(t *testing.T) {
}

func TestHasHealthChecks(t *testing.T) {
app := NewDockerApplication()
assert.False(t, app.HasHealthChecks())
app.Container.Docker.Container("quay.io/gambol99/apache-php:latest").Expose(80)
_, err := app.CheckTCP(80, 10)
assert.NoError(t, err)
assert.True(t, app.HasHealthChecks())
apps := []*Application{
NewDockerApplication(),
NewDockerApplication(),
}

for i := range apps {
assert.False(t, apps[i].HasHealthChecks())
}

// Marathon < 1.5
apps[0].Container.Docker.Container("quay.io/gambol99/apache-php:latest").Expose(80)

// Marathon >= 1.5
apps[1].Container.Expose(80).Docker.Container("quay.io/gambol99/apache-php:latest")

for i := range apps {
_, err := apps[i].CheckTCP(80, 10)
assert.NoError(t, err)
assert.True(t, apps[i].HasHealthChecks())
}
}

func TestApplicationCheckTCP(t *testing.T) {
app := NewDockerApplication()
assert.False(t, app.HasHealthChecks())
_, err := app.CheckTCP(80, 10)
assert.Error(t, err)
assert.False(t, app.HasHealthChecks())
app.Container.Docker.Container("quay.io/gambol99/apache-php:latest").Expose(80)
_, err = app.CheckTCP(80, 10)
assert.NoError(t, err)
assert.True(t, app.HasHealthChecks())
check := (*app.HealthChecks)[0]
assert.Equal(t, "TCP", check.Protocol)
assert.Equal(t, 10, check.IntervalSeconds)
assert.Equal(t, 0, *check.PortIndex)
apps := []*Application{
NewDockerApplication(),
NewDockerApplication(),
}

for i := range apps {
assert.False(t, apps[i].HasHealthChecks())
_, err := apps[i].CheckTCP(80, 10)
assert.Error(t, err)
assert.False(t, apps[i].HasHealthChecks())
}

// Marathon < 1.5
apps[0].Container.Docker.Container("quay.io/gambol99/apache-php:latest").Expose(80)

// Marathon >= 1.5
apps[1].Container.Expose(80).Docker.Container("quay.io/gambol99/apache-php:latest")

for i := range apps {
_, err := apps[i].CheckTCP(80, 10)
assert.NoError(t, err)
assert.True(t, apps[i].HasHealthChecks())
check := (*apps[i].HealthChecks)[0]
assert.Equal(t, "TCP", check.Protocol)
assert.Equal(t, 10, check.IntervalSeconds)
assert.Equal(t, 0, *check.PortIndex)
}
}

func TestApplicationCheckHTTP(t *testing.T) {
app := NewDockerApplication()
assert.False(t, app.HasHealthChecks())
_, err := app.CheckHTTP("/", 80, 10)
assert.Error(t, err)
assert.False(t, app.HasHealthChecks())
app.Container.Docker.Container("quay.io/gambol99/apache-php:latest").Expose(80)
_, err = app.CheckHTTP("/health", 80, 10)
assert.NoError(t, err)
assert.True(t, app.HasHealthChecks())
check := (*app.HealthChecks)[0]
assert.Equal(t, "HTTP", check.Protocol)
assert.Equal(t, 10, check.IntervalSeconds)
assert.Equal(t, "/health", *check.Path)
assert.Equal(t, 0, *check.PortIndex)
apps := []*Application{
NewDockerApplication(),
NewDockerApplication(),
}

for i := range apps {
assert.False(t, apps[i].HasHealthChecks())
_, err := apps[i].CheckHTTP("/", 80, 10)
assert.Error(t, err)
assert.False(t, apps[i].HasHealthChecks())
}

// Marathon < 1.5
apps[0].Container.Docker.Container("quay.io/gambol99/apache-php:latest").Expose(80)

// Marathon >= 1.5
apps[1].Container.Expose(80).Docker.Container("quay.io/gambol99/apache-php:latest")

for i := range apps {
_, err := apps[i].CheckHTTP("/health", 80, 10)
assert.NoError(t, err)
assert.True(t, apps[i].HasHealthChecks())
check := (*apps[i].HealthChecks)[0]
assert.Equal(t, "HTTP", check.Protocol)
assert.Equal(t, 10, check.IntervalSeconds)
assert.Equal(t, "/health", *check.Path)
assert.Equal(t, 0, *check.PortIndex)
}
}

func TestCreateApplication(t *testing.T) {
Expand Down Expand Up @@ -483,9 +564,9 @@ func TestApplicationFetchURIs(t *testing.T) {
assert.Equal(t, Fetch{URI: "file://uri2.tar.gz"}, (*app.Fetch)[1])
assert.Equal(t, Fetch{URI: "file://uri3.tar.gz"}, (*app.Fetch)[2])

app.EmptyUris()
assert.NotNil(t, app.Uris)
assert.Equal(t, 0, len(*app.Uris))
app.EmptyFetchURIs()
assert.NotNil(t, app.Fetch)
assert.Equal(t, 0, len(*app.Fetch))
}

func TestSetApplicationVersion(t *testing.T) {
Expand Down Expand Up @@ -725,7 +806,7 @@ func TestIPAddressPerTask(t *testing.T) {
assert.Equal(t, 1, len(*ipPerTask.Groups))
assert.Equal(t, "label", (*ipPerTask.Groups)[0])
assert.Equal(t, "value", (*ipPerTask.Labels)["key"])
assert.NotEmpty(t, ipPerTask.Discovery)
assert.NotEmpty(t, *ipPerTask.Discovery)

ipPerTask.EmptyGroups()
assert.Equal(t, 0, len(*ipPerTask.Groups))
Expand Down Expand Up @@ -764,3 +845,24 @@ func TestUpgradeStrategy(t *testing.T) {
assert.Nil(t, us.MinimumHealthCapacity)
assert.Nil(t, us.MaximumOverCapacity)
}

func TestBridgedNetworking(t *testing.T) {
app := NewDockerApplication().SetNetwork("test", "container/bridge")
networks := *app.Networks

assert.Equal(t, networks[0].Mode, "container/bridge")
}

func TestContainerNetworking(t *testing.T) {
app := NewDockerApplication().SetNetwork("test", "container")
networks := *app.Networks

assert.Equal(t, networks[0].Mode, "container")
}

func TestHostNetworking(t *testing.T) {
app := NewDockerApplication().SetNetwork("test", "host")
networks := *app.Networks

assert.Equal(t, networks[0].Mode, "host")
}
2 changes: 1 addition & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ func (r *marathonClient) apiCall(method, path string, body, result interface{})
r.debugLog("apiCall(): %v %v returned %v %s", request.Method, request.URL.String(), response.Status, oneLogLine(respBody))
}

// step: check for a successfull response
// step: check for a successful response
if response.StatusCode >= 200 && response.StatusCode <= 299 {
if result != nil {
// If we have a deployment ID header and no response body, give them that
Expand Down
Loading