237 lines
7.2 KiB
Go
237 lines
7.2 KiB
Go
/*
|
|
Copyright 2014 The go-marathon Authors All rights reserved.
|
|
|
|
Licensed 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 marathon
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// Group is a marathon application group
|
|
type Group struct {
|
|
ID string `json:"id"`
|
|
Apps []*Application `json:"apps"`
|
|
Dependencies []string `json:"dependencies"`
|
|
Groups []*Group `json:"groups"`
|
|
}
|
|
|
|
// Groups is a collection of marathon application groups
|
|
type Groups struct {
|
|
ID string `json:"id"`
|
|
Apps []*Application `json:"apps"`
|
|
Dependencies []string `json:"dependencies"`
|
|
Groups []*Group `json:"groups"`
|
|
}
|
|
|
|
// GetGroupOpts contains a payload for Group and Groups method
|
|
// embed: Embeds nested resources that match the supplied path.
|
|
// You can specify this parameter multiple times with different values
|
|
type GetGroupOpts struct {
|
|
Embed []string `url:"embed,omitempty"`
|
|
}
|
|
|
|
// DeleteGroupOpts contains a payload for DeleteGroup method
|
|
// force: overrides a currently running deployment.
|
|
type DeleteGroupOpts struct {
|
|
Force bool `url:"force,omitempty"`
|
|
}
|
|
|
|
// UpdateGroupOpts contains a payload for UpdateGroup method
|
|
// force: overrides a currently running deployment.
|
|
type UpdateGroupOpts struct {
|
|
Force bool `url:"force,omitempty"`
|
|
}
|
|
|
|
// NewApplicationGroup create a new application group
|
|
// name: the name of the group
|
|
func NewApplicationGroup(name string) *Group {
|
|
return &Group{
|
|
ID: name,
|
|
Apps: make([]*Application, 0),
|
|
Dependencies: make([]string, 0),
|
|
Groups: make([]*Group, 0),
|
|
}
|
|
}
|
|
|
|
// Name sets the name of the group
|
|
// name: the name of the group
|
|
func (r *Group) Name(name string) *Group {
|
|
r.ID = validateID(name)
|
|
return r
|
|
}
|
|
|
|
// App add a application to the group in question
|
|
// application: a pointer to the Application
|
|
func (r *Group) App(application *Application) *Group {
|
|
if r.Apps == nil {
|
|
r.Apps = make([]*Application, 0)
|
|
}
|
|
r.Apps = append(r.Apps, application)
|
|
return r
|
|
}
|
|
|
|
// Groups retrieves a list of all the groups from marathon
|
|
func (r *marathonClient) Groups() (*Groups, error) {
|
|
groups := new(Groups)
|
|
if err := r.apiGet(marathonAPIGroups, "", groups); err != nil {
|
|
return nil, err
|
|
}
|
|
return groups, nil
|
|
}
|
|
|
|
// Group retrieves the configuration of a specific group from marathon
|
|
// name: the identifier for the group
|
|
func (r *marathonClient) Group(name string) (*Group, error) {
|
|
group := new(Group)
|
|
if err := r.apiGet(fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name)), nil, group); err != nil {
|
|
return nil, err
|
|
}
|
|
return group, nil
|
|
}
|
|
|
|
// GroupsBy retrieves a list of all the groups from marathon by embed options
|
|
// opts: GetGroupOpts request payload
|
|
func (r *marathonClient) GroupsBy(opts *GetGroupOpts) (*Groups, error) {
|
|
path, err := addOptions(marathonAPIGroups, opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
groups := new(Groups)
|
|
if err := r.apiGet(path, "", groups); err != nil {
|
|
return nil, err
|
|
}
|
|
return groups, nil
|
|
}
|
|
|
|
// GroupBy retrieves the configuration of a specific group from marathon
|
|
// name: the identifier for the group
|
|
// opts: GetGroupOpts request payload
|
|
func (r *marathonClient) GroupBy(name string, opts *GetGroupOpts) (*Group, error) {
|
|
path, err := addOptions(fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name)), opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
group := new(Group)
|
|
if err := r.apiGet(path, nil, group); err != nil {
|
|
return nil, err
|
|
}
|
|
return group, nil
|
|
}
|
|
|
|
// HasGroup checks if the group exists in marathon
|
|
// name: the identifier for the group
|
|
func (r *marathonClient) HasGroup(name string) (bool, error) {
|
|
path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
|
|
err := r.apiGet(path, "", nil)
|
|
if err != nil {
|
|
if apiErr, ok := err.(*APIError); ok && apiErr.ErrCode == ErrCodeNotFound {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// CreateGroup creates a new group in marathon
|
|
// group: a pointer the Group structure defining the group
|
|
func (r *marathonClient) CreateGroup(group *Group) error {
|
|
return r.apiPost(marathonAPIGroups, group, nil)
|
|
}
|
|
|
|
// WaitOnGroup waits for all the applications in a group to be deployed
|
|
// group: the identifier for the group
|
|
// timeout: a duration of time to wait before considering it failed (all tasks in all apps running defined as deployed)
|
|
func (r *marathonClient) WaitOnGroup(name string, timeout time.Duration) error {
|
|
err := deadline(timeout, func(stop_channel chan bool) error {
|
|
var flick atomicSwitch
|
|
go func() {
|
|
<-stop_channel
|
|
close(stop_channel)
|
|
flick.SwitchOn()
|
|
}()
|
|
for !flick.IsSwitched() {
|
|
if group, err := r.Group(name); err != nil {
|
|
continue
|
|
} else {
|
|
allRunning := true
|
|
// for each of the application, check if the tasks and running
|
|
for _, appID := range group.Apps {
|
|
// Arrrgghhh!! .. so we can't use application instances from the Application struct like with app wait on as it
|
|
// appears the instance count is not set straight away!! .. it defaults to zero and changes probably at the
|
|
// dependencies gets deployed. Which is probably how it internally handles dependencies ..
|
|
// step: grab the application
|
|
application, err := r.Application(appID.ID)
|
|
if err != nil {
|
|
allRunning = false
|
|
break
|
|
}
|
|
|
|
if application.Tasks == nil {
|
|
allRunning = false
|
|
} else if len(application.Tasks) != *appID.Instances {
|
|
allRunning = false
|
|
} else if application.TasksRunning != *appID.Instances {
|
|
allRunning = false
|
|
} else if len(application.DeploymentIDs()) > 0 {
|
|
allRunning = false
|
|
}
|
|
}
|
|
// has anyone toggle the flag?
|
|
if allRunning {
|
|
return nil
|
|
}
|
|
}
|
|
time.Sleep(r.config.PollingWaitTime)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
// DeleteGroup deletes a group from marathon
|
|
// name: the identifier for the group
|
|
// force: used to force the delete operation in case of blocked deployment
|
|
func (r *marathonClient) DeleteGroup(name string, force bool) (*DeploymentID, error) {
|
|
version := new(DeploymentID)
|
|
path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
|
|
if force {
|
|
path += "?force=true"
|
|
}
|
|
if err := r.apiDelete(path, nil, version); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return version, nil
|
|
}
|
|
|
|
// UpdateGroup updates the parameters of a groups
|
|
// name: the identifier for the group
|
|
// group: the group structure with the new params
|
|
// force: used to force the update operation in case of blocked deployment
|
|
func (r *marathonClient) UpdateGroup(name string, group *Group, force bool) (*DeploymentID, error) {
|
|
deploymentID := new(DeploymentID)
|
|
path := fmt.Sprintf("%s/%s", marathonAPIGroups, trimRootPath(name))
|
|
if force {
|
|
path += "?force=true"
|
|
}
|
|
if err := r.apiPut(path, group, deploymentID); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return deploymentID, nil
|
|
}
|