Brad Ison | 53e997c | 2016-03-26 18:02:05 -0400 | [diff] [blame] | 1 | package policies |
| 2 | |
| 3 | import ( |
Brad Ison | a06e2ca | 2016-04-11 14:59:06 -0400 | [diff] [blame] | 4 | "time" |
| 5 | |
Brad Ison | 53e997c | 2016-03-26 18:02:05 -0400 | [diff] [blame] | 6 | "github.com/mitchellh/mapstructure" |
| 7 | |
| 8 | "github.com/rackspace/gophercloud" |
| 9 | "github.com/rackspace/gophercloud/pagination" |
| 10 | ) |
| 11 | |
| 12 | type policyResult struct { |
| 13 | gophercloud.Result |
| 14 | } |
| 15 | |
Brad Ison | 55523e5 | 2016-04-06 19:25:20 -0400 | [diff] [blame] | 16 | // Extract interprets any policyResult as a Policy, if possible. |
| 17 | func (r policyResult) Extract() (*Policy, error) { |
| 18 | if r.Err != nil { |
| 19 | return nil, r.Err |
| 20 | } |
| 21 | |
| 22 | var response struct { |
Brad Ison | 2b73916 | 2016-04-11 12:40:11 -0400 | [diff] [blame] | 23 | Policy policy `mapstructure:"policy"` |
Brad Ison | 55523e5 | 2016-04-06 19:25:20 -0400 | [diff] [blame] | 24 | } |
| 25 | |
Brad Ison | 2b73916 | 2016-04-11 12:40:11 -0400 | [diff] [blame] | 26 | if err := mapstructure.Decode(r.Body, &response); err != nil { |
| 27 | return nil, err |
| 28 | } |
Brad Ison | 55523e5 | 2016-04-06 19:25:20 -0400 | [diff] [blame] | 29 | |
Brad Ison | 2b73916 | 2016-04-11 12:40:11 -0400 | [diff] [blame] | 30 | policy := response.Policy.toExported() |
| 31 | |
| 32 | return &policy, nil |
Brad Ison | 55523e5 | 2016-04-06 19:25:20 -0400 | [diff] [blame] | 33 | } |
| 34 | |
Brad Ison | e7d6dfc | 2016-04-06 14:55:07 -0400 | [diff] [blame] | 35 | // CreateResult represents the result of a create operation. |
| 36 | type CreateResult struct { |
| 37 | policyResult |
| 38 | } |
| 39 | |
| 40 | // Extract extracts a slice of Policies from a CreateResult. Multiple policies |
| 41 | // can be created in a single operation, so the result of a create is always a |
| 42 | // list of policies. |
| 43 | func (res CreateResult) Extract() ([]Policy, error) { |
| 44 | if res.Err != nil { |
| 45 | return nil, res.Err |
| 46 | } |
| 47 | |
| 48 | return commonExtractPolicies(res.Body) |
| 49 | } |
| 50 | |
Brad Ison | 55523e5 | 2016-04-06 19:25:20 -0400 | [diff] [blame] | 51 | // GetResult temporarily contains the response from a Get call. |
| 52 | type GetResult struct { |
| 53 | policyResult |
| 54 | } |
| 55 | |
Brad Ison | ac037d5 | 2016-04-07 19:41:29 -0400 | [diff] [blame] | 56 | // UpdateResult represents the result of an update operation. |
| 57 | type UpdateResult struct { |
| 58 | gophercloud.ErrResult |
| 59 | } |
| 60 | |
Brad Ison | 124df8e | 2016-04-07 19:51:51 -0400 | [diff] [blame] | 61 | // DeleteResult represents the result of a delete operation. |
| 62 | type DeleteResult struct { |
| 63 | gophercloud.ErrResult |
| 64 | } |
| 65 | |
Brad Ison | 42f8dfb | 2016-04-07 20:26:06 -0400 | [diff] [blame] | 66 | // ExecuteResult represents the result of an execute operation. |
| 67 | type ExecuteResult struct { |
| 68 | gophercloud.ErrResult |
| 69 | } |
| 70 | |
Brad Ison | 53e997c | 2016-03-26 18:02:05 -0400 | [diff] [blame] | 71 | // Type represents a type of scaling policy. |
| 72 | type Type string |
| 73 | |
| 74 | const ( |
| 75 | // Schedule policies run at given times. |
| 76 | Schedule Type = "schedule" |
| 77 | |
| 78 | // Webhook policies are triggered by HTTP requests. |
| 79 | Webhook Type = "webhook" |
| 80 | ) |
| 81 | |
Brad Ison | 2b73916 | 2016-04-11 12:40:11 -0400 | [diff] [blame] | 82 | // AdjustmentType represents the way in which a policy will change a group. |
| 83 | type AdjustmentType string |
| 84 | |
| 85 | // Valid types of adjustments for a policy. |
| 86 | const ( |
| 87 | Change AdjustmentType = "change" |
| 88 | ChangePercent AdjustmentType = "changePercent" |
| 89 | DesiredCapacity AdjustmentType = "desiredCapacity" |
| 90 | ) |
| 91 | |
Brad Ison | a06e2ca | 2016-04-11 14:59:06 -0400 | [diff] [blame] | 92 | // ScheduleArgs is implemented by types that can be converted into arguments for |
| 93 | // policies with type Schedule. |
| 94 | type ScheduleArgs interface { |
| 95 | ToPolicyArgs() (map[string]string, error) |
| 96 | } |
| 97 | |
| 98 | // At satisfies the ScheduleArgs interface and can be used to configure a policy |
| 99 | // to execute a particular time. |
| 100 | type At time.Time |
| 101 | |
| 102 | // ToPolicyArgs returns a key and value for use in constructing arguments to |
| 103 | // schedule policies. |
| 104 | func (at At) ToPolicyArgs() (map[string]string, error) { |
| 105 | t := time.Time(at) |
| 106 | |
| 107 | args := make(map[string]string) |
| 108 | args["at"] = t.UTC().Format(time.RFC3339) |
| 109 | |
| 110 | return args, nil |
| 111 | } |
| 112 | |
| 113 | // Cron satisfies the ScheduleArgs interface and can be used to configure a |
| 114 | // policy that executes at regular intervals. |
| 115 | type Cron string |
| 116 | |
| 117 | // ToPolicyArgs returns a key and value for use in constructing arguments to |
| 118 | // schedule policies. |
| 119 | func (cron Cron) ToPolicyArgs() (map[string]string, error) { |
| 120 | if cron == "" { |
| 121 | return nil, ErrEmptyCron |
| 122 | } |
| 123 | |
| 124 | args := make(map[string]string) |
| 125 | args["cron"] = string(cron) |
| 126 | |
| 127 | return args, nil |
| 128 | } |
| 129 | |
Brad Ison | 2b73916 | 2016-04-11 12:40:11 -0400 | [diff] [blame] | 130 | // Policy represents a scaling policy. |
| 131 | type Policy struct { |
| 132 | // UUID for the policy. |
| 133 | ID string |
| 134 | |
| 135 | // Name of the policy. |
| 136 | Name string |
| 137 | |
| 138 | // Type of scaling policy. |
| 139 | Type Type |
| 140 | |
| 141 | // Cooldown period, in seconds. |
| 142 | Cooldown int |
| 143 | |
| 144 | // The type of adjustment in capacity to be made. |
| 145 | AdjustmentType AdjustmentType |
| 146 | |
| 147 | // The numeric value of the adjustment in capacity. |
| 148 | AdjustmentValue float64 |
| 149 | |
Brad Ison | a06e2ca | 2016-04-11 14:59:06 -0400 | [diff] [blame] | 150 | // Arguments determining Schedule policy behavior, or nil for Webhook |
| 151 | // policies. |
| 152 | Schedule ScheduleArgs |
Brad Ison | 2b73916 | 2016-04-11 12:40:11 -0400 | [diff] [blame] | 153 | } |
| 154 | |
| 155 | // This is an intermediate representation of the exported Policy type. The |
| 156 | // fields in API responses vary by policy type and configuration. This lets us |
| 157 | // decode responses then normalize them into a Policy. |
| 158 | type policy struct { |
| 159 | ID string `mapstructure:"id"` |
| 160 | Name string `mapstructure:"name"` |
| 161 | Type Type `mapstructure:"type"` |
| 162 | Cooldown int `mapstructure:"cooldown"` |
| 163 | |
| 164 | // The API will respond with exactly one of these omitting the others. |
| 165 | Change interface{} `mapstructure:"change"` |
| 166 | ChangePercent interface{} `mapstructure:"changePercent"` |
| 167 | DesiredCapacity interface{} `mapstructure:"desiredCapacity"` |
| 168 | |
| 169 | // Additional configuration options for schedule policies. |
Brad Ison | a06e2ca | 2016-04-11 14:59:06 -0400 | [diff] [blame] | 170 | Args map[string]string `mapstructure:"args"` |
Brad Ison | 2b73916 | 2016-04-11 12:40:11 -0400 | [diff] [blame] | 171 | } |
| 172 | |
| 173 | // Assemble a Policy from the intermediate policy struct. |
| 174 | func (p policy) toExported() Policy { |
| 175 | policy := Policy{} |
| 176 | |
| 177 | policy.ID = p.ID |
| 178 | policy.Name = p.Name |
| 179 | policy.Type = p.Type |
| 180 | policy.Cooldown = p.Cooldown |
| 181 | |
Brad Ison | a06e2ca | 2016-04-11 14:59:06 -0400 | [diff] [blame] | 182 | if cron, ok := p.Args["cron"]; ok { |
| 183 | policy.Schedule = Cron(cron) |
| 184 | } else if at, ok := p.Args["at"]; ok { |
| 185 | // Set an At schedule if the "at" argument parses as an RFC3339 time. |
| 186 | if t, err := time.Parse(time.RFC3339, at); err == nil { |
| 187 | policy.Schedule = At(t) |
| 188 | } |
| 189 | } |
Brad Ison | 2b73916 | 2016-04-11 12:40:11 -0400 | [diff] [blame] | 190 | |
| 191 | if v, ok := p.Change.(float64); ok { |
| 192 | policy.AdjustmentType = Change |
| 193 | policy.AdjustmentValue = v |
| 194 | } else if v, ok := p.ChangePercent.(float64); ok { |
| 195 | policy.AdjustmentType = ChangePercent |
| 196 | policy.AdjustmentValue = v |
| 197 | } else if v, ok := p.DesiredCapacity.(float64); ok { |
| 198 | policy.AdjustmentType = DesiredCapacity |
| 199 | policy.AdjustmentValue = v |
| 200 | } |
| 201 | |
| 202 | return policy |
| 203 | } |
| 204 | |
Brad Ison | 53e997c | 2016-03-26 18:02:05 -0400 | [diff] [blame] | 205 | // PolicyPage is the page returned by a pager when traversing over a collection |
| 206 | // of scaling policies. |
| 207 | type PolicyPage struct { |
| 208 | pagination.SinglePageBase |
| 209 | } |
| 210 | |
| 211 | // IsEmpty returns true if a page contains no Policy results. |
| 212 | func (page PolicyPage) IsEmpty() (bool, error) { |
| 213 | policies, err := ExtractPolicies(page) |
| 214 | |
| 215 | if err != nil { |
| 216 | return true, err |
| 217 | } |
| 218 | |
| 219 | return len(policies) == 0, nil |
| 220 | } |
| 221 | |
| 222 | // ExtractPolicies interprets the results of a single page from a List() call, |
| 223 | // producing a slice of Policies. |
| 224 | func ExtractPolicies(page pagination.Page) ([]Policy, error) { |
Brad Ison | e7d6dfc | 2016-04-06 14:55:07 -0400 | [diff] [blame] | 225 | return commonExtractPolicies(page.(PolicyPage).Body) |
| 226 | } |
Brad Ison | 53e997c | 2016-03-26 18:02:05 -0400 | [diff] [blame] | 227 | |
Brad Ison | e7d6dfc | 2016-04-06 14:55:07 -0400 | [diff] [blame] | 228 | func commonExtractPolicies(body interface{}) ([]Policy, error) { |
Brad Ison | 53e997c | 2016-03-26 18:02:05 -0400 | [diff] [blame] | 229 | var response struct { |
Brad Ison | 2b73916 | 2016-04-11 12:40:11 -0400 | [diff] [blame] | 230 | Policies []policy `mapstructure:"policies"` |
Brad Ison | 53e997c | 2016-03-26 18:02:05 -0400 | [diff] [blame] | 231 | } |
| 232 | |
Brad Ison | e7d6dfc | 2016-04-06 14:55:07 -0400 | [diff] [blame] | 233 | err := mapstructure.Decode(body, &response) |
Brad Ison | 53e997c | 2016-03-26 18:02:05 -0400 | [diff] [blame] | 234 | |
| 235 | if err != nil { |
| 236 | return nil, err |
| 237 | } |
| 238 | |
Brad Ison | 2b73916 | 2016-04-11 12:40:11 -0400 | [diff] [blame] | 239 | policies := make([]Policy, len(response.Policies)) |
| 240 | |
| 241 | for i, p := range response.Policies { |
| 242 | policies[i] = p.toExported() |
| 243 | } |
| 244 | |
| 245 | return policies, nil |
Brad Ison | 53e997c | 2016-03-26 18:02:05 -0400 | [diff] [blame] | 246 | } |