Categorygithub.com/digitalocean/go-marathon
modulepackage
1.8.0
Repository: https://github.com/digitalocean/go-marathon.git
Documentation: pkg.go.dev

# README

Build Status GoDoc Go Report Card Coverage Status

Go-Marathon

Go-marathon is a API library for working with Marathon. It currently supports

  • Application and group deployment
  • Helper filters for pulling the status, configuration and tasks
  • Multiple Endpoint support for HA deployments
  • Marathon Event Subscriptions and Event Streams
  • Pods

Note: the library is still under active development; users should expect frequent (possibly breaking) API changes for the time being.

It requires Go version 1.6 or higher.

Code Examples

There is also an examples directory in the source which shows hints and snippets of code of how to use it — which is probably the best place to start.

You can use examples/docker-compose.yml in order to start a test cluster.

Creating a client

import (
	marathon "github.com/gambol99/go-marathon"
)

marathonURL := "http://10.241.1.71:8080"
config := marathon.NewDefaultConfig()
config.URL = marathonURL
client, err := marathon.NewClient(config)
if err != nil {
	log.Fatalf("Failed to create a client for marathon, error: %s", err)
}

applications, err := client.Applications(nil)
...

Note, you can also specify multiple endpoint for Marathon (i.e. you have setup Marathon in HA mode and having multiple running)

marathonURL := "http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080"

The first one specified will be used, if that goes offline the member is marked as "unavailable" and a background process will continue to ping the member until it's back online.

You can also pass a custom path to the URL, which is especially needed in case of DCOS:

marathonURL := "http://10.241.1.71:8080/cluster,10.241.1.72:8080/cluster,10.241.1.73:8080/cluster"

If you specify a DCOSToken in the configuration file but do not pass a custom URL path, /marathon will be used.

Customizing the HTTP Clients

HTTP clients with reasonable timeouts are used by default. It is possible to pass custom clients to the configuration though if the behavior should be customized (e.g., to bypass TLS verification, load root CAs, or change timeouts).

Two clients can be given independently of each other:

  • HTTPClient used only for (non-SSE) HTTP API requests. By default, an http.Client with 10 seconds timeout for the entire request is used.
  • HTTPSSEClient used only for SSE-based subscription requests. Note that HTTPSSEClient cannot have a response read timeout set as this breaks SSE communication; trying to do so will lead to an error during the SSE connection setup. By default, an http.Client with 5 seconds timeout for dial and TLS handshake, and 10 seconds timeout for response headers received is used.

If no HTTPSSEClient is given but an HTTPClient is, it will be used for SSE subscriptions as well (thereby overriding the default SSE HTTP client).

marathonURL := "http://10.241.1.71:8080"
config := marathon.NewDefaultConfig()
config.URL = marathonURL
config.HTTPClient = &http.Client{
    Timeout: (time.Duration(10) * time.Second),
    Transport: &http.Transport{
        Dial: (&net.Dialer{
            Timeout:   10 * time.Second,
            KeepAlive: 10 * time.Second,
        }).Dial,
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,
        },
    },
}
config.HTTPSSEClient = &http.Client{
    // Invalid to set Timeout as it contains timeout for reading a response body
    Transport: &http.Transport{
        Dial: (&net.Dialer{
            Timeout:   10 * time.Second,
            KeepAlive: 10 * time.Second,
        }).Dial,
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,
        },
    },
}

Listing the applications

applications, err := client.Applications(nil)
if err != nil {
	log.Fatalf("Failed to list applications: %s", err)
}

log.Printf("Found %d application(s) running", len(applications.Apps))
for _, application := range applications.Apps {
	log.Printf("Application: %s", application)
	appID := application.ID

	details, err := client.Application(appID)
	if err != nil {
		log.Fatalf("Failed to get application %s: %s", appID, err)
	}
	if details.Tasks != nil {
		for _, task := range details.Tasks {
			log.Printf("application %s has task: %s", appID, task)
		}
	}
}

Creating a new application

log.Printf("Deploying a new application")
application := marathon.NewDockerApplication().
  Name(applicationName).
  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").
  CheckHTTP("/health", 10, 5)

application.
  Container.Docker.Container("quay.io/gambol99/apache-php:latest").
  Bridged().
  Expose(80).
  Expose(443)

if _, err := client.CreateApplication(application); err != nil {
	log.Fatalf("Failed to create application: %s, error: %s", application, err)
} else {
	log.Printf("Created the application: %s", application)
}

Note: Applications may also be defined by means of initializing a marathon.Application struct instance directly. However, go-marathon's DSL as shown above provides a more concise way to achieve the same.

Scaling application

Change the number of application instances to 4

log.Printf("Scale to 4 instances")
if err := client.ScaleApplicationInstances(application.ID, 4); err != nil {
	log.Fatalf("Failed to delete the application: %s, error: %s", application, err)
} else {
	client.WaitOnApplication(application.ID, 30 * time.Second)
	log.Printf("Successfully scaled the application")
}

Pods

Pods allow you to deploy groups of tasks as a unit. All tasks in a single instance of a pod share networking and storage. View the Marathon documentation for more details on this feature.

Examples of their usage can be seen in the examples/pods directory, and a smaller snippet is below.

// Initialize a single-container pod running nginx
pod := marathon.NewPod()

image := marathon.NewDockerPodContainerImage().SetID("nginx")

container := marathon.NewPodContainer().
	SetName("container", i).
	CPUs(0.1).
	Memory(128).
	SetImage(image)

pod.Name("mypod").AddContainer(container)

// Create it and wait for it to start up
pod, err := client.CreatePod(pod)
err = client.WaitOnPod(pod.ID, time.Minute*1)

// Scale it
pod.Count(5)
pod, err = client.UpdatePod(pod, true)

// Delete it
id, err := client.DeletePod(pod.ID, true)

Subscription & Events

Request to listen to events related to applications — namely status updates, health checks changes and failures. There are two different event transports controlled by EventsTransport setting with the following possible values: EventsTransportSSE and EventsTransportCallback (default value). See Event Stream and Event Subscriptions for details.

Event subscriptions can also be individually controlled with the Subscribe and Unsubscribe functions. See Controlling subscriptions for more details.

Event Stream

Only available in Marathon >= 0.9.0. Does not require any special configuration or prerequisites.

// Configure client
config := marathon.NewDefaultConfig()
config.URL = marathonURL
config.EventsTransport = marathon.EventsTransportSSE

client, err := marathon.NewClient(config)
if err != nil {
	log.Fatalf("Failed to create a client for marathon, error: %s", err)
}

// Register for events
events, err = client.AddEventsListener(marathon.EventIDApplications)
if err != nil {
	log.Fatalf("Failed to register for events, %s", err)
}

timer := time.After(60 * time.Second)
done := false

// Receive events from channel for 60 seconds
for {
	if done {
		break
	}
	select {
	case <-timer:
		log.Printf("Exiting the loop")
		done = true
	case event := <-events:
		log.Printf("Received event: %s", event)
	}
}

// Unsubscribe from Marathon events
client.RemoveEventsListener(events)

Event Subscriptions

Requires to start a built-in web server accessible by Marathon to connect and push events to. Consider the following additional settings:

  • EventsInterface — the interface we should be listening on for events. Default "eth0".
  • EventsPort — built-in web server port. Default 10001.
  • CallbackURL — custom callback URL. Default "".
// Configure client
config := marathon.NewDefaultConfig()
config.URL = marathonURL
config.EventsInterface = marathonInterface
config.EventsPort = marathonPort

client, err := marathon.NewClient(config)
if err != nil {
	log.Fatalf("Failed to create a client for marathon, error: %s", err)
}

// Register for events
events, err = client.AddEventsListener(marathon.EventIDApplications)
if err != nil {
	log.Fatalf("Failed to register for events, %s", err)
}

timer := time.After(60 * time.Second)
done := false

// Receive events from channel for 60 seconds
for {
	if done {
		break
	}
	select {
	case <-timer:
		log.Printf("Exiting the loop")
		done = true
	case event := <-events:
		log.Printf("Received event: %s", event)
	}
}

// Unsubscribe from Marathon events
client.RemoveEventsListener(events)

See events.go for a full list of event IDs.

Controlling subscriptions

If you simply want to (de)register event subscribers (i.e. without starting an internal web server) you can use the Subscribe and Unsubscribe methods.

// Configure client
config := marathon.NewDefaultConfig()
config.URL = marathonURL

client, err := marathon.NewClient(config)
if err != nil {
	log.Fatalf("Failed to create a client for marathon, error: %s", err)
}

// Register an event subscriber via a callback URL
callbackURL := "http://10.241.1.71:9494"
if err := client.Subscribe(callbackURL); err != nil {
	log.Fatalf("Unable to register the callbackURL [%s], error: %s", callbackURL, err)
}

// Deregister the same subscriber
if err := client.Unsubscribe(callbackURL); err != nil {
	log.Fatalf("Unable to deregister the callbackURL [%s], error: %s", callbackURL, err)
}

Contributing

See the contribution guidelines.

Development

Marathon Fake

go-marathon employs a fake Marathon implementation for testing purposes. It maintains a YML-encoded list of HTTP response messages which are returned upon a successful match based upon a number of attributes, the so-called message identifier:

  • HTTP URI (without the protocol and the hostname, e.g., /v2/apps)
  • HTTP method (e.g., GET)
  • response content (i.e., the message returned)
  • scope (see below)

Response Content

The response content can be provided in one of two forms:

  • static: A pure response message returned on every match, including repeated queries.
  • index: A list of response messages associated to a particular (indexed) sequence order. A message will be returned iff it matches and its zero-based index equals the current request count.

An example for a trivial static response content is

- uri: /v2/apps
  method: POST
  content: |
		{
		"app": {
		}
		}

which would be returned for every POST request targetting /v2/apps.

An indexed response content would look like:

- uri: /v2/apps
  method: POST
  contentSequence:
		- index: 1
		- content: |
			{
			"app": {
				"id": "foo"
			}
			}
		- index: 3
		- content: |
			{
			"app": {
				"id": "bar"
			}
			}

What this means is that the first POST request to /v2/apps would yield a 404, the second one the foo app, the third one 404 again, the fourth one bar, and every following request thereafter a 404 again. Indexed responses enable more flexible testing required by some use cases.

Trying to define both a static and indexed response content constitutes an error and leads to panic.

Scope

By default, all responses are defined globally: Every message can be queried by any request across all tests. This enables reusability and allows to keep the YML definition fairly short. For certain cases, however, it is desirable to define a set of responses that are delivered exclusively for a particular test. Scopes offer a means to do so by representing a concept similar to namespaces. Combined with indexed responses, they allow to return different responses for message identifiers already defined at the global level.

Scopes do not have a particular format -- they are just strings. A scope must be defined in two places: The message specification and the server configuration. They are pure strings without any particular structure. Given the messages specification

- uri: /v2/apps
  method: GET
	# Note: no scope defined.
  content: |
		{
		"app": {
			"id": "foo"
		}
		}
- uri: /v2/apps
  method: GET
	scope: v1.1.1  # This one does have a scope.
  contentSequence:
		- index: 1
		- content: |
			{
			"app": {
				"id": "bar"
			}
			}

and the tests

func TestFoo(t * testing.T) {
	endpoint := newFakeMarathonEndpoint(t, nil)  // No custom configs given.
	defer endpoint.Close()
	app, err := endpoint.Client.Applications(nil)
	// Do something with "foo"
}

func TestFoo(t * testing.T) {
	endpoint := newFakeMarathonEndpoint(t, &configContainer{
		server: &serverConfig{
			scope: "v1.1.1",		// Matches the message spec's scope.
		},
	})
	defer endpoint.Close()
	app, err := endpoint.Client.Applications(nil)
	// Do something with "bar"
}

The "foo" response can be used by all tests using the default fake endpoint (such as TestFoo), while the "bar" response is only visible by tests that explicitly set the scope to 1.1.1 (as TestBar does) and query the endpoint twice.

# Packages

No description provided by the author

# Functions

GetEvent returns allocated empty event object which corresponds to provided event type eventType: the type of Marathon event.
NewAPIError creates a new APIError instance from the given response code and content.
NewApplicationGroup create a new application group name: the name of the group.
NewClient creates a new marathon client config: the configuration to use.
NewContainerPodNetwork creates a PodNetwork for a container.
NewDefaultConfig create a default client config.
NewDefaultHealthCheck creates a default application health check.
NewDockerApplication creates a default docker application.
NewDockerContainer creates a default docker container for you.
NewDockerPodContainerImage creates a docker PodContainerImage.
NewPod create an empty pod.
NewPodContainer creates an empty PodContainer.
NewPodContainerImage creates an empty PodContainerImage.
NewPodEndpoint creates an empty PodEndpoint.
NewPodNetwork creates an empty PodNetwork.
NewPodPlacement creates an empty PodPlacement.
NewPodSchedulingPolicy creates an empty PodSchedulingPolicy.
NewPodVolume creates a new PodVolume.
NewPodVolumeMount creates a new PodVolumeMount.
NewResources creates an empty Resources.

# Constants

No description provided by the author
No description provided by the author
ErrCodeAppLocked specifies a POST 409 Conflict error.
ErrCodeBadRequest specifies a 400 Bad Request error.
ErrCodeDuplicateID specifies a PUT 409 Conflict error.
ErrCodeForbidden specifies a 403 Forbidden error.
ErrCodeInvalidBean specifies a 422 UnprocessableEntity error.
ErrCodeMethodNotAllowed specifies a 405 Method Not Allowed.
ErrCodeNotFound specifies a 404 Not Found error.
ErrCodeServer specifies a 500+ Server error.
ErrCodeUnauthorized specifies a 401 Unauthorized error.
ErrCodeUnknown specifies an unknown error.
EventIDAddHealthCheck is the event listener ID for the corresponding event.
EventIDAPIRequest is the event listener ID for the corresponding event.
EventIDApplications comprises all listener IDs for application events.
EventIDAppTerminated is the event listener ID for the corresponding event.
EventIDChangedHealthCheck is the event listener ID for the corresponding event.
EventIDDeploymentFailed is the event listener ID for the corresponding event.
EventIDDeploymentInfo is the event listener ID for the corresponding event.
EventIDDeploymentStepFailed is the event listener ID for the corresponding event.
EventIDDeploymentStepSuccess is the event listener ID for the corresponding event.
EventIDDeploymentSuccess is the event listener ID for the corresponding event.
EventIDFailedHealthCheck is the event listener ID for the corresponding event.
EventIDFrameworkMessage is the event listener ID for the corresponding event.
EventIDGroupChangeFailed is the event listener ID for the corresponding event.
EventIDGroupChangeSuccess is the event listener ID for the corresponding event.
EventIDRemoveHealthCheck is the event listener ID for the corresponding event.
EventIDStatusUpdate is the event listener ID for the corresponding event.
EventIDStreamAttached is the event listener ID for the corresponding event.
EventIDStreamDetached is the event listener ID for the corresponding event.
EventIDSubscription is the event listener ID for the corresponding event.
EventIDSubscriptions comprises all listener IDs for subscription events.
EventIDUnsubscribed is the event listener ID for the corresponding event.
EventsTransportCallback activates callback events transport.
EventsTransportSSE activates stream events transport.
No description provided by the author
ImageTypeAppC is the appc format.
ImageTypeDocker is the docker format.
PersistentVolumeTypeMount is the mount type of the persistent volume.
PersistentVolumeTypePath is the mount path of the persistent volume.
PersistentVolumeTypeRoot is the root path of the persistent volume.
PodInstanceStateDegraded is when an instance is degraded status.
PodInstanceStatePending is when an instance is pending scheduling.
PodInstanceStateStable is when an instance is stably running.
PodInstanceStateStaging is when an instance is staged to be scheduled.
PodInstanceStateTerminal is when an instance is terminal.
PodStateDegraded is a degraded pod.
PodStateStable is a stable pod.
PodStateTerminal is a terminal pod.
TaskLostBehaviorTypeRelaunchAfterTimeout indicates to try relaunching the lost resident task on another node after the relaunch escalation timeout has elapsed.
TaskLostBehaviorTypeWaitForever indicates to not take any action when the resident task is lost.
UnreachableStrategyAbsenceReasonDisabled signifies the reason of disabled unreachable strategy.

# Variables

ErrMarathonDown is thrown when all the marathon endpoints are down.
ErrNoApplicationContainer is thrown when a container has been specified yet.
ErrTimeoutError is thrown when the operation has timed out.

# Structs

AllTasksOpts contains a payload for AllTasks method status: Return only those tasks whose status matches this parameter.
APIError represents a generic API error.
Application is the definition for an application in marathon.
Applications is a collection of applications.
ApplicationVersion is the application version response from marathon.
ApplicationVersions is a collection of application versions for a specific app in marathon.
Command is the command health check type.
CommandHealthCheck describes a shell-based health check.
Config holds the settings and options for the client.
Constraint describes the constraint for pod placement.
Container is the definition for a container type in marathon.
ContainerStatus contains all status information for a container instance.
ContainerTerminationHistory is the termination history of a container in a pod.
ContainerTerminationState describes why a container terminated.
Delay cotains the application postpone information.
DeleteAppOpts contains a payload for DeleteApplication method force: overrides a currently running deployment.
DeleteGroupOpts contains a payload for DeleteGroup method force: overrides a currently running deployment.
Deployment is a marathon deployment definition.
DeploymentID is the identifier for a application deployment.
DeploymentPlan is a collection of steps for application deployment.
DeploymentStep is a step in the application deployment plan.
Discovery provides info about ports expose by IP-per-task functionality.
Docker is the docker definition from a marathon application.
EnabledUnreachableStrategy covers parameters pertaining to present unreachable strategies.
Event is the definition for a event in marathon.
EventAddHealthCheck describes an 'add_health_check_event' event.
EventAPIRequest describes an 'api_post_event' event.
EventAppTerminated describes an 'app_terminated_event' event.
EventDeploymentFailed describes a 'deployment_failed' event.
EventDeploymentInfo describes a 'deployment_info' event.
EventDeploymentStepFailure describes a 'deployment_step_failure' event.
EventDeploymentStepSuccess describes a 'deployment_step_success' event.
EventDeploymentSuccess describes a 'deployment_success' event.
EventFailedHealthCheck describes a 'failed_health_check_event' event.
EventFrameworkMessage describes a 'framework_message_event' event.
EventGroupChangeFailed describes a 'group_change_failed' event.
EventGroupChangeSuccess describes a 'group_change_success' event.
EventHealthCheckChanged describes a 'health_status_changed_event' event.
EventRemoveHealthCheck describes a 'remove_health_check_event' event.
EventsChannelContext holds contextual data for an EventsChannel.
EventStatusUpdate describes a 'status_update_event' event.
EventStreamAttached describes an 'event_stream_attached' event.
EventStreamDetached describes an 'event_stream_detached' event.
EventSubscription describes a 'subscribe_event' event.
EventType is a wrapper for a marathon event.
EventUnsubscription describes an 'unsubscribe_event' event.
ExecutorResources are the resources supported by an executor (a task running a pod).
ExternalVolume is an external volume definition.
Fetch will download URI before task starts.
GetAppOpts contains a payload for Application method embed: Embeds nested resources that match the supplied path.
GetGroupOpts contains a payload for Group and Groups method embed: Embeds nested resources that match the supplied path.
Group is a marathon application group.
Groups is a collection of marathon application groups.
HealthCheck is the definition for an application health check.
HealthCheckResult is the health check result.
HTTPHealthCheck describes an HTTP based health check.
Info is the detailed stats returned from marathon info.
InvalidEndpointError indicates a endpoint error in the marathon urls.
IPAddress represents a task's IP address and protocol.
IPAddressPerTask is used by IP-per-task functionality https://mesosphere.github.io/marathon/docs/ip-per-task.html.
Item is the definition of element in the queue.
KillApplicationTasksOpts contains a payload for KillApplicationTasks method host: kill only those tasks on a specific host (optional) scale: Scale the app down (i.e.
KillTaskOpts contains a payload for task killing methods scale: Scale the app down.
LastTaskFailure provides details on the last error experienced by an application.
Network provides info about application networking.
Parameters is the parameters to pass to the docker client when creating the container.
PersistentVolume declares a Volume to be Persistent, and sets the size (in MiB) and optional type, max size (MiB) and constraints for the Volume.
Pod is the definition for an pod in marathon.
PodAgentInfo contains info about the agent the instance is running on.
PodArtifact describes how to obtain a generic artifact for a pod.
PodBackoff describes the backoff for re-run attempts of a pod.
PodCommand is the command to run as the entrypoint of the container.
PodContainer describes a container in a pod.
PodContainerImage describes how to retrieve the container image.
PodEndpoint describes an endpoint for a pod's container.
PodExec contains the PodCommand.
PodHealthCheck describes how to determine a pod's health.
PodInstance is the representation of an instance as returned by deleting an instance.
PodInstanceID contains the instance ID.
PodInstanceStateHistory is the pod instance's state.
PodInstanceStatus is the status of a pod instance.
PodLifecycle describes the lifecycle of a pod.
PodNetwork contains network descriptors for a pod.
PodNetworkInfo contains the network info for a task.
PodNetworkStatus is the networks attached to a pod instance.
PodPlacement supports constraining which hosts a pod is placed on.
PodScalingPolicy is the scaling policy of the pod.
PodSchedulingPolicy is the overarching pod scheduling policy.
PodStatus describes the pod status.
PodTask contains the info about the specific task within the instance.
PodTaskCondition contains a string representation of the condition.
PodTaskStatus is the current status of the task.
PodTerminationHistory is the termination history of the pod.
PodUpgrade describes the policy for upgrading a pod in-place.
PodVolume describes a volume on the host.
PodVolumeMount describes how to mount a volume into a task.
Port provides info about ports used by IP-per-task.
PortDefinition is a definition of a port that should be considered part of a resource.
PortMapping is the portmapping structure between container and mesos.
Queue is the definition of marathon queue.
ReadinessCheck represents a readiness check.
ReadinessCheckResult is the result of a readiness check.
ReadinessLastResponse holds the result of the last response embedded in a readiness check result.
Residency defines how terminal states of tasks with local persistent volumes are handled.
Resources are the full set of resources for a task.
Secret is the environment variable and secret store path associated with a secret.
Stats is a collection of aggregate statistics about an application's tasks.
StatusCondition describes info about a status change.
StepActions is a series of deployment steps.
Subscriptions is a collection to urls that marathon is implementing a callback on.
Task is the definition for a marathon task.
Tasks is a collection of marathon tasks.
TaskStats is a container for Stats.
TCPHealthCheck describes a TCP based health check.
TmpEnvSecret holds the secret values deserialized from the environment variables field.
TmpSecret holds the deserialized secrets field in a Marathon application configuration.
UnreachableStrategy is the unreachable strategy applied to an application.
UpdateGroupOpts contains a payload for UpdateGroup method force: overrides a currently running deployment.
UpgradeStrategy is the upgrade strategy applied to an application.
VersionInfo is the application versioning details from marathon.
Volume is the docker volume details associated to the container.

# Interfaces

Marathon is the interface to the marathon API.

# Type aliases

Alias aliases the Application struct so that it will be marshaled/unmarshaled automatically.
EventsTransport describes which transport should be used to deliver Marathon events.
ImageType represents the image format type.
PersistentVolumeType is the a persistent docker volume to be mounted.
PodAlias aliases the Pod struct so that it will be marshaled/unmarshaled automatically.
PodContainerAlias aliases the PodContainer struct so that it will be marshaled/unmarshaled automatically.
PodInstanceState is the state of a specific pod instance.
PodNetworkMode is the mode of a network descriptor.
PodState defines the state of a pod.
TaskLostBehaviorType sets action taken when the resident task is lost.