File: requests.go

package info (click to toggle)
golang-github-rackspace-gophercloud 1.0.0%2Bgit20161013.1012.e00690e8-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 5,148 kB
  • ctags: 6,414
  • sloc: sh: 16; makefile: 6
file content (302 lines) | stat: -rw-r--r-- 7,794 bytes parent folder | download | duplicates (2)
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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
package policies

import (
	"errors"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"
)

// Validation errors returned by create or update operations.
var (
	ErrNoName            = errors.New("Policy name cannot be empty.")
	ErrNoSchedule        = errors.New("Schedule cannot be nil for schedule policies.")
	ErrCooldownRange     = errors.New("Cooldown is out of range (0, 86400).")
	ErrUnknownType       = errors.New("Unknown policy type.")
	ErrUnknownAdjustment = errors.New("Unknown adjustment type.")
	ErrEmptyCron         = errors.New("Cron argument cannot be empty.")
)

// List returns all scaling policies for a group.
func List(client *gophercloud.ServiceClient, groupID string) pagination.Pager {
	url := listURL(client, groupID)

	createPageFn := func(r pagination.PageResult) pagination.Page {
		return PolicyPage{pagination.SinglePageBase(r)}
	}

	return pagination.NewPager(client, url, createPageFn)
}

// CreateOptsBuilder is the interface responsible for generating the map that
// will be marshalled to JSON for a Create operation.
type CreateOptsBuilder interface {
	ToPolicyCreateMap() ([]map[string]interface{}, error)
}

// CreateOpts is a slice of CreateOpt structs that allow the user to create
// multiple policies in a single operation.
type CreateOpts []CreateOpt

// CreateOpt represents the options to create a policy.
type CreateOpt struct {
	// Name [required] is a name for the policy.
	Name string

	// Type [required] of policy, i.e. either "webhook" or "schedule".
	Type Type

	// Cooldown [required] period in seconds.
	Cooldown int

	// AdjustmentType [requried] is the method used to change the capacity of
	// the group, i.e. one of: Change, ChangePercent, or DesiredCapacity.
	AdjustmentType AdjustmentType

	// AdjustmentValue [required] is the numeric value of the adjustment.  For
	// adjustments of type Change or DesiredCapacity, this will be converted to
	// an integer.
	AdjustmentValue float64

	// Value determining Schedule policy behavior, or nil for Webhook policies.
	// This should be an appropriately configured Cron or an At value.
	Schedule ScheduleArgs
}

// ToPolicyCreateMap converts a slice of CreateOpt structs into a map for use
// in the request body of a Create operation.
func (opts CreateOpts) ToPolicyCreateMap() ([]map[string]interface{}, error) {
	var policies []map[string]interface{}

	for _, o := range opts {
		if o.Name == "" {
			return nil, ErrNoName
		}

		if o.Type == Schedule && o.Schedule == nil {
			return nil, ErrNoSchedule
		}

		if ok := validateType(o.Type); !ok {
			return nil, ErrUnknownType
		}

		if ok := validateCooldown(o.Cooldown); !ok {
			return nil, ErrCooldownRange
		}

		policy := make(map[string]interface{})

		policy["name"] = o.Name
		policy["type"] = o.Type
		policy["cooldown"] = o.Cooldown

		err := setAdjustment(o.AdjustmentType, o.AdjustmentValue, policy)

		if err != nil {
			return nil, err
		}

		if o.Schedule != nil {
			args, err := o.Schedule.ToPolicyArgs()

			if err != nil {
				return nil, err
			}

			policy["args"] = args
		}

		policies = append(policies, policy)
	}

	return policies, nil
}

// Create requests a new policy be created and associated with the given group.
func Create(client *gophercloud.ServiceClient, groupID string, opts CreateOptsBuilder) CreateResult {
	var res CreateResult

	reqBody, err := opts.ToPolicyCreateMap()

	if err != nil {
		res.Err = err
		return res
	}

	_, res.Err = client.Post(createURL(client, groupID), reqBody, &res.Body, nil)

	return res
}

// Get requests the details of a single policy with the given ID.
func Get(client *gophercloud.ServiceClient, groupID, policyID string) GetResult {
	var result GetResult

	_, result.Err = client.Get(getURL(client, groupID, policyID), &result.Body, nil)

	return result
}

// UpdateOptsBuilder is the interface responsible for generating the map
// structure for producing JSON for an Update operation.
type UpdateOptsBuilder interface {
	ToPolicyUpdateMap() (map[string]interface{}, error)
}

// UpdateOpts represents the options for updating an existing policy.
//
// Update operations completely replace the configuration being updated. Empty
// values in the update are accepted and overwrite previously specified
// parameters.
type UpdateOpts struct {
	// Name [required] is a name for the policy.
	Name string

	// Type [required] of policy, i.e. either "webhook" or "schedule".
	Type Type

	// Cooldown [required] period in seconds.  If you don't specify a cooldown,
	// it will default to zero, and the policy will be configured as such.
	Cooldown int

	// AdjustmentType [requried] is the method used to change the capacity of
	// the group, i.e. one of: Change, ChangePercent, or DesiredCapacity.
	AdjustmentType AdjustmentType

	// AdjustmentValue [required] is the numeric value of the adjustment.  For
	// adjustments of type Change or DesiredCapacity, this will be converted to
	// an integer.
	AdjustmentValue float64

	// Value determining Schedule policy behavior, or nil for Webhook policies.
	// This should be an appropriately configured Cron or an At value.
	Schedule ScheduleArgs
}

// ToPolicyUpdateMap converts an UpdateOpts struct into a map for use as the
// request body in an Update request.
func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) {
	if opts.Name == "" {
		return nil, ErrNoName
	}

	if opts.Type == Schedule && opts.Schedule == nil {
		return nil, ErrNoSchedule
	}

	if ok := validateType(opts.Type); !ok {
		return nil, ErrUnknownType
	}

	if ok := validateCooldown(opts.Cooldown); !ok {
		return nil, ErrCooldownRange
	}

	policy := make(map[string]interface{})

	policy["name"] = opts.Name
	policy["type"] = opts.Type
	policy["cooldown"] = opts.Cooldown

	err := setAdjustment(opts.AdjustmentType, opts.AdjustmentValue, policy)

	if err != nil {
		return nil, err
	}

	if opts.Schedule != nil {
		args, err := opts.Schedule.ToPolicyArgs()

		if err != nil {
			return nil, err
		}

		policy["args"] = args
	}

	return policy, nil
}

// Update requests the configuration of the given policy be updated.
func Update(client *gophercloud.ServiceClient, groupID, policyID string, opts UpdateOptsBuilder) UpdateResult {
	var result UpdateResult

	url := updateURL(client, groupID, policyID)
	reqBody, err := opts.ToPolicyUpdateMap()

	if err != nil {
		result.Err = err
		return result
	}

	_, result.Err = client.Put(url, reqBody, nil, &gophercloud.RequestOpts{
		OkCodes: []int{204},
	})

	return result
}

// Delete requests the given policy be permanently deleted.
func Delete(client *gophercloud.ServiceClient, groupID, policyID string) DeleteResult {
	var result DeleteResult

	url := deleteURL(client, groupID, policyID)
	_, result.Err = client.Delete(url, &gophercloud.RequestOpts{
		OkCodes: []int{204},
	})

	return result
}

// Execute requests the given policy be executed immediately.
func Execute(client *gophercloud.ServiceClient, groupID, policyID string) ExecuteResult {
	var result ExecuteResult

	url := executeURL(client, groupID, policyID)
	_, result.Err = client.Post(url, nil, &result.Body, &gophercloud.RequestOpts{
		OkCodes: []int{202},
	})

	return result
}

// Validate and set an adjustment on the given request body.
func setAdjustment(t AdjustmentType, v float64, body map[string]interface{}) error {
	key := string(t)

	switch t {
	case ChangePercent:
		body[key] = v

	case Change, DesiredCapacity:
		body[key] = int(v)

	default:
		return ErrUnknownAdjustment
	}

	return nil
}

func validateType(t Type) (ok bool) {
	switch t {
	case Schedule, Webhook:
		ok = true
		return

	default:
		ok = false
		return
	}
}

func validateCooldown(cooldown int) (ok bool) {
	if cooldown < 0 || cooldown > 86400 {
		ok = false
		return
	}

	ok = true
	return
}