Skip to content

Commit ec17088

Browse files
authored
Merge pull request #52 from cloudfoundry/develop
Merge Develop
2 parents 09844b8 + 6d9e10f commit ec17088

File tree

13 files changed

+1807
-881
lines changed

13 files changed

+1807
-881
lines changed

README.md

Lines changed: 77 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ cf uninstall-plugin AutoScaler
3737
| [autoscaling-policy, asp](#cf-autoscaling-policy) | Retrieve the scaling policy of an application |
3838
| [attach-autoscaling-policy, aasp](#cf-attach-autoscaling-policy) | Attach a scaling policy to an application |
3939
| [detach-autoscaling-policy, dasp](#cf-detach-autoscaling-policy) | Detach the scaling policy from an application |
40+
| [create-autoscaling-credential, casc](#cf-create-autoscaling-credential) | Create custom metric credential for an application |
41+
| [delete-autoscaling-credential, dasc](#cf-delete-autoscaling-credential) | Delete the custom metric credential of an application |
4042
| [autoscaling-metrics, asm](#cf-autoscaling-metrics) | Retrieve the metrics of an application |
4143
| [autoscaling-history, ash](#cf-autoscaling-history) | Retrieve the scaling history of an application|
4244

@@ -139,13 +141,13 @@ Showing policy for app APP_NAME...
139141
```
140142
$ cf asp APP_NAME --output PATH_TO_FILE
141143
142-
Showing policy for app APP_NAME...
144+
Saving policy for app APP_NAME to PATH_TO_FILE...
143145
OK
144146
```
145147

146148
### `cf attach-autoscaling-policy`
147149

148-
Attach a scaling policy to an application, the policy file must be a JSON file, refer to [policy specification](https://github.com/cloudfoundry-incubator/blob/master/docs/policy.md) for the policy format.
150+
Attach a scaling policy to an application, the policy file must be a JSON file, refer to [policy specification](https://github.com/cloudfoundry/app-autoscaler/blob/develop/docs/policy.md) for the policy format.
149151

150152
```
151153
cf attach-autoscaling-policy APP_NAME PATH_TO_POLICY_FILE
@@ -167,7 +169,7 @@ OK
167169
Detach the scaling policy from an application, the policy will be **deleted** when detached.
168170

169171
```
170-
cf detach-as-policy APP_NAME
172+
cf detach-autoscaling-policy APP_NAME
171173
```
172174
#### ALIAS: dasp
173175

@@ -180,27 +182,87 @@ OK
180182
```
181183

182184

185+
### `cf create-autoscaling-credential`
186+
187+
Credential is required when submitting custom metrics to app-autoscaler. If an application is connecting to autoscaler through a service binding approach, the required credential could be found in Cloud Foundry `VCAP_SERVICES` environment variables. Otherwise, you need to generate the required credential explicitly with this command.
188+
189+
The command will generate autoscaler credential and display it in JSON format. Then you need to set this credential to your application through environment variables or user-provided-service.
190+
191+
Note: Auto-scaler only grants access with the most recent credential, so the newly generated credential will overwritten the old pairs. Please make sure to update the credential setting in your application once you launch the command `create-autoscaling-credential`.
192+
193+
Random credential pair will be created by default when username and password are not specified by `--username` and `--password` option.
194+
195+
```
196+
cf create-autoscaling-credential APP_NAME [--username USERNAME --password PASSWORD] [--output PATH_TO_FILE]
197+
```
198+
#### ALIAS: casc
199+
200+
201+
#### OPTIONS:
202+
- `--username, -u` : username of the custom metric credential, random username will be set if not specified
203+
- `--password, -p` : password of the custom metric credential, random password will be set if not specified
204+
- `--output` : Dump the credential to a file in JSON format
205+
206+
#### EXAMPLES:
207+
- Create and view custom credential with user-defined username and password:
208+
```
209+
$ cf create-autoscaling-credential APP_NAME --username MY_USERNAME --password MY_PASSWORD
210+
211+
Creating custom metric credential for app APP_NAME...
212+
{
213+
"app_id": "<APP_ID>",
214+
"username": "MY_USERNAME",
215+
"password": "MY_PASSWORD",
216+
"url": "https://autoscalermetrics.<DOMAIN>"
217+
}
218+
```
219+
- Create random username and password and dump the credential to a file:
220+
```
221+
$ cf create-autoscaling-credential APP_NAME --output PATH_TO_FILE
222+
223+
Saving new created credential for app APP_NAME to PATH_TO_FILE...
224+
OK
225+
```
226+
227+
228+
### `cf delete-autoscaling-credential`
229+
230+
Delete the custom metric credential of an application.
231+
232+
```
233+
cf delete-autoscaling-credential APP_NAME
234+
```
235+
#### ALIAS: dasc
236+
237+
#### EXAMPLES:
238+
```
239+
$ cf delete-autoscaling-credential APP_NAME
240+
241+
Deleting custom metric credential for app APP_NAME...
242+
OK
243+
```
244+
245+
183246
### `cf autoscaling-metrics`
184247

185-
Retrieve the aggregated metrics of an application. You can specify the start/end time or the number of the returned query result, and the display order(ascending or descending). The metrics will be shown in a table.
248+
Retrieve the aggregated metrics of an application. You can specify the start/end time of the returned query result, and the display order(ascending or descending). The metrics will be shown in a table.
186249

187250
```
188-
cf autoscaling-metrics APP_NAME METRIC_NAME [--number RECORD_NUMBER] [--start START_TIME] [--end END_TIME] [--desc] [--output PATH_TO_FILE]
251+
cf autoscaling-metrics APP_NAME METRIC_NAME [--start START_TIME] [--end END_TIME] [--asc] [--output PATH_TO_FILE]
189252
```
190253
#### ALIAS: asm
191254

192255

193256
#### OPTIONS:
194-
- `METRIC_NAME` : available metric supported: memoryused, memoryutil, responsetime, throughput and cpu.
257+
- `METRIC_NAME` : default metrics "memoryused, memoryutil, responsetime, throughput, cpu" or customized name for your own metrics.
195258
- `--start` : start time of metrics collected with format `yyyy-MM-ddTHH:mm:ss+/-HH:mm` or `yyyy-MM-ddTHH:mm:ssZ`, default to very beginning if not specified.
196259
- `--end` : end time of the metrics collected with format `yyyy-MM-ddTHH:mm:ss+/-HH:mm` or `yyyy-MM-ddTHH:mm:ssZ`, default to current time if not speficied.
197-
- `--number|-n` : the number of the records to return, will be ignored if both start time and end time are specified.
198-
- `--desc` : display in descending order, default to ascending order if not specified
260+
- `--asc` : display in ascending order, default to descending order if not specified
199261
- `--output` : dump the metrics to a file
200262

201263
#### EXAMPLES:
202264
```
203-
$ cf autoscaling-metrics APP_NAME memoryused --start 2018-12-27T11:49:00+08:00 --end 2018-12-27T11:52:20+08:00 --desc
265+
$ cf autoscaling-metrics APP_NAME memoryused --start 2018-12-27T11:49:00+08:00 --end 2018-12-27T11:52:20+08:00 --asc
204266
205267
Retriving aggregated metrics for app APP_NAME...
206268
Metrics Name Value Timestamp
@@ -216,29 +278,28 @@ memoryused 62MB 2018-12-27T11:51:40+08:00
216278

217279
### `cf autoscaling-history`
218280

219-
Retrieve the scaling event history of an application. You can specify the start/end time or the number of the returned query result, and the display order(ascending or descending). The scaling event history will be shown in a table.
281+
Retrieve the scaling event history of an application. You can specify the start/end time of the returned query result, and the display order(ascending or descending). The scaling event history will be shown in a table.
220282
```
221-
cf autoscaling-history APP_NAME [--number RECORD_NUMBER] [--start START_TIME] [--end END_TIME] [--desc] [--output PATH_TO_FILE]
283+
cf autoscaling-history APP_NAME [--start START_TIME] [--end END_TIME] [--asc] [--output PATH_TO_FILE]
222284
```
223285

224286
#### ALIAS: ash
225287

226288
#### OPTIONS:
227289
- `--start` : start time of the scaling history with format `yyyy-MM-ddTHH:mm:ss+/-HH:mm` or `yyyy-MM-ddTHH:mm:ssZ`, default to very beginning if not specified.
228290
- `--end` : end time of the scaling history with format `yyyy-MM-ddTHH:mm:ss+/-HH:mm` or `yyyy-MM-ddTHH:mm:ssZ`, default to current time if not speficied.
229-
- `--number|-n` : the number of the records to return, will be ignored if both start time and end time are specified.
230-
- `--desc` : display in descending order, default to ascending order if not specified
291+
- `--asc` : display in ascending order, default to descending order if not specified
231292
- `--output` : dump the scaling history to a file
232293

233294
#### EXAMPLES:
234295
```
235-
$ cf autoscaling-history APP_NAME --start 2018-08-16T17:58:53+08:00 --end 2018-08-16T18:01:00+08:00 --number 3 --desc
296+
$ cf autoscaling-history APP_NAME --start 2018-08-16T17:58:53+08:00 --end 2018-08-16T18:01:00+08:00 --asc
236297
237298
Showing history for app APP_NAME...
238299
Scaling Type Status Instance Changes Time Action Error
239-
scheduled succeeded 3->6 2018-08-16T18:00:00+08:00 3 instance(s) because limited by min instances 6
240-
dynamic succeeded 2->3 2018-08-16T17:59:33+08:00 +1 instance(s) because memoryused >= 15MB for 120 seconds
241300
dynamic failed 2->-1 2018-08-16T17:58:53+08:00 -1 instance(s) because throughput < 10rps for 120 seconds app does not have policy set
301+
dynamic succeeded 2->3 2018-08-16T17:59:33+08:00 +1 instance(s) because memoryused >= 15MB for 120 seconds
302+
scheduled succeeded 3->6 2018-08-16T18:00:00+08:00 3 instance(s) because limited by min instances 6
242303
```
243304
- `Scaling Type`: the trigger type of the scaling action, possible scaling types: `dynamic` and `scheduled`
244305
- `dynamic`: the scaling action is triggered by a dynamic rule (memoryused, memoryutil, responsetime or throughput)

src/cli/api/apihelper.go

Lines changed: 139 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
const (
2929
HealthPath = "/health"
3030
PolicyPath = "/v1/apps/{appId}/policy"
31+
CredentialPath = "/v1/apps/{appId}/credential"
3132
AggregatedMetricPath = "/v1/apps/{appId}/aggregated_metric_histories/{metric_type}"
3233
HistoryPath = "/v1/apps/{appId}/scaling_histories"
3334
)
@@ -95,16 +96,24 @@ func (helper *APIHelper) DoRequest(req *http.Request) (*http.Response, error) {
9596

9697
}
9798

98-
func parseErrResponse(raw []byte) string {
99-
100-
var f interface{}
101-
err := json.Unmarshal(raw, &f)
102-
if err != nil {
103-
return string(raw)
99+
func parseErrArrayResponse(a []interface{}) string {
100+
retMsg := ""
101+
for _, entry := range a {
102+
mentry := entry.(map[string]interface{})
103+
var context, description string
104+
for ik, iv := range mentry {
105+
if ik == "context" {
106+
context = iv.(string)
107+
} else if ik == "description" {
108+
description,_ = strconv.Unquote(strings.Replace(strconv.Quote(iv.(string)), `\\u`, `\u`, -1))
109+
}
110+
}
111+
retMsg = retMsg + "\n" + fmt.Sprintf("%v: %v", context, description)
104112
}
113+
return retMsg
114+
}
105115

106-
m := f.(map[string]interface{})
107-
116+
func parseErrObjectResponse(m map[string]interface{}) string {
108117
retMsg := ""
109118
for k, v := range m {
110119
if k == "error" {
@@ -131,12 +140,31 @@ func parseErrResponse(raw []byte) string {
131140
retMsg = fmt.Sprintf("%v", v)
132141
}
133142

143+
} else if k == "message" {
144+
retMsg = fmt.Sprintf("%v", v)
134145
}
135146
}
136-
137147
return retMsg
138148
}
139149

150+
func parseErrResponse(raw []byte) string {
151+
152+
var f interface{}
153+
err := json.Unmarshal(raw, &f)
154+
if err != nil {
155+
return string(raw)
156+
}
157+
158+
switch f.(type) {
159+
case map[string]interface{}:
160+
return parseErrObjectResponse(f.(map[string]interface{}))
161+
case []interface{}:
162+
return parseErrArrayResponse(f.([]interface{}))
163+
default:
164+
return ""
165+
}
166+
}
167+
140168
func (helper *APIHelper) CheckHealth() error {
141169
baseURL := helper.Endpoint.URL
142170
requestURL := fmt.Sprintf("%s%s", baseURL, HealthPath)
@@ -294,7 +322,7 @@ func (helper *APIHelper) DeletePolicy() error {
294322

295323
}
296324

297-
func (helper *APIHelper) GetAggregatedMetrics(metricName string, startTime, endTime int64, desc bool, page uint64) (bool, [][]string, error) {
325+
func (helper *APIHelper) GetAggregatedMetrics(metricName string, startTime, endTime int64, asc bool, page uint64) (bool, [][]string, error) {
298326

299327
if page <= 1 {
300328
err := helper.CheckHealth()
@@ -317,10 +345,10 @@ func (helper *APIHelper) GetAggregatedMetrics(metricName string, startTime, endT
317345
if endTime > 0 {
318346
q.Add("end-time", strconv.FormatInt(endTime, 10))
319347
}
320-
if desc {
321-
q.Add("order", "desc")
322-
} else {
348+
if asc {
323349
q.Add("order", "asc")
350+
} else {
351+
q.Add("order", "desc")
324352
}
325353
q.Add("page", strconv.FormatUint(page, 10))
326354
req.URL.RawQuery = q.Encode()
@@ -362,7 +390,7 @@ func (helper *APIHelper) GetAggregatedMetrics(metricName string, startTime, endT
362390

363391
}
364392

365-
func (helper *APIHelper) GetHistory(startTime, endTime int64, desc bool, page uint64) (bool, [][]string, error) {
393+
func (helper *APIHelper) GetHistory(startTime, endTime int64, asc bool, page uint64) (bool, [][]string, error) {
366394

367395
if page <= 1 {
368396
err := helper.CheckHealth()
@@ -383,10 +411,10 @@ func (helper *APIHelper) GetHistory(startTime, endTime int64, desc bool, page ui
383411
if endTime > 0 {
384412
q.Add("end-time", strconv.FormatInt(endTime, 10))
385413
}
386-
if desc {
387-
q.Add("order", "desc")
388-
} else {
414+
if asc {
389415
q.Add("order", "asc")
416+
} else {
417+
q.Add("order", "desc")
390418
}
391419
q.Add("page", strconv.FormatUint(page, 10))
392420
req.URL.RawQuery = q.Encode()
@@ -449,3 +477,97 @@ func (helper *APIHelper) GetHistory(startTime, endTime int64, desc bool, page ui
449477
}
450478

451479
}
480+
481+
func (helper *APIHelper) DeleteCredential() error {
482+
483+
err := helper.CheckHealth()
484+
if err != nil {
485+
return err
486+
}
487+
488+
baseURL := helper.Endpoint.URL
489+
requestURL := fmt.Sprintf("%s%s", baseURL, strings.Replace(CredentialPath, "{appId}", helper.Client.AppId, -1))
490+
491+
req, err := http.NewRequest("DELETE", requestURL, nil)
492+
req.Header.Add("Authorization", helper.Client.AuthToken)
493+
494+
resp, err := helper.DoRequest(req)
495+
if err != nil {
496+
return err
497+
}
498+
defer resp.Body.Close()
499+
500+
raw, err := ioutil.ReadAll(resp.Body)
501+
if resp.StatusCode != http.StatusOK {
502+
var errorMsg string
503+
switch resp.StatusCode {
504+
case 401:
505+
errorMsg = fmt.Sprintf(ui.Unauthorized, baseURL)
506+
default:
507+
errorMsg = parseErrResponse(raw)
508+
}
509+
return errors.New(errorMsg)
510+
}
511+
512+
return nil
513+
514+
}
515+
516+
func (helper *APIHelper) CreateCredential(data interface{}) ([]byte, error) {
517+
518+
err := helper.CheckHealth()
519+
if err != nil {
520+
return nil, err
521+
}
522+
523+
baseURL := helper.Endpoint.URL
524+
requestURL := fmt.Sprintf("%s%s", baseURL, strings.Replace(CredentialPath, "{appId}", helper.Client.AppId, -1))
525+
526+
var body io.Reader
527+
if data != nil {
528+
jsonByte, e := json.Marshal(data)
529+
if e != nil {
530+
return nil, fmt.Errorf(ui.InvalidCredential, e)
531+
}
532+
body = bytes.NewBuffer(jsonByte)
533+
}
534+
535+
req, err := http.NewRequest("PUT", requestURL, body)
536+
req.Header.Add("Authorization", helper.Client.AuthToken)
537+
req.Header.Add("Content-Type", "application/json")
538+
539+
resp, err := helper.DoRequest(req)
540+
if err != nil {
541+
return nil, err
542+
}
543+
defer resp.Body.Close()
544+
545+
raw, err := ioutil.ReadAll(resp.Body)
546+
547+
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
548+
549+
var errorMsg string
550+
switch resp.StatusCode {
551+
case 401:
552+
errorMsg = fmt.Sprintf(ui.Unauthorized, baseURL)
553+
case 400:
554+
errorMsg = fmt.Sprintf(ui.InvalidCredential, parseErrResponse(raw))
555+
default:
556+
errorMsg = parseErrResponse(raw)
557+
}
558+
return nil, errors.New(errorMsg)
559+
}
560+
561+
var credential models.CredentialResponse
562+
err = json.Unmarshal(raw, &credential)
563+
if err != nil {
564+
return nil, err
565+
}
566+
567+
prettyCredential, err := cjson.MarshalWithoutHTMLEscape(credential)
568+
if err != nil {
569+
return nil, err
570+
}
571+
572+
return prettyCredential, nil
573+
}

0 commit comments

Comments
 (0)