From 1efc6881604f3affb579de18116714620612adf6 Mon Sep 17 00:00:00 2001 From: Kazumichi Yamamoto Date: Mon, 20 Feb 2023 14:36:08 +0900 Subject: [PATCH] =?UTF-8?q?cooldown=E3=81=AE=E5=9F=BA=E6=BA=96=E3=81=A8?= =?UTF-8?q?=E3=81=97=E3=81=A6=E5=90=84=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9?= =?UTF-8?q?=E3=81=AEModifiedAt=E3=82=92=E7=94=A8=E3=81=84=E3=82=8B=20(#475?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: cooldown - modified_atを持たないリソースについて追記 * cooldownの基準として各リソースのModifiedAtを用いる --- core/core.go | 18 +++-- core/job.go | 27 +++---- core/job_test.go | 101 ++++++++++++------------- core/resource_def_elb.go | 16 ++++ core/resource_def_parent.go | 19 ++++- core/resource_def_router.go | 21 +++++ core/resource_def_server.go | 16 ++++ core/resource_def_server_group.go | 20 +++++ core/resource_def_server_group_test.go | 47 ++++++++++++ core/resource_definition.go | 4 + core/resource_definitions.go | 19 +++++ core/resource_definitions_test.go | 50 ++++++++++++ core/resource_parent.go | 3 +- core/resource_stub_test.go | 8 ++ docs/design/cooldown.md | 19 ++++- e2e/horizontal_scaling/autoscaler.yaml | 2 +- e2e/horizontal_scaling/e2e_test.go | 4 +- e2e/vertical_scaling/autoscaler.yaml | 2 +- e2e/vertical_scaling/e2e_test.go | 5 +- 19 files changed, 321 insertions(+), 80 deletions(-) diff --git a/core/core.go b/core/core.go index 56c5e305..2980515d 100644 --- a/core/core.go +++ b/core/core.go @@ -193,11 +193,6 @@ func (c *Core) currentJob(ctx *RequestContext) *JobStatus { func (c *Core) handle(ctx *RequestContext) (*JobStatus, string, error) { job := c.currentJob(ctx) - if !job.Acceptable(ctx.request.requestType) { - ctx.Logger().Info("status", request.ScalingJobStatus_JOB_IGNORED, "message", "job is in an unacceptable state") //nolint - return job, "job is in an unacceptable state", nil - } - if c.stopping { ctx.Logger().Info("status", request.ScalingJobStatus_JOB_IGNORED, "message", "core is shutting down") //nolint return job, "core is shutting down", nil @@ -214,6 +209,19 @@ func (c *Core) handle(ctx *RequestContext) (*JobStatus, string, error) { return job, "", err } + // さくらのクラウドAPI経由で対象リソース情報を参照し最終更新日時を取得 + lastModifiedAt, err := rds.LastModifiedAt(ctx, c.config.APIClient()) + if err != nil { + job.SetStatus(request.ScalingJobStatus_JOB_CANCELED) // まだ実行前のためCANCELEDを返す + ctx.Logger().Info("status", request.ScalingJobStatus_JOB_CANCELED, "error", err) //nolint + return job, "", err + } + + if !job.Acceptable(ctx.request.requestType, lastModifiedAt) { + ctx.Logger().Info("status", request.ScalingJobStatus_JOB_IGNORED, "message", "job is in an unacceptable state") //nolint + return job, "job is in an unacceptable state", nil + } + job.SetStatus(request.ScalingJobStatus_JOB_ACCEPTED) ctx.Logger().Info("status", request.ScalingJobStatus_JOB_ACCEPTED) //nolint diff --git a/core/job.go b/core/job.go index a1a803d9..b575a56d 100644 --- a/core/job.go +++ b/core/job.go @@ -26,11 +26,10 @@ import ( // // Inputsからのリクエストパラメータ ResourceNameごとに作成される type JobStatus struct { - id string - status request.ScalingJobStatus - statusChanged time.Time - coolDown *CoolDown - mu sync.Mutex + id string + status request.ScalingJobStatus + coolDown *CoolDown + mu sync.Mutex } func NewJobStatus(req *requestInfo, coolDown *CoolDown) *JobStatus { @@ -38,10 +37,9 @@ func NewJobStatus(req *requestInfo, coolDown *CoolDown) *JobStatus { coolDown = &CoolDown{} } return &JobStatus{ - id: req.ID(), - status: request.ScalingJobStatus_JOB_UNKNOWN, - statusChanged: time.Now(), - coolDown: coolDown, + id: req.ID(), + status: request.ScalingJobStatus_JOB_DONE, // 完了状態 == ジョブ受け入れ可能ということで初期値にしておく + coolDown: coolDown, } } @@ -61,28 +59,27 @@ func (j *JobStatus) SetStatus(status request.ScalingJobStatus) { defer j.mu.Unlock() j.status = status - j.statusChanged = time.Now() } func (j *JobStatus) String() string { - return fmt.Sprintf("ID: %s Status: %s StatusChanged: %s", j.ID(), j.Status(), j.statusChanged) + return fmt.Sprintf("ID: %s Status: %s", j.ID(), j.Status()) } // Acceptable このジョブが新規に受け入れ可能(新たに起動できる)状態の場合true -func (j *JobStatus) Acceptable(requestType RequestTypes) bool { +func (j *JobStatus) Acceptable(requestType RequestTypes, lastModifiedAt time.Time) bool { switch j.Status() { case request.ScalingJobStatus_JOB_ACCEPTED, request.ScalingJobStatus_JOB_RUNNING: // すでに受け入れ済み or 実行中 return false default: // 以外は冷却期間でなければtrue - return !j.inCoolDownTime(requestType) + return !j.inCoolDownTime(requestType, lastModifiedAt) } } // inCoolDownTime StatusがDONE、かつ冷却期間内であればtrue -func (j *JobStatus) inCoolDownTime(requestType RequestTypes) bool { +func (j *JobStatus) inCoolDownTime(requestType RequestTypes, lastModifiedAt time.Time) bool { coolDownTime := j.coolDown.Duration(requestType) return j.Status() == request.ScalingJobStatus_JOB_DONE && - j.statusChanged.After(time.Now().Add(-1*coolDownTime)) + lastModifiedAt.After(time.Now().Add(-1*coolDownTime)) } diff --git a/core/job_test.go b/core/job_test.go index c82e0ff7..95af7c78 100644 --- a/core/job_test.go +++ b/core/job_test.go @@ -23,155 +23,154 @@ import ( func TestJobStatus_Acceptable(t *testing.T) { type fields struct { - status request.ScalingJobStatus - statusChanged time.Time - coolDown *CoolDown + status request.ScalingJobStatus + coolDown *CoolDown } tests := []struct { - name string - fields fields - requestType RequestTypes - want bool + name string + fields fields + requestType RequestTypes + lastModifiedAt time.Time + want bool }{ { name: "returns true if status is DONE and is not in cooling down time: up", fields: fields{ - status: request.ScalingJobStatus_JOB_DONE, - statusChanged: time.Now().Add(-2 * time.Second), + status: request.ScalingJobStatus_JOB_DONE, coolDown: &CoolDown{ Up: 1, Down: 1000, }, }, - requestType: requestTypeUp, - want: true, + lastModifiedAt: time.Now().Add(-2 * time.Second), + requestType: requestTypeUp, + want: true, }, { name: "returns false if is in cooling down time: up", fields: fields{ - status: request.ScalingJobStatus_JOB_DONE, - statusChanged: time.Now(), + status: request.ScalingJobStatus_JOB_DONE, coolDown: &CoolDown{ Up: 1000, Down: 1, }, }, - requestType: requestTypeUp, - want: false, + lastModifiedAt: time.Now(), + requestType: requestTypeUp, + want: false, }, { name: "returns true if status is DONE and is not in cooling down time: down", fields: fields{ - status: request.ScalingJobStatus_JOB_DONE, - statusChanged: time.Now().Add(-2 * time.Second), + status: request.ScalingJobStatus_JOB_DONE, coolDown: &CoolDown{ Up: 1000, Down: 1, }, }, - requestType: requestTypeDown, - want: true, + lastModifiedAt: time.Now().Add(-2 * time.Second), + requestType: requestTypeDown, + want: true, }, { name: "returns false if is in cooling down time: down", fields: fields{ - status: request.ScalingJobStatus_JOB_DONE, - statusChanged: time.Now(), + status: request.ScalingJobStatus_JOB_DONE, coolDown: &CoolDown{ Up: 1, Down: 1000, }, }, - requestType: requestTypeDown, - want: false, + lastModifiedAt: time.Now(), + requestType: requestTypeDown, + want: false, }, { name: "returns false if status is RUNNING", fields: fields{ - status: request.ScalingJobStatus_JOB_RUNNING, - statusChanged: time.Now().Add(-2 * time.Second), + status: request.ScalingJobStatus_JOB_RUNNING, coolDown: &CoolDown{ Up: 1, Down: 1, }, }, - requestType: requestTypeUp, - want: false, + lastModifiedAt: time.Now().Add(-2 * time.Second), + requestType: requestTypeUp, + want: false, }, { name: "returns true if status is UNKNOWN", fields: fields{ - status: request.ScalingJobStatus_JOB_UNKNOWN, - statusChanged: time.Now().Add(-2 * time.Second), + status: request.ScalingJobStatus_JOB_UNKNOWN, coolDown: &CoolDown{ Up: 1, Down: 1, }, }, - requestType: requestTypeUp, - want: true, + lastModifiedAt: time.Now().Add(-2 * time.Second), + requestType: requestTypeUp, + want: true, }, { name: "returns true if status is CANCELED", fields: fields{ - status: request.ScalingJobStatus_JOB_CANCELED, - statusChanged: time.Now().Add(-2 * time.Second), + status: request.ScalingJobStatus_JOB_CANCELED, coolDown: &CoolDown{ Up: 1, Down: 1, }, }, - requestType: requestTypeUp, - want: true, + lastModifiedAt: time.Now().Add(-2 * time.Second), + requestType: requestTypeUp, + want: true, }, { name: "returns true if status is DONE_NOOP", fields: fields{ - status: request.ScalingJobStatus_JOB_DONE_NOOP, - statusChanged: time.Now().Add(-2 * time.Second), + status: request.ScalingJobStatus_JOB_DONE_NOOP, coolDown: &CoolDown{ Up: 1, Down: 1, }, }, - requestType: requestTypeUp, - want: true, + lastModifiedAt: time.Now().Add(-2 * time.Second), + requestType: requestTypeUp, + want: true, }, { name: "returns false if status is ACCEPTED", fields: fields{ - status: request.ScalingJobStatus_JOB_ACCEPTED, - statusChanged: time.Now().Add(-2 * time.Second), + status: request.ScalingJobStatus_JOB_ACCEPTED, coolDown: &CoolDown{ Up: 1, Down: 1, }, }, - requestType: requestTypeUp, - want: false, + lastModifiedAt: time.Now().Add(-2 * time.Second), + requestType: requestTypeUp, + want: false, }, { name: "returns true if status is FAILED", fields: fields{ - status: request.ScalingJobStatus_JOB_FAILED, - statusChanged: time.Now().Add(-2 * time.Second), + status: request.ScalingJobStatus_JOB_FAILED, coolDown: &CoolDown{ Up: 1, Down: 1, }, }, - requestType: requestTypeUp, - want: true, + lastModifiedAt: time.Now().Add(-2 * time.Second), + requestType: requestTypeUp, + want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { j := &JobStatus{ - status: tt.fields.status, - statusChanged: tt.fields.statusChanged, - coolDown: tt.fields.coolDown, + status: tt.fields.status, + coolDown: tt.fields.coolDown, } - if got := j.Acceptable(tt.requestType); got != tt.want { + if got := j.Acceptable(tt.requestType, tt.lastModifiedAt); got != tt.want { t.Errorf("Acceptable() = %v, want %v", got, tt.want) } }) diff --git a/core/resource_def_elb.go b/core/resource_def_elb.go index 344f5f34..9980d8d8 100644 --- a/core/resource_def_elb.go +++ b/core/resource_def_elb.go @@ -17,6 +17,7 @@ package core import ( "context" "fmt" + "time" "github.com/hashicorp/go-multierror" "github.com/sacloud/autoscaler/validate" @@ -134,3 +135,18 @@ func (d *ResourceDefELB) findCloudResources(ctx context.Context, apiClient iaas. } return found.ProxyLBs, nil } + +// LastModifiedAt この定義が対象とするリソース(群)の最終更新日時を返す +func (d *ResourceDefELB) LastModifiedAt(ctx *RequestContext, apiClient iaas.APICaller) (time.Time, error) { + cloudResources, err := d.findCloudResources(ctx, apiClient) + if err != nil { + return time.Time{}, err + } + last := time.Time{} + for _, r := range cloudResources { + if r.GetModifiedAt().After(last) { + last = r.GetModifiedAt() + } + } + return last, nil +} diff --git a/core/resource_def_parent.go b/core/resource_def_parent.go index e94121c7..142f6cfe 100644 --- a/core/resource_def_parent.go +++ b/core/resource_def_parent.go @@ -18,6 +18,7 @@ import ( "context" "fmt" "strings" + "time" "github.com/goccy/go-yaml" "github.com/hashicorp/go-multierror" @@ -92,9 +93,25 @@ func (d *ParentResourceDef) Compute(ctx *RequestContext, apiClient iaas.APICalle return resources, nil } +// LastModifiedAt この定義が対象とするリソース(群)の最終更新日時を返す +func (d *ParentResourceDef) LastModifiedAt(ctx *RequestContext, apiClient iaas.APICaller) (time.Time, error) { + cloudResources, err := d.findCloudResources(ctx, apiClient, ctx.zone) + if err != nil { + return time.Time{}, err + } + last := time.Time{} + for _, r := range cloudResources { + if r.GetModifiedAt().After(last) { + last = r.GetModifiedAt() + } + } + return last, nil +} + type SakuraCloudResource interface { GetID() types.ID GetName() string + GetModifiedAt() time.Time } func (d *ParentResourceDef) findCloudResources(ctx context.Context, apiClient iaas.APICaller, zone string) ([]SakuraCloudResource, error) { @@ -136,7 +153,7 @@ func (d *ParentResourceDef) findCloudResources(ctx context.Context, apiClient ia return nil, fmt.Errorf("computing status failed: %s", err) } for _, v := range found.Internet { - results = append(results, v) + results = append(results, &sakuraCloudRouter{Internet: v, zone: zone}) } case ResourceTypeLoadBalancer: op := iaas.NewLoadBalancerOp(apiClient) diff --git a/core/resource_def_router.go b/core/resource_def_router.go index 9f552605..393409af 100644 --- a/core/resource_def_router.go +++ b/core/resource_def_router.go @@ -17,6 +17,7 @@ package core import ( "context" "fmt" + "time" "github.com/hashicorp/go-multierror" "github.com/sacloud/autoscaler/validate" @@ -140,7 +141,27 @@ func (d *ResourceDefRouter) findCloudResources(ctx context.Context, apiClient ia return results, nil } +// LastModifiedAt この定義が対象とするリソース(群)の最終更新日時を返す +func (d *ResourceDefRouter) LastModifiedAt(ctx *RequestContext, apiClient iaas.APICaller) (time.Time, error) { + cloudResources, err := d.findCloudResources(ctx, apiClient) + if err != nil { + return time.Time{}, err + } + last := time.Time{} + for _, r := range cloudResources { + if r.GetModifiedAt().After(last) { + last = r.GetModifiedAt() + } + } + return last, nil +} + type sakuraCloudRouter struct { *iaas.Internet zone string } + +func (w *sakuraCloudRouter) GetModifiedAt() time.Time { + // ルータはModifiedAtを持たないためCreatedAtを返す + return w.CreatedAt +} diff --git a/core/resource_def_server.go b/core/resource_def_server.go index b5e72a57..fa303a1e 100644 --- a/core/resource_def_server.go +++ b/core/resource_def_server.go @@ -17,6 +17,7 @@ package core import ( "context" "fmt" + "time" "github.com/hashicorp/go-multierror" "github.com/sacloud/autoscaler/validate" @@ -169,6 +170,21 @@ func (d *ResourceDefServer) findCloudResources(ctx context.Context, apiClient ia return results, nil } +// LastModifiedAt この定義が対象とするリソース(群)の最終更新日時を返す +func (d *ResourceDefServer) LastModifiedAt(ctx *RequestContext, apiClient iaas.APICaller) (time.Time, error) { + cloudResources, err := d.findCloudResources(ctx, apiClient) + if err != nil { + return time.Time{}, err + } + last := time.Time{} + for _, r := range cloudResources { + if r.GetModifiedAt().After(last) { + last = r.GetModifiedAt() + } + } + return last, nil +} + type sakuraCloudServer struct { *iaas.Server zone string diff --git a/core/resource_def_server_group.go b/core/resource_def_server_group.go index 8929f894..c69ce5f3 100644 --- a/core/resource_def_server_group.go +++ b/core/resource_def_server_group.go @@ -19,6 +19,7 @@ import ( "fmt" "sort" "strings" + "time" "github.com/hashicorp/go-multierror" "github.com/sacloud/autoscaler/config" @@ -329,3 +330,22 @@ func (d *ResourceDefServerGroup) filterCloudServers(servers []*iaas.Server) []*i } return filtered } + +// LastModifiedAt この定義が対象とするリソース(群)の最終更新日時を返す +func (d *ResourceDefServerGroup) LastModifiedAt(ctx *RequestContext, apiClient iaas.APICaller) (time.Time, error) { + cloudResources, err := d.findCloudResources(ctx, apiClient) + if err != nil { + return time.Time{}, err + } + return d.lastModifiedAt(cloudResources), nil +} + +func (d *ResourceDefServerGroup) lastModifiedAt(cloudResources []*iaas.Server) time.Time { + last := time.Time{} + for _, r := range cloudResources { + if r.GetModifiedAt().After(last) { + last = r.GetModifiedAt() + } + } + return last +} diff --git a/core/resource_def_server_group_test.go b/core/resource_def_server_group_test.go index 95036a7a..0bc7ed9d 100644 --- a/core/resource_def_server_group_test.go +++ b/core/resource_def_server_group_test.go @@ -18,6 +18,7 @@ import ( "bytes" "context" "fmt" + "reflect" "testing" "time" @@ -1173,3 +1174,49 @@ func TestResourceDefServerGroup_determineZone(t *testing.T) { }) } } + +func TestResourceDefServerGroup_lastModifiedAt(t *testing.T) { + type args struct { + cloudResources []*iaas.Server + } + tests := []struct { + name string + args args + want time.Time + }{ + { + name: "empty", + args: args{}, + want: time.Time{}, + }, + { + name: "returns modified-at simply", + args: args{ + cloudResources: []*iaas.Server{ + {ModifiedAt: time.UnixMilli(100)}, + }, + }, + want: time.UnixMilli(100), + }, + { + name: "returns last modified-at", + args: args{ + cloudResources: []*iaas.Server{ + {ModifiedAt: time.UnixMilli(103)}, + {ModifiedAt: time.UnixMilli(107)}, + {ModifiedAt: time.UnixMilli(105)}, + {ModifiedAt: time.UnixMilli(101)}, + }, + }, + want: time.UnixMilli(107), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := &ResourceDefServerGroup{} + if got := d.lastModifiedAt(tt.args.cloudResources); !reflect.DeepEqual(got, tt.want) { + t.Errorf("lastModifiedAt() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/core/resource_definition.go b/core/resource_definition.go index 87462867..9d78cf2b 100644 --- a/core/resource_definition.go +++ b/core/resource_definition.go @@ -16,6 +16,7 @@ package core import ( "context" + "time" "github.com/sacloud/autoscaler/defaults" "github.com/sacloud/iaas-api-go" @@ -34,6 +35,9 @@ type ResourceDefinition interface { // // TypeとSelectorを元にさくらのクラウドAPIを用いて実リソースを検索、Resourceを作成して返す Compute(ctx *RequestContext, apiClient iaas.APICaller) (Resources, error) + + // LastModifiedAt この定義が対象とするリソース(群)の最終更新を返す + LastModifiedAt(ctx *RequestContext, apiClient iaas.APICaller) (time.Time, error) } // ResourceDefBase 全てのリソース定義が実装すべき基本プロパティ diff --git a/core/resource_definitions.go b/core/resource_definitions.go index eac83897..3c41f1bb 100644 --- a/core/resource_definitions.go +++ b/core/resource_definitions.go @@ -237,3 +237,22 @@ func (rds *ResourceDefinitions) handleAllByFunc(computed Computed, handlers Hand } return nil } + +// LastModifiedAt 内包する定義群が対象とするリソース(群)の更新日時のうち、最も新しい(時間が遅い)値を返す +func (rds *ResourceDefinitions) LastModifiedAt(ctx *RequestContext, apiClient iaas.APICaller) (time.Time, error) { + lastModifiedAt := time.Time{} + err := rds.walk(*rds, func(r ResourceDefinition) error { + t, err := r.LastModifiedAt(ctx, apiClient) + if err != nil { + return err + } + if t.After(lastModifiedAt) { + lastModifiedAt = t + } + return nil + }) + if err != nil { + return time.Time{}, err + } + return lastModifiedAt, nil +} diff --git a/core/resource_definitions_test.go b/core/resource_definitions_test.go index d1f4dcfd..029f0bd7 100644 --- a/core/resource_definitions_test.go +++ b/core/resource_definitions_test.go @@ -17,7 +17,9 @@ package core import ( "context" "fmt" + "reflect" "testing" + "time" "github.com/goccy/go-yaml" "github.com/hashicorp/go-multierror" @@ -234,3 +236,51 @@ func TestResourceDefinitions_Validate(t *testing.T) { }) } } + +func TestResourceDefinitions_LastModifiedAt(t *testing.T) { + tests := []struct { + name string + rds ResourceDefinitions + want time.Time + wantErr bool + }{ + { + name: "empty", + rds: ResourceDefinitions{}, + want: time.Time{}, + wantErr: false, + }, + { + name: "returns last modified_at", + rds: ResourceDefinitions{ + &stubResourceDef{lastModifiedAt: time.UnixMilli(100)}, + &stubResourceDef{lastModifiedAt: time.UnixMilli(300)}, + &stubResourceDef{lastModifiedAt: time.UnixMilli(200)}, + }, + want: time.UnixMilli(300), + wantErr: false, + }, + { + name: "returns error", + rds: ResourceDefinitions{ + &stubResourceDef{lastModifiedAt: time.UnixMilli(100)}, + &stubResourceDef{lastmodifiedAtErr: fmt.Errorf("dummy")}, + &stubResourceDef{lastModifiedAt: time.UnixMilli(200)}, + }, + want: time.Time{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.rds.LastModifiedAt(nil, nil) + if (err != nil) != tt.wantErr { + t.Errorf("LastModifiedAt() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("LastModifiedAt() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/core/resource_parent.go b/core/resource_parent.go index 6e898ae2..c39a2bb0 100644 --- a/core/resource_parent.go +++ b/core/resource_parent.go @@ -136,10 +136,11 @@ func (r *ParentResource) refresh(ctx *RequestContext) error { } case ResourceTypeRouter: op := iaas.NewInternetOp(r.apiClient) - found, err = op.Read(ctx, r.zone, r.resource.GetID()) + read, err := op.Read(ctx, r.zone, r.resource.GetID()) if err != nil { return fmt.Errorf("computing status failed: %s", err) } + found = &sakuraCloudRouter{Internet: read, zone: r.zone} case ResourceTypeLoadBalancer: op := iaas.NewLoadBalancerOp(r.apiClient) found, err = op.Read(ctx, r.zone, r.resource.GetID()) diff --git a/core/resource_stub_test.go b/core/resource_stub_test.go index 3a1a9759..030ea2a9 100644 --- a/core/resource_stub_test.go +++ b/core/resource_stub_test.go @@ -16,6 +16,7 @@ package core import ( "context" + "time" "github.com/sacloud/iaas-api-go" ) @@ -26,6 +27,9 @@ type stubResourceDef struct { Dummy string `validate:"omitempty,oneof=value1 value2"` validateFunc func(ctx context.Context, apiClient iaas.APICaller) []error + + lastModifiedAt time.Time + lastmodifiedAtErr error } func (d *stubResourceDef) String() string { @@ -46,6 +50,10 @@ func (d *stubResourceDef) Compute(ctx *RequestContext, apiClient iaas.APICaller) return nil, nil } +func (d *stubResourceDef) LastModifiedAt(ctx *RequestContext, apiClient iaas.APICaller) (time.Time, error) { + return d.lastModifiedAt, d.lastmodifiedAtErr +} + type stubResource struct { *ResourceBase computeFunc func(ctx *RequestContext, refresh bool) (Computed, error) diff --git a/docs/design/cooldown.md b/docs/design/cooldown.md index d9297da9..9296f1b8 100644 --- a/docs/design/cooldown.md +++ b/docs/design/cooldown.md @@ -39,7 +39,22 @@ Note: 毎回のup/down実行時にさくらのクラウドAPI呼び出しが発 - `core.ResourceDefinitions`にも`LastModifiedAt(...)`を追加し、内包するResourceDefinitionsが返したmodified_atの内、最も遅い時間を示すmodified_atの値を返すようにする - `core.JobStatus`のfunc`Acceptable(...)`を上記のLastModifiedAt()を呼び出して判定するよう修正 +### 基準とするmodified_atフィールドについて -### 更新内容 +ルータについてはmodified_atフィールドを持たない。 +しかしルータのプラン変更時は新規リソースが作成されるためcreated_atで代用可能である。 -- 2023/2/16: 作成 \ No newline at end of file +よってmodified_atだけでなくcreated_atも用いて判断する。 + +#### modified_atはいつ変更されるか? + +- リソース作成時 +- プランを変更した時(実際にはリソースの新規作成にあたるため) +- 電源操作時(サーバなど) + +Note: リソース名の変更など一部の操作ではmodifie_atが変更されない。 + +## 更新内容 + +- 2023/2/16: 作成 +- 2023/2/17: modified_atを持たないリソースが存在することへの考慮を追加 diff --git a/e2e/horizontal_scaling/autoscaler.yaml b/e2e/horizontal_scaling/autoscaler.yaml index e7a15d31..ea481458 100644 --- a/e2e/horizontal_scaling/autoscaler.yaml +++ b/e2e/horizontal_scaling/autoscaler.yaml @@ -40,4 +40,4 @@ resources: selector: "autoscaler-e2e-horizontal-scaling" autoscaler: - cooldown: 5 \ No newline at end of file + cooldown: 180 \ No newline at end of file diff --git a/e2e/horizontal_scaling/e2e_test.go b/e2e/horizontal_scaling/e2e_test.go index 5d2389b5..f04118de 100644 --- a/e2e/horizontal_scaling/e2e_test.go +++ b/e2e/horizontal_scaling/e2e_test.go @@ -114,7 +114,7 @@ func TestE2E_HorizontalScaling(t *testing.T) { } // 冷却期間待機 - time.Sleep(5 * time.Second) + time.Sleep(180 * time.Second) // Terraformステートのリフレッシュ(複数回IDが変更されるため毎回リフレッシュしておく) e2e.TerraformRefresh() // nolint @@ -148,7 +148,7 @@ func TestE2E_HorizontalScaling(t *testing.T) { } // 冷却期間待機 - time.Sleep(5 * time.Second) + time.Sleep(180 * time.Second) // Terraformステートのリフレッシュ(複数回IDが変更されるため毎回リフレッシュしておく) e2e.TerraformRefresh() // nolint diff --git a/e2e/vertical_scaling/autoscaler.yaml b/e2e/vertical_scaling/autoscaler.yaml index 92e4a531..4fcf26ed 100644 --- a/e2e/vertical_scaling/autoscaler.yaml +++ b/e2e/vertical_scaling/autoscaler.yaml @@ -19,4 +19,4 @@ resources: selector: "autoscaler-e2e-vertical-scaling" autoscaler: - cooldown: 30 \ No newline at end of file + cooldown: 180 \ No newline at end of file diff --git a/e2e/vertical_scaling/e2e_test.go b/e2e/vertical_scaling/e2e_test.go index 0550de12..20da1b08 100644 --- a/e2e/vertical_scaling/e2e_test.go +++ b/e2e/vertical_scaling/e2e_test.go @@ -102,6 +102,9 @@ func TestE2E_VerticalScaling(t *testing.T) { t.Fatalf("grpc-health-prove: unexpected response: %s", string(out)) } + // 冷却期間待機(リソース更新日時が基準のため初回のスケールアップも影響を受ける) + time.Sleep(180 * time.Second) + /************************************************************************** * Step 1-1: スケールアップ *************************************************************************/ @@ -171,7 +174,7 @@ func TestE2E_VerticalScaling(t *testing.T) { } // 冷却期間待機 - time.Sleep(30 * time.Second) + time.Sleep(180 * time.Second) // Terraformステートのリフレッシュ(複数回IDが変更されるため毎回リフレッシュしておく) e2e.TerraformRefresh() // nolint