Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/auto scale behavior client #235

Merged
merged 7 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
github.com/pmorie/go-open-service-broker-client v0.0.0-20180330214919-dca737037ce6
github.com/sabhiram/go-gitignore v0.0.0-20171017070213-362f9845770f
github.com/tsuru/gnuflag v0.0.0-20151217162021-86b8c1b864aa
github.com/tsuru/go-tsuruclient v0.0.0-20240812213541-df5e446efabf
github.com/tsuru/go-tsuruclient v0.0.0-20241029183502-e219a905d873
github.com/tsuru/tablecli v0.0.0-20190131152944-7ded8a3383c6
github.com/tsuru/tsuru v0.0.0-20240703132558-bfd1d9c89602
golang.org/x/net v0.25.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ github.com/tsuru/config v0.0.0-20201023175036-375aaee8b560 h1:fniQ/BmYAHdnNmY333
github.com/tsuru/config v0.0.0-20201023175036-375aaee8b560/go.mod h1:mj6t8JKWU51GScTT50XRmDj65T5XhTyNvO5FUNV5zS4=
github.com/tsuru/gnuflag v0.0.0-20151217162021-86b8c1b864aa h1:JlLQP1xa13a994p/Aau2e3K9xXYaHNoNvTDVIMHSUa4=
github.com/tsuru/gnuflag v0.0.0-20151217162021-86b8c1b864aa/go.mod h1:UibOSvkMFKRe/eiwktAPAvQG8L+p8nYsECJvu3Dgw7I=
github.com/tsuru/go-tsuruclient v0.0.0-20240812213541-df5e446efabf h1:Jwxn42PtNz2bbOYKwQWFUMlo3sYy2LIYEDFcEPej7M0=
github.com/tsuru/go-tsuruclient v0.0.0-20240812213541-df5e446efabf/go.mod h1:qwh/KJ6ypa2GISRI79XFOHhnSjGOe1cZVPHF3nfrf18=
github.com/tsuru/go-tsuruclient v0.0.0-20241029183502-e219a905d873 h1:Rs3urDCvqLpmGpUKOJNRiOCij/A+EcemdeOaGmGcs/E=
github.com/tsuru/go-tsuruclient v0.0.0-20241029183502-e219a905d873/go.mod h1:qwh/KJ6ypa2GISRI79XFOHhnSjGOe1cZVPHF3nfrf18=
github.com/tsuru/tablecli v0.0.0-20190131152944-7ded8a3383c6 h1:1XDdWFAjIbCSG1OjN9v9KdWhuM8UtYlFcfHe/Ldkchk=
github.com/tsuru/tablecli v0.0.0-20190131152944-7ded8a3383c6/go.mod h1:ztYpOhW+u1k21FEqp7nZNgpWbr0dUKok5lgGCZi+1AQ=
github.com/tsuru/tsuru v0.0.0-20240703132558-bfd1d9c89602 h1:HiF99OFCkd2F0DwyMzBDStxm43rtrK8sBnVA2ZyfIZ4=
Expand Down
9 changes: 8 additions & 1 deletion tsuru/client/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,14 @@ func (a *app) String(simplified bool) string {
prometheusInfo,
}))
}

scaleDown := getParamsScaleDownJson(as.Behavior)
autoScaleTable.AddRow([]string{
"Scale Down Behavior",
fmt.Sprintf("Units: %s\nPercentage: %s%%\nStabilization Window: %ss",
scaleDown.UnitsPolicyValue,
scaleDown.PercentagePolicyValue,
scaleDown.StabilizationWindow),
})
autoScaleTables = append(autoScaleTables, autoScaleTable)
}

Expand Down
138 changes: 91 additions & 47 deletions tsuru/client/apps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1570,14 +1570,28 @@ func (s *S) TestAppInfoWithAutoScale(c *check.C) {
"minUnits":1,
"maxUnits":10,
"averageCPU":"500m",
"version":10
"version":10,
"behavior": {
"scaleDown": {
"percentagePolicyValue": 30,
"unitsPolicyValue": 20,
"stabilizationWindow": 100
}
}
},
{
"process":"worker",
"minUnits":2,
"maxUnits":5,
"averageCPU":"2",
"version":10
"version":10,
"behavior": {
"scaleDown": {
"percentagePolicyValue": 10,
"unitsPolicyValue": 10,
"stabilizationWindow": 60
}
}
}
]
}`
Expand Down Expand Up @@ -1608,18 +1622,26 @@ Units [process worker]: 1
Auto Scale:

Process: web (v10), Min Units: 1, Max Units: 10
+----------+-----------------+
| Triggers | Trigger details |
+----------+-----------------+
| CPU | Target: 50% |
+----------+-----------------+
+---------------------+----------------------------+
| Triggers | Trigger details |
+---------------------+----------------------------+
| CPU | Target: 50% |
+---------------------+----------------------------+
| Scale Down Behavior | Units: 20 |
| | Percentage: 30% |
| | Stabilization Window: 100s |
+---------------------+----------------------------+

Process: worker (v10), Min Units: 2, Max Units: 5
+----------+-----------------+
| Triggers | Trigger details |
+----------+-----------------+
| CPU | Target: 200% |
+----------+-----------------+
+---------------------+---------------------------+
| Triggers | Trigger details |
+---------------------+---------------------------+
| CPU | Target: 200% |
+---------------------+---------------------------+
| Scale Down Behavior | Units: 10 |
| | Percentage: 10% |
| | Stabilization Window: 60s |
+---------------------+---------------------------+

`
context := cmd.Context{
Expand Down Expand Up @@ -1672,6 +1694,13 @@ func (s *S) TestAppInfoWithKEDAAutoScale(c *check.C) {
"maxUnits":10,
"averageCPU":"500m",
"version":10,
"behavior": {
"scaleDown": {
"percentagePolicyValue": 21,
"unitsPolicyValue": 25,
"stabilizationWindow": 50
}
},
"schedules": [
{
"minReplicas":2,
Expand Down Expand Up @@ -1705,6 +1734,13 @@ func (s *S) TestAppInfoWithKEDAAutoScale(c *check.C) {
"maxUnits":5,
"averageCPU":"2",
"version":10,
"behavior": {
"scaleDown": {
"percentagePolicyValue": 5,
"unitsPolicyValue": 7,
"stabilizationWindow": 60
}
},
"schedules": [
{
"minReplicas":1,
Expand Down Expand Up @@ -1743,43 +1779,51 @@ Units [process worker]: 1
Auto Scale:

Process: web (v10), Min Units: 1, Max Units: 10
+------------+-------------------------------------------+
| Triggers | Trigger details |
+------------+-------------------------------------------+
| CPU | Target: 50% |
+------------+-------------------------------------------+
| Schedule | Start: At 06:00 AM (0 6 * * *) |
| | End: At 06:00 PM (0 18 * * *) |
| | Units: 2 |
| | Timezone: UTC |
+------------+-------------------------------------------+
| Schedule | Start: At 12:00 PM (0 12 * * *) |
| | End: At 03:00 PM (0 15 * * *) |
| | Units: 3 |
| | Timezone: UTC |
+------------+-------------------------------------------+
| Prometheus | Name: my_metric_id |
| | Query: my_query{app='my-app'} |
| | Threshold: 2 |
| | PrometheusAddress: default.com |
+------------+-------------------------------------------+
| Prometheus | Name: my_metric_id_2 |
| | Query: my_query_2{app='my-app'} |
| | Threshold: 5 |
| | PrometheusAddress: exemple.prometheus.com |
+------------+-------------------------------------------+
+---------------------+-------------------------------------------+
| Triggers | Trigger details |
+---------------------+-------------------------------------------+
| CPU | Target: 50% |
+---------------------+-------------------------------------------+
| Schedule | Start: At 06:00 AM (0 6 * * *) |
| | End: At 06:00 PM (0 18 * * *) |
| | Units: 2 |
| | Timezone: UTC |
+---------------------+-------------------------------------------+
| Schedule | Start: At 12:00 PM (0 12 * * *) |
| | End: At 03:00 PM (0 15 * * *) |
| | Units: 3 |
| | Timezone: UTC |
+---------------------+-------------------------------------------+
| Prometheus | Name: my_metric_id |
| | Query: my_query{app='my-app'} |
| | Threshold: 2 |
| | PrometheusAddress: default.com |
+---------------------+-------------------------------------------+
| Prometheus | Name: my_metric_id_2 |
| | Query: my_query_2{app='my-app'} |
| | Threshold: 5 |
| | PrometheusAddress: exemple.prometheus.com |
+---------------------+-------------------------------------------+
| Scale Down Behavior | Units: 25 |
| | Percentage: 21% |
| | Stabilization Window: 50s |
+---------------------+-------------------------------------------+

Process: worker (v10), Min Units: 2, Max Units: 5
+----------+--------------------------------+
| Triggers | Trigger details |
+----------+--------------------------------+
| CPU | Target: 200% |
+----------+--------------------------------+
| Schedule | Start: At 12:00 AM (0 0 * * *) |
| | End: At 06:00 AM (0 6 * * *) |
| | Units: 1 |
| | Timezone: America/Sao_Paulo |
+----------+--------------------------------+
+---------------------+--------------------------------+
| Triggers | Trigger details |
+---------------------+--------------------------------+
| CPU | Target: 200% |
+---------------------+--------------------------------+
| Schedule | Start: At 12:00 AM (0 0 * * *) |
| | End: At 06:00 AM (0 6 * * *) |
| | Units: 1 |
| | Timezone: America/Sao_Paulo |
+---------------------+--------------------------------+
| Scale Down Behavior | Units: 7 |
| | Percentage: 5% |
| | Stabilization Window: 60s |
+---------------------+--------------------------------+

`
context := cmd.Context{
Expand Down
51 changes: 51 additions & 0 deletions tsuru/client/apps_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2016 tsuru-client authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package client

import (
"encoding/json"
"fmt"

"github.com/tsuru/go-tsuruclient/pkg/tsuru"
)

type behaviorScaleDownJson struct {
ScaleDown scaleDownJson `json:"scaleDown"`
}

type scaleDownJson struct {
UnitsPolicyValue *int32 `json:"unitsPolicyValue,omitempty"`
PercentagePolicyValue *int32 `json:"percentagePolicyValue,omitempty"`
StabilizationWindow *int32 `json:"stabilizationWindow,omitempty"`
}

type scaleDownOutput struct {
UnitsPolicyValue string `json:"unitsPolicyValue,omitempty"`
PercentagePolicyValue string `json:"percentagePolicyValue,omitempty"`
StabilizationWindow string `json:"stabilizationWindow,omitempty"`
}

func getParamsScaleDownJson(behavior tsuru.AutoScaleSpecBehavior) scaleDownOutput {
b, err := json.Marshal(behavior)
if err != nil {
return scaleDownOutput{}
}
var behaviorJson behaviorScaleDownJson
err = json.Unmarshal(b, &behaviorJson)
if err != nil {
return scaleDownOutput{}
}
output := scaleDownOutput{}
if behaviorJson.ScaleDown.UnitsPolicyValue != nil {
output.UnitsPolicyValue = fmt.Sprintf("%d", *behaviorJson.ScaleDown.UnitsPolicyValue)
}
if behaviorJson.ScaleDown.PercentagePolicyValue != nil {
output.PercentagePolicyValue = fmt.Sprintf("%d", *behaviorJson.ScaleDown.PercentagePolicyValue)
}
if behaviorJson.ScaleDown.StabilizationWindow != nil {
output.StabilizationWindow = fmt.Sprintf("%d", *behaviorJson.ScaleDown.StabilizationWindow)
}
return output
}
16 changes: 9 additions & 7 deletions tsuru/client/autoscale.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ func (c *AutoScaleSet) Flags() *gnuflag.FlagSet {

c.fs.Var(&c.schedules, "schedule", "Schedule window to up/down scale. Example: {\"minReplicas\": 2, \"start\": \"0 6 * * *\", \"end\": \"0 18 * * *\"}")
c.fs.Var(&c.prometheus, "prometheus", "Prometheus settings to up/down scale. Example: {\"name\": \"my_metric_identification\", \"threshold\": 10, \"query\":\"sum(my_metric{tsuru_app=\\\"my_app\\\"})\"}")

c.fs.Var((*int32Value)(&c.autoscale.Behavior.ScaleDown.PercentagePolicyValue), "scale-down-percentage", "Percentage of units to downscale when the metric is below the threshold")
c.fs.Var((*int32Value)(&c.autoscale.Behavior.ScaleDown.PercentagePolicyValue), "sdp", "Percentage of units to downscale when the metric is below the threshold")

c.fs.Var((*int32Value)(&c.autoscale.Behavior.ScaleDown.StabilizationWindow), "scale-down-stabilization-window", "Stabilization window in seconds to avoid scale down")
c.fs.Var((*int32Value)(&c.autoscale.Behavior.ScaleDown.StabilizationWindow), "sdsw", "Stabilization window in seconds to avoid scale down")

c.fs.Var((*int32Value)(&c.autoscale.Behavior.ScaleDown.UnitsPolicyValue), "scale-down-units", "Number of units to downscale when the metric is below the threshold")
c.fs.Var((*int32Value)(&c.autoscale.Behavior.ScaleDown.UnitsPolicyValue), "sdu", "Number of units to downscale when the metric is below the threshold")
}
return c.fs
}
Expand All @@ -90,31 +99,24 @@ func (c *AutoScaleSet) Run(ctx *cmd.Context) error {
if err != nil {
return err
}

schedules := []tsuru.AutoScaleSchedule{}
for _, scheduleString := range c.schedules {
var autoScaleSchedule tsuru.AutoScaleSchedule
if err = json.Unmarshal([]byte(scheduleString), &autoScaleSchedule); err != nil {
return err
}

schedules = append(schedules, autoScaleSchedule)
}

c.autoscale.Schedules = schedules

prometheus := []tsuru.AutoScalePrometheus{}
for _, prometheusString := range c.prometheus {
var autoScalePrometheus tsuru.AutoScalePrometheus
if err = json.Unmarshal([]byte(prometheusString), &autoScalePrometheus); err != nil {
return err
}

prometheus = append(prometheus, autoScalePrometheus)
}

c.autoscale.Prometheus = prometheus

_, err = apiClient.AppApi.AutoScaleAdd(context.TODO(), appName, c.autoscale)
if err != nil {
return err
Expand Down
71 changes: 71 additions & 0 deletions tsuru/client/autoscale_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,77 @@ func (s *S) TestAutoScaleSet(c *check.C) {
c.Assert(stdout.String(), check.Equals, expected)
}

func (s *S) TestAutoScaleBehaviorSet(c *check.C) {
tests := []struct {
param []string
expected tsuru.AutoScaleSpecBehavior
}{
{
param: []string{"-a", "myapp", "-p", "proc1", "--min", "2", "--max", "5", "--sdp", "3", "--sdsw", "22", "--sdu", "9"},
expected: tsuru.AutoScaleSpecBehavior{
ScaleDown: tsuru.AutoScaleSpecBehaviorScaleDown{
StabilizationWindow: 22,
PercentagePolicyValue: 3,
UnitsPolicyValue: 9,
},
},
},
{
param: []string{"-a", "myapp", "-p", "proc1", "--min", "2", "--max", "5", "--scale-down-percentage", "5", "--scale-down-stabilization-window", "7", "--scale-down-units", "40"},
expected: tsuru.AutoScaleSpecBehavior{
ScaleDown: tsuru.AutoScaleSpecBehaviorScaleDown{
StabilizationWindow: 7,
PercentagePolicyValue: 5,
UnitsPolicyValue: 40,
},
},
},
{
param: []string{"-a", "myapp", "-p", "proc1", "--min", "2", "--max", "5"},
expected: tsuru.AutoScaleSpecBehavior{},
},
}

for _, tt := range tests {
var stdout, stderr bytes.Buffer
expected := "Unit auto scale successfully set.\n"
context := cmd.Context{
Stdout: &stdout,
Stderr: &stderr,
Args: []string{},
}
trans := cmdtest.ConditionalTransport{
Transport: cmdtest.Transport{Message: "", Status: http.StatusOK},
CondFunc: func(r *http.Request) bool {
c.Assert(r.URL.Path, check.Equals, "/1.9/apps/myapp/units/autoscale")
c.Assert(r.Method, check.Equals, "POST")
var ret tsuru.AutoScaleSpec
c.Assert(r.Header.Get("Content-Type"), check.Equals, "application/json")
data, err := io.ReadAll(r.Body)
c.Assert(err, check.IsNil)
err = json.Unmarshal(data, &ret)
c.Assert(err, check.IsNil)
c.Assert(ret, check.DeepEquals, tsuru.AutoScaleSpec{
MinUnits: 2,
MaxUnits: 5,
Process: "proc1",
Behavior: tsuru.AutoScaleSpecBehavior{
ScaleDown: tt.expected.ScaleDown,
},
})
return true
},
}
s.setupFakeTransport(&trans)
command := AutoScaleSet{}
command.Info()
command.Flags().Parse(true, tt.param)
err := command.Run(&context)
c.Assert(err, check.IsNil)
c.Assert(stdout.String(), check.Equals, expected)
}
}

func (s *S) TestKEDAScheduleAutoScaleSet(c *check.C) {
var stdout, stderr bytes.Buffer
expected := "Unit auto scale successfully set.\n"
Expand Down
Loading