-
Notifications
You must be signed in to change notification settings - Fork 127
/
group.go
237 lines (211 loc) · 7.17 KB
/
group.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/*
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
}