From ac623f0e3ac76089ea9a7523187ea57fc195c3c8 Mon Sep 17 00:00:00 2001 From: Rintaro Okamura Date: Thu, 11 Jun 2020 09:53:48 +0900 Subject: [PATCH 01/11] :recycle: use io.pipe for reading s3 buckets Signed-off-by: Rintaro Okamura --- internal/db/storage/blob/s3/reader/option.go | 17 +++++++- internal/db/storage/blob/s3/reader/reader.go | 43 ++++++++++++++++---- internal/db/storage/blob/s3/s3.go | 1 + 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/internal/db/storage/blob/s3/reader/option.go b/internal/db/storage/blob/s3/reader/option.go index 2216928f47..9f2a437c93 100644 --- a/internal/db/storage/blob/s3/reader/option.go +++ b/internal/db/storage/blob/s3/reader/option.go @@ -16,14 +16,27 @@ package reader -import "github.com/aws/aws-sdk-go/service/s3" +import ( + "github.com/aws/aws-sdk-go/service/s3" + "github.com/vdaas/vald/internal/errgroup" +) type Option func(r *reader) var ( - defaultOpts = []Option{} + defaultOpts = []Option{ + WithErrGroup(errgroup.Get()), + } ) +func WithErrGroup(eg errgroup.Group) Option { + return func(r *reader) { + if eg != nil { + r.eg = eg + } + } +} + func WithService(s *s3.S3) Option { return func(r *reader) { if s != nil { diff --git a/internal/db/storage/blob/s3/reader/reader.go b/internal/db/storage/blob/s3/reader/reader.go index 91d8774c93..29d8de2791 100644 --- a/internal/db/storage/blob/s3/reader/reader.go +++ b/internal/db/storage/blob/s3/reader/reader.go @@ -19,18 +19,23 @@ package reader import ( "context" "io" + "sync" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3" + "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/safety" ) type reader struct { + eg errgroup.Group service *s3.S3 bucket string key string - resp *s3.GetObjectOutput + pr io.ReadCloser + wg *sync.WaitGroup } type Reader interface { @@ -53,23 +58,47 @@ func (r *reader) Open(ctx context.Context) (err error) { Key: aws.String(r.key), } - r.resp, err = r.service.GetObjectWithContext(ctx, input) + r.wg = new(sync.WaitGroup) - return err + var pw io.WriteCloser + + r.pr, pw = io.Pipe() + + resp, err := r.service.GetObjectWithContext(ctx, input) + if err != nil { + return err + } + + r.wg.Add(1) + + r.eg.Go(safety.RecoverFunc(func() (err error) { + defer r.wg.Done() + defer resp.Body.Close() + defer pw.Close() + + _, err = io.Copy(pw, resp.Body) + return err + })) + + return nil } func (r *reader) Close() error { - if r.resp != nil { - return r.resp.Body.Close() + if r.pr != nil { + return r.pr.Close() + } + + if r.wg != nil { + r.wg.Wait() } return nil } func (r *reader) Read(p []byte) (n int, err error) { - if r.resp == nil { + if r.pr == nil { return 0, errors.ErrStorageReaderNotOpened } - return r.resp.Body.Read(p) + return r.pr.Read(p) } diff --git a/internal/db/storage/blob/s3/s3.go b/internal/db/storage/blob/s3/s3.go index 7fc1b1429b..11cd8fd64e 100644 --- a/internal/db/storage/blob/s3/s3.go +++ b/internal/db/storage/blob/s3/s3.go @@ -62,6 +62,7 @@ func (c *client) Close() error { func (c *client) Reader(ctx context.Context, key string) (io.ReadCloser, error) { r := reader.New( + reader.WithErrGroup(c.eg), reader.WithService(c.service), reader.WithBucket(c.bucket), reader.WithKey(key), From 9ba5648d5e6c79f93ca79c6bc189b46ebd5be2c8 Mon Sep 17 00:00:00 2001 From: Rintaro Okamura Date: Thu, 11 Jun 2020 09:55:31 +0900 Subject: [PATCH 02/11] :white_check_mark: fix tests Signed-off-by: Rintaro Okamura --- .../db/storage/blob/s3/reader/option_test.go | 142 ++++++++++++++++-- .../db/storage/blob/s3/reader/reader_test.go | 59 ++++++-- 2 files changed, 173 insertions(+), 28 deletions(-) diff --git a/internal/db/storage/blob/s3/reader/option_test.go b/internal/db/storage/blob/s3/reader/option_test.go index da080b585b..0d7b9ab86e 100644 --- a/internal/db/storage/blob/s3/reader/option_test.go +++ b/internal/db/storage/blob/s3/reader/option_test.go @@ -20,10 +20,126 @@ import ( "testing" "github.com/aws/aws-sdk-go/service/s3" + "github.com/vdaas/vald/internal/errgroup" "go.uber.org/goleak" ) +func TestWithErrGroup(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + eg errgroup.Group + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + eg: nil, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + eg: nil, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithErrGroup(test.args.eg) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithErrGroup(test.args.eg) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + func TestWithService(t *testing.T) { + // Change interface type to the type of object you are testing type T = interface{} type args struct { s *s3.S3 @@ -61,7 +177,7 @@ func TestWithService(t *testing.T) { /* defaultCheckFunc := func(w want, obj *T) error { if !reflect.DeepEqual(obj, w.obj) { - return errors.Errorf("got = %v, want %v", obj, w.c) + return errors.Errorf("got = %v, want %v", obj, w.obj) } return nil } @@ -99,7 +215,7 @@ func TestWithService(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -120,7 +236,7 @@ func TestWithService(t *testing.T) { } */ - // Uncomment this block if the option returns an error, otherwise delete it + // Uncomment this block if the option do not return an error, otherwise delete it /* if test.checkFunc == nil { test.checkFunc = defaultCheckFunc @@ -128,7 +244,7 @@ func TestWithService(t *testing.T) { got := WithService(test.args.s) obj := new(T) got(obj) - if err := test.checkFunc(tt.want, obj); err != nil { + if err := test.checkFunc(test.want, obj); err != nil { tt.Errorf("error = %v", err) } */ @@ -137,6 +253,7 @@ func TestWithService(t *testing.T) { } func TestWithBucket(t *testing.T) { + // Change interface type to the type of object you are testing type T = interface{} type args struct { bucket string @@ -174,7 +291,7 @@ func TestWithBucket(t *testing.T) { /* defaultCheckFunc := func(w want, obj *T) error { if !reflect.DeepEqual(obj, w.obj) { - return errors.Errorf("got = %v, want %v", obj, w.c) + return errors.Errorf("got = %v, want %v", obj, w.obj) } return nil } @@ -212,7 +329,7 @@ func TestWithBucket(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -233,7 +350,7 @@ func TestWithBucket(t *testing.T) { } */ - // Uncomment this block if the option returns an error, otherwise delete it + // Uncomment this block if the option do not return an error, otherwise delete it /* if test.checkFunc == nil { test.checkFunc = defaultCheckFunc @@ -241,7 +358,7 @@ func TestWithBucket(t *testing.T) { got := WithBucket(test.args.bucket) obj := new(T) got(obj) - if err := test.checkFunc(tt.want, obj); err != nil { + if err := test.checkFunc(test.want, obj); err != nil { tt.Errorf("error = %v", err) } */ @@ -250,6 +367,7 @@ func TestWithBucket(t *testing.T) { } func TestWithKey(t *testing.T) { + // Change interface type to the type of object you are testing type T = interface{} type args struct { key string @@ -287,7 +405,7 @@ func TestWithKey(t *testing.T) { /* defaultCheckFunc := func(w want, obj *T) error { if !reflect.DeepEqual(obj, w.obj) { - return errors.Errorf("got = %v, want %v", obj, w.c) + return errors.Errorf("got = %v, want %v", obj, w.obj) } return nil } @@ -325,7 +443,7 @@ func TestWithKey(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -346,7 +464,7 @@ func TestWithKey(t *testing.T) { } */ - // Uncomment this block if the option returns an error, otherwise delete it + // Uncomment this block if the option do not return an error, otherwise delete it /* if test.checkFunc == nil { test.checkFunc = defaultCheckFunc @@ -354,7 +472,7 @@ func TestWithKey(t *testing.T) { got := WithKey(test.args.key) obj := new(T) got(obj) - if err := test.checkFunc(tt.want, obj); err != nil { + if err := test.checkFunc(test.want, obj); err != nil { tt.Errorf("error = %v", err) } */ diff --git a/internal/db/storage/blob/s3/reader/reader_test.go b/internal/db/storage/blob/s3/reader/reader_test.go index b50eb4a9f2..5c88ae09ee 100644 --- a/internal/db/storage/blob/s3/reader/reader_test.go +++ b/internal/db/storage/blob/s3/reader/reader_test.go @@ -18,10 +18,13 @@ package reader import ( "context" + "io" "reflect" + "sync" "testing" "github.com/aws/aws-sdk-go/service/s3" + "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "go.uber.org/goleak" ) @@ -77,7 +80,7 @@ func TestNew(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -102,10 +105,12 @@ func Test_reader_Open(t *testing.T) { ctx context.Context } type fields struct { + eg errgroup.Group service *s3.S3 bucket string key string - resp *s3.GetObjectOutput + pr io.ReadCloser + wg *sync.WaitGroup } type want struct { err error @@ -134,10 +139,12 @@ func Test_reader_Open(t *testing.T) { ctx: nil, }, fields: fields { + eg: nil, service: nil, bucket: "", key: "", - resp: nil, + pr: nil, + wg: sync.WaitGroup{}, }, want: want{}, checkFunc: defaultCheckFunc, @@ -153,10 +160,12 @@ func Test_reader_Open(t *testing.T) { ctx: nil, }, fields: fields { + eg: nil, service: nil, bucket: "", key: "", - resp: nil, + pr: nil, + wg: sync.WaitGroup{}, }, want: want{}, checkFunc: defaultCheckFunc, @@ -167,7 +176,7 @@ func Test_reader_Open(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -178,10 +187,12 @@ func Test_reader_Open(t *testing.T) { test.checkFunc = defaultCheckFunc } r := &reader{ + eg: test.fields.eg, service: test.fields.service, bucket: test.fields.bucket, key: test.fields.key, - resp: test.fields.resp, + pr: test.fields.pr, + wg: test.fields.wg, } err := r.Open(test.args.ctx) @@ -195,10 +206,12 @@ func Test_reader_Open(t *testing.T) { func Test_reader_Close(t *testing.T) { type fields struct { + eg errgroup.Group service *s3.S3 bucket string key string - resp *s3.GetObjectOutput + pr io.ReadCloser + wg *sync.WaitGroup } type want struct { err error @@ -223,10 +236,12 @@ func Test_reader_Close(t *testing.T) { { name: "test_case_1", fields: fields { + eg: nil, service: nil, bucket: "", key: "", - resp: nil, + pr: nil, + wg: sync.WaitGroup{}, }, want: want{}, checkFunc: defaultCheckFunc, @@ -239,10 +254,12 @@ func Test_reader_Close(t *testing.T) { return test { name: "test_case_2", fields: fields { + eg: nil, service: nil, bucket: "", key: "", - resp: nil, + pr: nil, + wg: sync.WaitGroup{}, }, want: want{}, checkFunc: defaultCheckFunc, @@ -253,7 +270,7 @@ func Test_reader_Close(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc() } @@ -264,10 +281,12 @@ func Test_reader_Close(t *testing.T) { test.checkFunc = defaultCheckFunc } r := &reader{ + eg: test.fields.eg, service: test.fields.service, bucket: test.fields.bucket, key: test.fields.key, - resp: test.fields.resp, + pr: test.fields.pr, + wg: test.fields.wg, } err := r.Close() @@ -284,10 +303,12 @@ func Test_reader_Read(t *testing.T) { p []byte } type fields struct { + eg errgroup.Group service *s3.S3 bucket string key string - resp *s3.GetObjectOutput + pr io.ReadCloser + wg *sync.WaitGroup } type want struct { wantN int @@ -320,10 +341,12 @@ func Test_reader_Read(t *testing.T) { p: nil, }, fields: fields { + eg: nil, service: nil, bucket: "", key: "", - resp: nil, + pr: nil, + wg: sync.WaitGroup{}, }, want: want{}, checkFunc: defaultCheckFunc, @@ -339,10 +362,12 @@ func Test_reader_Read(t *testing.T) { p: nil, }, fields: fields { + eg: nil, service: nil, bucket: "", key: "", - resp: nil, + pr: nil, + wg: sync.WaitGroup{}, }, want: want{}, checkFunc: defaultCheckFunc, @@ -353,7 +378,7 @@ func Test_reader_Read(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -364,10 +389,12 @@ func Test_reader_Read(t *testing.T) { test.checkFunc = defaultCheckFunc } r := &reader{ + eg: test.fields.eg, service: test.fields.service, bucket: test.fields.bucket, key: test.fields.key, - resp: test.fields.resp, + pr: test.fields.pr, + wg: test.fields.wg, } gotN, err := r.Read(test.args.p) From 8ae30048ee2352f9b4ae509065a708afda95174f Mon Sep 17 00:00:00 2001 From: Rintaro Okamura Date: Thu, 11 Jun 2020 10:06:18 +0900 Subject: [PATCH 03/11] :green_heart: Fix CI trigger for agent-sidecar Signed-off-by: Rintaro Okamura --- .github/workflows/dockers-agent-sidecar-image.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/dockers-agent-sidecar-image.yml b/.github/workflows/dockers-agent-sidecar-image.yml index 3a443ef370..c25dea2cc4 100644 --- a/.github/workflows/dockers-agent-sidecar-image.yml +++ b/.github/workflows/dockers-agent-sidecar-image.yml @@ -11,6 +11,7 @@ on: paths: - 'internal/**' - '!internal/db/**' + - 'internal/db/storage/blob' - '!internal/k8s/**' - 'apis/grpc/**' - 'pkg/agent/sidecar/**' @@ -22,6 +23,7 @@ on: paths: - 'internal/**' - '!internal/db/**' + - 'internal/db/storage/blob' - '!internal/k8s/**' - 'apis/grpc/**' - 'pkg/agent/sidecar/**' From 5e48fcbffe4261f8dde4e6503454735b462010e8 Mon Sep 17 00:00:00 2001 From: Rintaro Okamura Date: Thu, 11 Jun 2020 12:05:02 +0900 Subject: [PATCH 04/11] :sparkles: Add backoff option for agent sidecar initcontainer mode Signed-off-by: Rintaro Okamura --- charts/vald/values.yaml | 18 ++++- internal/config/sidecar.go | 9 +++ internal/db/storage/blob/s3/reader/reader.go | 10 +++ internal/errors/blob.go | 67 +++++++++++++++++++ pkg/agent/sidecar/service/restorer/option.go | 13 ++++ .../sidecar/service/restorer/restorer.go | 25 +++++-- .../usecase/initcontainer/initcontainer.go | 1 + 7 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 internal/errors/blob.go diff --git a/charts/vald/values.yaml b/charts/vald/values.yaml index d1c0d9d392..c39127ba8e 100644 --- a/charts/vald/values.yaml +++ b/charts/vald/values.yaml @@ -431,7 +431,7 @@ defaults: # @schema {"name": "defaults.grpc.client.connection_pool.old_conn_close_duration", "type": "string"} # defaults.grpc.client.connection_pool.old_conn_close_duration -- makes delay before gRPC client connection closing during connection pool rebalance old_conn_close_duration: "3s" - # @schema {"name": "defaults.grpc.client.backoff", "type": "object"} + # @schema {"name": "defaults.grpc.client.backoff", "type": "object", "anchor": "backoff"} backoff: # @schema {"name": "defaults.grpc.client.backoff.initial_duration", "type": "string"} # defaults.grpc.client.backoff.initial_duration -- gRPC client backoff initial duration @@ -1318,6 +1318,22 @@ agent: # `lz4`: >= 0, higher is better compression. # `zstd`: 1 (fastest) to 22 (best), however implementation relies on klauspost/compress. compression_level: -1 + # @schema {"name": "agent.sidecar.config.restore_backoff", "alias": "backoff"} + restore_backoff: + # agent.sidecar.config.restore_backoff.initial_duration -- restore backoff initial duration + initial_duration: 1s + # agent.sidecar.config.restore_backoff.backoff_time_limit -- restore backoff time limit + backoff_time_limit: 1m + # agent.sidecar.config.restore_backoff.maximum_duration -- restore backoff maximum duration + maximum_duration: 30s + # agent.sidecar.config.restore_backoff.jitter_limit -- restore backoff jitter limit + jitter_limit: 10s + # agent.sidecar.config.restore_backoff.backoff_factor -- restore backoff factor + backoff_factor: 1.2 + # agent.sidecar.config.restore_backoff.retry_count -- restore backoff retry count + retry_count: 100 + # agent.sidecar.config.restore_backoff.enable_error_log -- restore backoff log enabled + enable_error_log: true # @schema {"name": "discoverer", "type": "object"} discoverer: diff --git a/internal/config/sidecar.go b/internal/config/sidecar.go index e81c12f0b6..9614b721ed 100644 --- a/internal/config/sidecar.go +++ b/internal/config/sidecar.go @@ -41,6 +41,9 @@ type AgentSidecar struct { // Compress represent compression configurations Compress *CompressCore `yaml:"compress" json:"compress"` + + // RestoreBackoff repsresent backoff configurations for restoring process + RestoreBackoff *Backoff `yaml:"restore_backoff" json:"restore_backoff"` } func (s *AgentSidecar) Bind() *AgentSidecar { @@ -63,5 +66,11 @@ func (s *AgentSidecar) Bind() *AgentSidecar { s.Compress = new(CompressCore) } + if s.RestoreBackoff != nil { + s.RestoreBackoff = s.RestoreBackoff.Bind() + } else { + s.RestoreBackoff = new(Backoff) + } + return s } diff --git a/internal/db/storage/blob/s3/reader/reader.go b/internal/db/storage/blob/s3/reader/reader.go index 29d8de2791..7b57475add 100644 --- a/internal/db/storage/blob/s3/reader/reader.go +++ b/internal/db/storage/blob/s3/reader/reader.go @@ -22,6 +22,7 @@ import ( "sync" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/s3" "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" @@ -66,6 +67,15 @@ func (r *reader) Open(ctx context.Context) (err error) { resp, err := r.service.GetObjectWithContext(ctx, input) if err != nil { + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + case s3.ErrCodeNoSuchBucket: + return errors.NewErrBlobNoSuchBucket(err, r.bucket) + case s3.ErrCodeNoSuchKey: + return errors.NewErrBlobNoSuchKey(err, r.key) + } + } + return err } diff --git a/internal/errors/blob.go b/internal/errors/blob.go new file mode 100644 index 0000000000..4fa594ddeb --- /dev/null +++ b/internal/errors/blob.go @@ -0,0 +1,67 @@ +// +// Copyright (C) 2019-2020 Vdaas.org Vald team ( kpango, rinx, kmrmt ) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Package errors provides error types and function +package errors + +var ( + // BlobStorage + NewErrBlobNoSuchBucket = func(err error, name string) error { + return &ErrBlobNoSuchBucket{ + err: Wrap(err, Errorf("bucket %s not found", name).Error()), + } + } + + NewErrBlobNoSuchKey = func(err error, key string) error { + return &ErrBlobNoSuchKey{ + err: Wrap(err, Errorf("key %s not found", key).Error()), + } + } +) + +type ErrBlobNoSuchBucket struct { + err error +} + +func (e *ErrBlobNoSuchBucket) Error() string { + return e.err.Error() +} + +func IsErrBlobNoSuchBucket(err error) bool { + switch err.(type) { + case *ErrBlobNoSuchBucket: + return true + default: + return false + } +} + +type ErrBlobNoSuchKey struct { + err error +} + +func (e *ErrBlobNoSuchKey) Error() string { + return e.err.Error() +} + +func IsErrBlobNoSuchKey(err error) bool { + switch err.(type) { + case *ErrBlobNoSuchKey: + return true + default: + return false + } +} diff --git a/pkg/agent/sidecar/service/restorer/option.go b/pkg/agent/sidecar/service/restorer/option.go index 7c0192631a..e0300f26ba 100644 --- a/pkg/agent/sidecar/service/restorer/option.go +++ b/pkg/agent/sidecar/service/restorer/option.go @@ -18,6 +18,7 @@ package restorer import ( + "github.com/vdaas/vald/internal/backoff" "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/pkg/agent/sidecar/service/storage" ) @@ -59,3 +60,15 @@ func WithBlobStorage(storage storage.Storage) Option { return nil } } + +func WithBackoffOpts(opts ...backoff.Option) Option { + return func(r *restorer) error { + if r.backoffOpts == nil { + r.backoffOpts = opts + } + + r.backoffOpts = append(r.backoffOpts, opts...) + + return nil + } +} diff --git a/pkg/agent/sidecar/service/restorer/restorer.go b/pkg/agent/sidecar/service/restorer/restorer.go index e7bc1a6215..7d20be04ea 100644 --- a/pkg/agent/sidecar/service/restorer/restorer.go +++ b/pkg/agent/sidecar/service/restorer/restorer.go @@ -26,6 +26,7 @@ import ( "reflect" "syscall" + "github.com/vdaas/vald/internal/backoff" "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/log" @@ -43,6 +44,8 @@ type restorer struct { eg errgroup.Group storage storage.Storage + + backoffOpts []backoff.Option } func New(opts ...Option) (Restorer, error) { @@ -109,10 +112,24 @@ func (r *restorer) startRestore(ctx context.Context) (<-chan error, error) { r.eg.Go(safety.RecoverFunc(func() (err error) { defer close(ech) - err = r.restore(ctx) - if err != nil { - log.Errorf("restoring failed: %s", err) - } + b := backoff.New(r.backoffOpts...) + defer b.Close() + + _, err = b.Do(ctx, func() (interface{}, error) { + err := r.restore(ctx) + if err != nil { + log.Errorf("restoring failed: %s", err) + + if errors.IsErrBlobNoSuchBucket(err) || + errors.IsErrBlobNoSuchKey(err) { + return nil, nil + } + + return nil, err + } + + return nil, nil + }) return p.Signal(syscall.SIGTERM) // TODO: #403 })) diff --git a/pkg/agent/sidecar/usecase/initcontainer/initcontainer.go b/pkg/agent/sidecar/usecase/initcontainer/initcontainer.go index 9cfc75c41e..3e9e46b26b 100644 --- a/pkg/agent/sidecar/usecase/initcontainer/initcontainer.go +++ b/pkg/agent/sidecar/usecase/initcontainer/initcontainer.go @@ -105,6 +105,7 @@ func New(cfg *config.Data) (r runner.Runner, err error) { restorer.WithErrGroup(eg), restorer.WithDir(cfg.AgentSidecar.WatchDir), restorer.WithBlobStorage(bs), + restorer.WithBackoffOpts(cfg.AgentSidecar.RestoreBackoff.Opts()...), ) if err != nil { return nil, err From c88f59a46979167a8eed6aeb28863700e1a23a7f Mon Sep 17 00:00:00 2001 From: Rintaro Okamura Date: Thu, 11 Jun 2020 12:36:04 +0900 Subject: [PATCH 05/11] :white_check_mark: fix tests Signed-off-by: Rintaro Okamura --- internal/config/sidecar_test.go | 10 +- internal/errors/blob_test.go | 316 ++++++++++++++++++ .../sidecar/service/restorer/option_test.go | 142 +++++++- .../sidecar/service/restorer/restorer_test.go | 57 ++-- pkg/tools/cli/loadtest/usecase/load_test.go | 32 +- 5 files changed, 496 insertions(+), 61 deletions(-) create mode 100644 internal/errors/blob_test.go diff --git a/internal/config/sidecar_test.go b/internal/config/sidecar_test.go index 204df3f05a..73b06c628c 100644 --- a/internal/config/sidecar_test.go +++ b/internal/config/sidecar_test.go @@ -30,10 +30,12 @@ func TestAgentSidecar_Bind(t *testing.T) { Mode string WatchDir string AutoBackupDuration string + PostStopTimeout string Filename string FilenameSuffix string BlobStorage *Blob Compress *CompressCore + RestoreBackoff *Backoff } type want struct { want *AgentSidecar @@ -61,10 +63,12 @@ func TestAgentSidecar_Bind(t *testing.T) { Mode: "", WatchDir: "", AutoBackupDuration: "", + PostStopTimeout: "", Filename: "", FilenameSuffix: "", BlobStorage: Blob{}, Compress: CompressCore{}, + RestoreBackoff: Backoff{}, }, want: want{}, checkFunc: defaultCheckFunc, @@ -80,10 +84,12 @@ func TestAgentSidecar_Bind(t *testing.T) { Mode: "", WatchDir: "", AutoBackupDuration: "", + PostStopTimeout: "", Filename: "", FilenameSuffix: "", BlobStorage: Blob{}, Compress: CompressCore{}, + RestoreBackoff: Backoff{}, }, want: want{}, checkFunc: defaultCheckFunc, @@ -94,7 +100,7 @@ func TestAgentSidecar_Bind(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc() } @@ -108,10 +114,12 @@ func TestAgentSidecar_Bind(t *testing.T) { Mode: test.fields.Mode, WatchDir: test.fields.WatchDir, AutoBackupDuration: test.fields.AutoBackupDuration, + PostStopTimeout: test.fields.PostStopTimeout, Filename: test.fields.Filename, FilenameSuffix: test.fields.FilenameSuffix, BlobStorage: test.fields.BlobStorage, Compress: test.fields.Compress, + RestoreBackoff: test.fields.RestoreBackoff, } got := s.Bind() diff --git a/internal/errors/blob_test.go b/internal/errors/blob_test.go new file mode 100644 index 0000000000..1e3fc094a2 --- /dev/null +++ b/internal/errors/blob_test.go @@ -0,0 +1,316 @@ +// +// Copyright (C) 2019-2020 Vdaas.org Vald team ( kpango, rinx, kmrmt ) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Package errors provides error types and function +package errors + +import ( + "reflect" + "testing" + + + "go.uber.org/goleak" +) + +func TestErrBlobNoSuchBucket_Error(t *testing.T) { + type fields struct { + err error + } + type want struct { + want string + } + type test struct { + name string + fields fields + want want + checkFunc func(want, string) error + beforeFunc func() + afterFunc func() + } + defaultCheckFunc := func(w want, got string) error { + if !reflect.DeepEqual(got, w.want) { + return Errorf("got = %v, want %v", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + fields: fields { + err: nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + fields: fields { + err: nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc() + } + if test.afterFunc != nil { + defer test.afterFunc() + } + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + e := &ErrBlobNoSuchBucket{ + err: test.fields.err, + } + + got := e.Error() + if err := test.checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + + }) + } +} + +func TestIsErrBlobNoSuchBucket(t *testing.T) { + type args struct { + err error + } + type want struct { + want bool + } + type test struct { + name string + args args + want want + checkFunc func(want, bool) error + beforeFunc func(args) + afterFunc func(args) + } + defaultCheckFunc := func(w want, got bool) error { + if !reflect.DeepEqual(got, w.want) { + return Errorf("got = %v, want %v", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + err: nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + err: nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := IsErrBlobNoSuchBucket(test.args.err) + if err := test.checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + + }) + } +} + +func TestErrBlobNoSuchKey_Error(t *testing.T) { + type fields struct { + err error + } + type want struct { + want string + } + type test struct { + name string + fields fields + want want + checkFunc func(want, string) error + beforeFunc func() + afterFunc func() + } + defaultCheckFunc := func(w want, got string) error { + if !reflect.DeepEqual(got, w.want) { + return Errorf("got = %v, want %v", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + fields: fields { + err: nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + fields: fields { + err: nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc() + } + if test.afterFunc != nil { + defer test.afterFunc() + } + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + e := &ErrBlobNoSuchKey{ + err: test.fields.err, + } + + got := e.Error() + if err := test.checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + + }) + } +} + +func TestIsErrBlobNoSuchKey(t *testing.T) { + type args struct { + err error + } + type want struct { + want bool + } + type test struct { + name string + args args + want want + checkFunc func(want, bool) error + beforeFunc func(args) + afterFunc func(args) + } + defaultCheckFunc := func(w want, got bool) error { + if !reflect.DeepEqual(got, w.want) { + return Errorf("got = %v, want %v", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + err: nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + err: nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := IsErrBlobNoSuchKey(test.args.err) + if err := test.checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + + }) + } +} diff --git a/pkg/agent/sidecar/service/restorer/option_test.go b/pkg/agent/sidecar/service/restorer/option_test.go index 6cbf6b347f..da67f1e1e9 100644 --- a/pkg/agent/sidecar/service/restorer/option_test.go +++ b/pkg/agent/sidecar/service/restorer/option_test.go @@ -20,12 +20,14 @@ package restorer import ( "testing" + "github.com/vdaas/vald/internal/backoff" "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/pkg/agent/sidecar/service/storage" "go.uber.org/goleak" ) func TestWithErrGroup(t *testing.T) { + // Change interface type to the type of object you are testing type T = interface{} type args struct { eg errgroup.Group @@ -63,7 +65,7 @@ func TestWithErrGroup(t *testing.T) { /* defaultCheckFunc := func(w want, obj *T) error { if !reflect.DeepEqual(obj, w.obj) { - return errors.Errorf("got = %v, want %v", obj, w.c) + return errors.Errorf("got = %v, want %v", obj, w.obj) } return nil } @@ -101,7 +103,7 @@ func TestWithErrGroup(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -122,7 +124,7 @@ func TestWithErrGroup(t *testing.T) { } */ - // Uncomment this block if the option returns an error, otherwise delete it + // Uncomment this block if the option do not return an error, otherwise delete it /* if test.checkFunc == nil { test.checkFunc = defaultCheckFunc @@ -130,7 +132,7 @@ func TestWithErrGroup(t *testing.T) { got := WithErrGroup(test.args.eg) obj := new(T) got(obj) - if err := test.checkFunc(tt.want, obj); err != nil { + if err := test.checkFunc(test.want, obj); err != nil { tt.Errorf("error = %v", err) } */ @@ -139,6 +141,7 @@ func TestWithErrGroup(t *testing.T) { } func TestWithDir(t *testing.T) { + // Change interface type to the type of object you are testing type T = interface{} type args struct { dir string @@ -176,7 +179,7 @@ func TestWithDir(t *testing.T) { /* defaultCheckFunc := func(w want, obj *T) error { if !reflect.DeepEqual(obj, w.obj) { - return errors.Errorf("got = %v, want %v", obj, w.c) + return errors.Errorf("got = %v, want %v", obj, w.obj) } return nil } @@ -214,7 +217,7 @@ func TestWithDir(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -235,7 +238,7 @@ func TestWithDir(t *testing.T) { } */ - // Uncomment this block if the option returns an error, otherwise delete it + // Uncomment this block if the option do not return an error, otherwise delete it /* if test.checkFunc == nil { test.checkFunc = defaultCheckFunc @@ -243,7 +246,7 @@ func TestWithDir(t *testing.T) { got := WithDir(test.args.dir) obj := new(T) got(obj) - if err := test.checkFunc(tt.want, obj); err != nil { + if err := test.checkFunc(test.want, obj); err != nil { tt.Errorf("error = %v", err) } */ @@ -252,6 +255,7 @@ func TestWithDir(t *testing.T) { } func TestWithBlobStorage(t *testing.T) { + // Change interface type to the type of object you are testing type T = interface{} type args struct { storage storage.Storage @@ -289,7 +293,7 @@ func TestWithBlobStorage(t *testing.T) { /* defaultCheckFunc := func(w want, obj *T) error { if !reflect.DeepEqual(obj, w.obj) { - return errors.Errorf("got = %v, want %v", obj, w.c) + return errors.Errorf("got = %v, want %v", obj, w.obj) } return nil } @@ -327,7 +331,7 @@ func TestWithBlobStorage(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -348,7 +352,7 @@ func TestWithBlobStorage(t *testing.T) { } */ - // Uncomment this block if the option returns an error, otherwise delete it + // Uncomment this block if the option do not return an error, otherwise delete it /* if test.checkFunc == nil { test.checkFunc = defaultCheckFunc @@ -356,7 +360,121 @@ func TestWithBlobStorage(t *testing.T) { got := WithBlobStorage(test.args.storage) obj := new(T) got(obj) - if err := test.checkFunc(tt.want, obj); err != nil { + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithBackoffOpts(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + opts []backoff.Option + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + opts: nil, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + opts: nil, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithBackoffOpts(test.args.opts...) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithBackoffOpts(test.args.opts...) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { tt.Errorf("error = %v", err) } */ diff --git a/pkg/agent/sidecar/service/restorer/restorer_test.go b/pkg/agent/sidecar/service/restorer/restorer_test.go index 50799f72cd..1e6ac33cd8 100644 --- a/pkg/agent/sidecar/service/restorer/restorer_test.go +++ b/pkg/agent/sidecar/service/restorer/restorer_test.go @@ -22,6 +22,7 @@ import ( "reflect" "testing" + "github.com/vdaas/vald/internal/backoff" "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/pkg/agent/sidecar/service/storage" @@ -83,7 +84,7 @@ func TestNew(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -108,9 +109,10 @@ func Test_restorer_Start(t *testing.T) { ctx context.Context } type fields struct { - dir string - eg errgroup.Group - storage storage.Storage + dir string + eg errgroup.Group + storage storage.Storage + backoffOpts []backoff.Option } type want struct { want <-chan error @@ -146,6 +148,7 @@ func Test_restorer_Start(t *testing.T) { dir: "", eg: nil, storage: nil, + backoffOpts: nil, }, want: want{}, checkFunc: defaultCheckFunc, @@ -164,6 +167,7 @@ func Test_restorer_Start(t *testing.T) { dir: "", eg: nil, storage: nil, + backoffOpts: nil, }, want: want{}, checkFunc: defaultCheckFunc, @@ -174,7 +178,7 @@ func Test_restorer_Start(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -185,9 +189,10 @@ func Test_restorer_Start(t *testing.T) { test.checkFunc = defaultCheckFunc } r := &restorer{ - dir: test.fields.dir, - eg: test.fields.eg, - storage: test.fields.storage, + dir: test.fields.dir, + eg: test.fields.eg, + storage: test.fields.storage, + backoffOpts: test.fields.backoffOpts, } got, err := r.Start(test.args.ctx) @@ -204,9 +209,10 @@ func Test_restorer_startRestore(t *testing.T) { ctx context.Context } type fields struct { - dir string - eg errgroup.Group - storage storage.Storage + dir string + eg errgroup.Group + storage storage.Storage + backoffOpts []backoff.Option } type want struct { want <-chan error @@ -242,6 +248,7 @@ func Test_restorer_startRestore(t *testing.T) { dir: "", eg: nil, storage: nil, + backoffOpts: nil, }, want: want{}, checkFunc: defaultCheckFunc, @@ -260,6 +267,7 @@ func Test_restorer_startRestore(t *testing.T) { dir: "", eg: nil, storage: nil, + backoffOpts: nil, }, want: want{}, checkFunc: defaultCheckFunc, @@ -270,7 +278,7 @@ func Test_restorer_startRestore(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -281,9 +289,10 @@ func Test_restorer_startRestore(t *testing.T) { test.checkFunc = defaultCheckFunc } r := &restorer{ - dir: test.fields.dir, - eg: test.fields.eg, - storage: test.fields.storage, + dir: test.fields.dir, + eg: test.fields.eg, + storage: test.fields.storage, + backoffOpts: test.fields.backoffOpts, } got, err := r.startRestore(test.args.ctx) @@ -300,9 +309,10 @@ func Test_restorer_restore(t *testing.T) { ctx context.Context } type fields struct { - dir string - eg errgroup.Group - storage storage.Storage + dir string + eg errgroup.Group + storage storage.Storage + backoffOpts []backoff.Option } type want struct { err error @@ -334,6 +344,7 @@ func Test_restorer_restore(t *testing.T) { dir: "", eg: nil, storage: nil, + backoffOpts: nil, }, want: want{}, checkFunc: defaultCheckFunc, @@ -352,6 +363,7 @@ func Test_restorer_restore(t *testing.T) { dir: "", eg: nil, storage: nil, + backoffOpts: nil, }, want: want{}, checkFunc: defaultCheckFunc, @@ -362,7 +374,7 @@ func Test_restorer_restore(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -373,9 +385,10 @@ func Test_restorer_restore(t *testing.T) { test.checkFunc = defaultCheckFunc } r := &restorer{ - dir: test.fields.dir, - eg: test.fields.eg, - storage: test.fields.storage, + dir: test.fields.dir, + eg: test.fields.eg, + storage: test.fields.storage, + backoffOpts: test.fields.backoffOpts, } err := r.restore(test.args.ctx) diff --git a/pkg/tools/cli/loadtest/usecase/load_test.go b/pkg/tools/cli/loadtest/usecase/load_test.go index f1a0baa4fe..c2f5768d7c 100644 --- a/pkg/tools/cli/loadtest/usecase/load_test.go +++ b/pkg/tools/cli/loadtest/usecase/load_test.go @@ -85,7 +85,7 @@ func TestNew(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -111,7 +111,6 @@ func Test_run_PreStart(t *testing.T) { } type fields struct { eg errgroup.Group - cfg *config.Data loader service.Loader client grpc.Client } @@ -143,7 +142,6 @@ func Test_run_PreStart(t *testing.T) { }, fields: fields { eg: nil, - cfg: nil, loader: nil, client: nil, }, @@ -162,7 +160,6 @@ func Test_run_PreStart(t *testing.T) { }, fields: fields { eg: nil, - cfg: nil, loader: nil, client: nil, }, @@ -175,7 +172,7 @@ func Test_run_PreStart(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -187,7 +184,6 @@ func Test_run_PreStart(t *testing.T) { } r := &run{ eg: test.fields.eg, - cfg: test.fields.cfg, loader: test.fields.loader, client: test.fields.client, } @@ -207,7 +203,6 @@ func Test_run_Start(t *testing.T) { } type fields struct { eg errgroup.Group - cfg *config.Data loader service.Loader client grpc.Client } @@ -243,7 +238,6 @@ func Test_run_Start(t *testing.T) { }, fields: fields { eg: nil, - cfg: nil, loader: nil, client: nil, }, @@ -262,7 +256,6 @@ func Test_run_Start(t *testing.T) { }, fields: fields { eg: nil, - cfg: nil, loader: nil, client: nil, }, @@ -275,7 +268,7 @@ func Test_run_Start(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -287,7 +280,6 @@ func Test_run_Start(t *testing.T) { } r := &run{ eg: test.fields.eg, - cfg: test.fields.cfg, loader: test.fields.loader, client: test.fields.client, } @@ -307,7 +299,6 @@ func Test_run_PreStop(t *testing.T) { } type fields struct { eg errgroup.Group - cfg *config.Data loader service.Loader client grpc.Client } @@ -339,7 +330,6 @@ func Test_run_PreStop(t *testing.T) { }, fields: fields { eg: nil, - cfg: nil, loader: nil, client: nil, }, @@ -358,7 +348,6 @@ func Test_run_PreStop(t *testing.T) { }, fields: fields { eg: nil, - cfg: nil, loader: nil, client: nil, }, @@ -371,7 +360,7 @@ func Test_run_PreStop(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -383,7 +372,6 @@ func Test_run_PreStop(t *testing.T) { } r := &run{ eg: test.fields.eg, - cfg: test.fields.cfg, loader: test.fields.loader, client: test.fields.client, } @@ -403,7 +391,6 @@ func Test_run_Stop(t *testing.T) { } type fields struct { eg errgroup.Group - cfg *config.Data loader service.Loader client grpc.Client } @@ -435,7 +422,6 @@ func Test_run_Stop(t *testing.T) { }, fields: fields { eg: nil, - cfg: nil, loader: nil, client: nil, }, @@ -454,7 +440,6 @@ func Test_run_Stop(t *testing.T) { }, fields: fields { eg: nil, - cfg: nil, loader: nil, client: nil, }, @@ -467,7 +452,7 @@ func Test_run_Stop(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -479,7 +464,6 @@ func Test_run_Stop(t *testing.T) { } r := &run{ eg: test.fields.eg, - cfg: test.fields.cfg, loader: test.fields.loader, client: test.fields.client, } @@ -499,7 +483,6 @@ func Test_run_PostStop(t *testing.T) { } type fields struct { eg errgroup.Group - cfg *config.Data loader service.Loader client grpc.Client } @@ -531,7 +514,6 @@ func Test_run_PostStop(t *testing.T) { }, fields: fields { eg: nil, - cfg: nil, loader: nil, client: nil, }, @@ -550,7 +532,6 @@ func Test_run_PostStop(t *testing.T) { }, fields: fields { eg: nil, - cfg: nil, loader: nil, client: nil, }, @@ -563,7 +544,7 @@ func Test_run_PostStop(t *testing.T) { for _, test := range tests { t.Run(test.name, func(tt *testing.T) { - defer goleak.VerifyNone(t) + defer goleak.VerifyNone(tt) if test.beforeFunc != nil { test.beforeFunc(test.args) } @@ -575,7 +556,6 @@ func Test_run_PostStop(t *testing.T) { } r := &run{ eg: test.fields.eg, - cfg: test.fields.cfg, loader: test.fields.loader, client: test.fields.client, } From d02d1f834f600cd4fd6d292c4f8393d2ef603ed9 Mon Sep 17 00:00:00 2001 From: Rintaro Okamura Date: Thu, 11 Jun 2020 13:54:12 +0900 Subject: [PATCH 06/11] :recycle: refactor the order of declaration. Signed-off-by: Rintaro Okamura --- internal/db/storage/blob/s3/reader/reader.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/db/storage/blob/s3/reader/reader.go b/internal/db/storage/blob/s3/reader/reader.go index 7b57475add..1f513ded6a 100644 --- a/internal/db/storage/blob/s3/reader/reader.go +++ b/internal/db/storage/blob/s3/reader/reader.go @@ -59,8 +59,6 @@ func (r *reader) Open(ctx context.Context) (err error) { Key: aws.String(r.key), } - r.wg = new(sync.WaitGroup) - var pw io.WriteCloser r.pr, pw = io.Pipe() @@ -79,6 +77,7 @@ func (r *reader) Open(ctx context.Context) (err error) { return err } + r.wg = new(sync.WaitGroup) r.wg.Add(1) r.eg.Go(safety.RecoverFunc(func() (err error) { From 6d31953c87f45e4c0c9b723497b37384e282b18b Mon Sep 17 00:00:00 2001 From: Rintaro Okamura Date: Fri, 12 Jun 2020 12:02:59 +0900 Subject: [PATCH 07/11] :sparkles: Add internal/net/http/client Signed-off-by: Rintaro Okamura --- charts/vald/values.yaml | 80 +++++++ internal/config/sidecar.go | 11 +- internal/config/transport.go | 5 + internal/db/storage/blob/s3/session/option.go | 10 + .../db/storage/blob/s3/session/session.go | 8 + internal/net/http/client/client.go | 57 +++++ internal/net/http/client/option.go | 204 ++++++++++++++++++ .../usecase/initcontainer/initcontainer.go | 27 +++ pkg/agent/sidecar/usecase/sidecar/sidecar.go | 27 +++ 9 files changed, 428 insertions(+), 1 deletion(-) create mode 100644 internal/net/http/client/client.go create mode 100644 internal/net/http/client/option.go diff --git a/charts/vald/values.yaml b/charts/vald/values.yaml index c39127ba8e..f0f15fa60c 100644 --- a/charts/vald/values.yaml +++ b/charts/vald/values.yaml @@ -1318,6 +1318,86 @@ agent: # `lz4`: >= 0, higher is better compression. # `zstd`: 1 (fastest) to 22 (best), however implementation relies on klauspost/compress. compression_level: -1 + # @schema {"name": "agent.sidecar.config.client", "type": "object"} + client: + # @schema {"name": "agent.sidecar.config.client.tcp", "alias": "tcp"} + tcp: + dns: + # agent.sidecar.config.client.tcp.dns.cache_enabled -- HTTP client TCP DNS cache enabled + cache_enabled: true + # agent.sidecar.config.client.tcp.dns.refresh_duration -- HTTP client TCP DNS cache refresh duration + refresh_duration: 1h + # agent.sidecar.config.client.tcp.dns.refresh_duration -- HTTP client TCP DNS cache expiration + cache_expiration: 24h + dialer: + # agent.sidecar.config.client.tcp.dialer.timeout -- HTTP client TCP dialer connect timeout + timeout: 5s + # agent.sidecar.config.client.tcp.dialer.keep_alive -- HTTP client TCP dialer keep alive + keep_alive: 5m + # agent.sidecar.config.client.tcp.dialer.dual_stack_enabled -- HTTP client TCP dialer dual stack enabled + dual_stack_enabled: false + tls: + # agent.sidecar.config.client.tcp.tls.enabled -- HTTP client TCP TLS enabled + enabled: false + # agent.sidecar.config.client.tcp.tls.cert -- HTTP client TCP TLS cert path + cert: /path/to/cert + # agent.sidecar.config.client.tcp.tls.key -- HTTP client TCP TLS key path + key: /path/to/key + # agent.sidecar.config.client.tcp.tls.ca -- HTTP client TCP TLS ca path + ca: /path/to/ca + # @schema {"name": "agent.sidecar.config.client.transport", "type": "object"} + transport: + # @schema {"name": "agent.sidecar.config.client.transport.round_tripper", "type": "object"} + round_tripper: + # @schema {"name": "agent.sidecar.config.client.transport.round_tripper.tls_handshake_timeout", "type": "string"} + # agent.sidecar.config.client.transport.round_tripper.tls_handshake_timeout -- TLS handshake timeout + tls_handshake_timeout: 5s + # @schema {"name": "agent.sidecar.config.client.transport.round_tripper.max_idle_conns", "type": "integer"} + # agent.sidecar.config.client.transport.round_tripper.max_idle_conns -- maximum count of idle connections + max_idle_conns: 100 + # @schema {"name": "agent.sidecar.config.client.transport.round_tripper.max_idle_conns_per_host", "type": "integer"} + # agent.sidecar.config.client.transport.round_tripper.max_idle_conns_per_host -- maximum count of idle connections per host + max_idle_conns_per_host: 10 + # @schema {"name": "agent.sidecar.config.client.transport.round_tripper.max_conns_per_host", "type": "integer"} + # agent.sidecar.config.client.transport.round_tripper.max_conns_per_host -- maximum count of connections per host + max_conns_per_host: 10 + # @schema {"name": "agent.sidecar.config.client.transport.round_tripper.idle_conn_timeout", "type": "string"} + # agent.sidecar.config.client.transport.round_tripper.idle_conn_timeout -- timeout for idle connections + idle_conn_timeout: 90s + # @schema {"name": "agent.sidecar.config.client.transport.round_tripper.response_header_timeout", "type": "string"} + # agent.sidecar.config.client.transport.round_tripper.response_header_timeout -- timeout for response header + response_header_timeout: 5s + # @schema {"name": "agent.sidecar.config.client.transport.round_tripper.expect_continue_timeout", "type": "string"} + # agent.sidecar.config.client.transport.round_tripper.expect_continue_timeout -- expect continue timeout + expect_continue_timeout: 5s + # @schema {"name": "agent.sidecar.config.client.transport.round_tripper.max_response_header_size", "type": "integer"} + # agent.sidecar.config.client.transport.round_tripper.max_response_header_size -- maximum response header size + max_response_header_size: 0 + # @schema {"name": "agent.sidecar.config.client.transport.round_tripper.write_buffer_size", "type": "integer"} + # agent.sidecar.config.client.transport.round_tripper.write_buffer_size -- write buffer size + write_buffer_size: 0 + # @schema {"name": "agent.sidecar.config.client.transport.round_tripper.read_buffer_size", "type": "integer"} + # agent.sidecar.config.client.transport.round_tripper.read_buffer_size -- read buffer size + read_buffer_size: 0 + # @schema {"name": "agent.sidecar.config.client.transport.round_tripper.force_attempt_http_2", "type": "boolean"} + # agent.sidecar.config.client.transport.round_tripper.force_attempt_http_2 -- force attempt HTTP2 + force_attempt_http_2: true + # @schema {"name": "agent.sidecar.config.client.transport.backoff", "alias": "backoff"} + backoff: + # agent.sidecar.config.client.transport.backoff.initial_duration -- backoff initial duration + initial_duration: 5ms + # agent.sidecar.config.client.transport.backoff.backoff_time_limit -- backoff time limit + backoff_time_limit: 5s + # agent.sidecar.config.client.transport.backoff.maximum_duration -- backoff maximum duration + maximum_duration: 5s + # agent.sidecar.config.client.transport.backoff.jitter_limit -- backoff jitter limit + jitter_limit: 100ms + # agent.sidecar.config.client.transport.backoff.backoff_factor -- backoff backoff factor + backoff_factor: 1.1 + # agent.sidecar.config.client.transport.backoff.retry_count -- backoff retry count + retry_count: 100 + # agent.sidecar.config.client.transport.backoff.enable_error_log -- backoff error log enabled + enable_error_log: true # @schema {"name": "agent.sidecar.config.restore_backoff", "alias": "backoff"} restore_backoff: # agent.sidecar.config.restore_backoff.initial_duration -- restore backoff initial duration diff --git a/internal/config/sidecar.go b/internal/config/sidecar.go index 9614b721ed..34a7605efe 100644 --- a/internal/config/sidecar.go +++ b/internal/config/sidecar.go @@ -42,8 +42,11 @@ type AgentSidecar struct { // Compress represent compression configurations Compress *CompressCore `yaml:"compress" json:"compress"` - // RestoreBackoff repsresent backoff configurations for restoring process + // RestoreBackoff represent backoff configurations for restoring process RestoreBackoff *Backoff `yaml:"restore_backoff" json:"restore_backoff"` + + // Client represent HTTP client configurations + Client *Client `yaml:"client" json:"client"` } func (s *AgentSidecar) Bind() *AgentSidecar { @@ -72,5 +75,11 @@ func (s *AgentSidecar) Bind() *AgentSidecar { s.RestoreBackoff = new(Backoff) } + if s.Client != nil { + s.Client = s.Client.Bind() + } else { + s.Client = new(Client) + } + return s } diff --git a/internal/config/transport.go b/internal/config/transport.go index 91cec4b165..f47842db84 100644 --- a/internal/config/transport.go +++ b/internal/config/transport.go @@ -48,9 +48,14 @@ func (r *RoundTripper) Bind() *RoundTripper { func (t *Transport) Bind() *Transport { if t.RoundTripper != nil { t.RoundTripper = t.RoundTripper.Bind() + } else { + t.RoundTripper = new(RoundTripper) } + if t.Backoff != nil { t.Backoff = t.Backoff.Bind() + } else { + t.Backoff = new(Backoff) } return t diff --git a/internal/db/storage/blob/s3/session/option.go b/internal/db/storage/blob/s3/session/option.go index 73a952bebb..bd10f2f6ff 100644 --- a/internal/db/storage/blob/s3/session/option.go +++ b/internal/db/storage/blob/s3/session/option.go @@ -16,6 +16,8 @@ package session +import "net/http" + type Option func(s *sess) var ( @@ -129,3 +131,11 @@ func WithEnableEndpointHostPrefix(enabled bool) Option { s.enableEndpointHostPrefix = enabled } } + +func WithHTTPClient(client *http.Client) Option { + return func(s *sess) { + if client != nil { + s.client = client + } + } +} diff --git a/internal/db/storage/blob/s3/session/session.go b/internal/db/storage/blob/s3/session/session.go index 594374aa3a..ac0082f171 100644 --- a/internal/db/storage/blob/s3/session/session.go +++ b/internal/db/storage/blob/s3/session/session.go @@ -17,6 +17,8 @@ package session import ( + "net/http" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" @@ -40,6 +42,8 @@ type sess struct { enableContentMD5Validation bool enableEndpointDiscovery bool enableEndpointHostPrefix bool + + client *http.Client } type Session interface { @@ -110,5 +114,9 @@ func (s *sess) Session() (*session.Session, error) { cfg = cfg.WithDisableEndpointHostPrefix(true) } + if s.client != nil { + cfg = cfg.WithHTTPClient(s.client) + } + return session.NewSession(cfg) } diff --git a/internal/net/http/client/client.go b/internal/net/http/client/client.go new file mode 100644 index 0000000000..81bf470df3 --- /dev/null +++ b/internal/net/http/client/client.go @@ -0,0 +1,57 @@ +// +// Copyright (C) 2019-2020 Vdaas.org Vald team ( kpango, rinx, kmrmt ) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package client + +import ( + "net/http" + "reflect" + + "github.com/vdaas/vald/internal/backoff" + "github.com/vdaas/vald/internal/errors" + htr "github.com/vdaas/vald/internal/net/http/transport" + "golang.org/x/net/http2" +) + +type transport struct { + *http.Transport + backoffOpts []backoff.Option +} + +func New(opts ...Option) (*http.Client, error) { + tr := new(transport) + tr.Transport = new(http.Transport) + + for _, opt := range append(defaultOptions, opts...) { + if err := opt(tr); err != nil { + return nil, errors.ErrOptionFailed(err, reflect.ValueOf(opt)) + } + } + + err := http2.ConfigureTransport(tr.Transport) + if err != nil { + return nil, err + } + + return &http.Client{ + Transport: htr.NewExpBackoff( + htr.WithRoundTripper(tr.Transport), + htr.WithBackoff( + backoff.New(tr.backoffOpts...), + ), + ), + }, nil +} diff --git a/internal/net/http/client/option.go b/internal/net/http/client/option.go new file mode 100644 index 0000000000..0613534e4f --- /dev/null +++ b/internal/net/http/client/option.go @@ -0,0 +1,204 @@ +// +// Copyright (C) 2019-2020 Vdaas.org Vald team ( kpango, rinx, kmrmt ) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package client + +import ( + "context" + "net" + "net/http" + "net/url" + + "github.com/vdaas/vald/internal/backoff" + "github.com/vdaas/vald/internal/timeutil" +) + +type Option func(*transport) error + +var ( + defaultOptions = []Option{ + WithProxy(http.ProxyFromEnvironment), + WithEnableKeepAlives(true), + WithEnableCompression(true), + } +) + +func WithProxy(px func(*http.Request) (*url.URL, error)) Option { + return func(tr *transport) error { + if px != nil { + tr.Proxy = px + } + + return nil + } +} + +func WithDialContext(dx func(ctx context.Context, network, addr string) (net.Conn, error)) Option { + return func(tr *transport) error { + if dx != nil { + tr.DialContext = dx + } + + return nil + } + +} + +func WithTLSHandshakeTimeout(dur string) Option { + return func(tr *transport) error { + d, err := timeutil.Parse(dur) + if err != nil { + return err + } + + tr.TLSHandshakeTimeout = d + + return nil + } +} + +func WithEnableKeepAlives(enable bool) Option { + return func(tr *transport) error { + tr.DisableKeepAlives = !enable + + return nil + } +} + +func WithEnableCompression(enable bool) Option { + return func(tr *transport) error { + tr.DisableCompression = !enable + + return nil + } +} + +func WithMaxIdleConns(cn int) Option { + return func(tr *transport) error { + tr.MaxIdleConns = cn + + return nil + } +} + +func WithMaxIdleConnsPerHost(cn int) Option { + return func(tr *transport) error { + tr.MaxIdleConnsPerHost = cn + + return nil + } +} + +func WithMaxConnsPerHost(cn int) Option { + return func(tr *transport) error { + tr.MaxConnsPerHost = cn + + return nil + } +} + +func WithIdleConnTimeout(dur string) Option { + return func(tr *transport) error { + d, err := timeutil.Parse(dur) + if err != nil { + return err + } + + tr.IdleConnTimeout = d + + return nil + } +} + +func WithResponseHeaderTimeout(dur string) Option { + return func(tr *transport) error { + d, err := timeutil.Parse(dur) + if err != nil { + return err + } + + tr.ResponseHeaderTimeout = d + + return nil + } +} + +func WithExpectContinueTimeout(dur string) Option { + return func(tr *transport) error { + d, err := timeutil.Parse(dur) + if err != nil { + return err + } + + tr.ExpectContinueTimeout = d + + return nil + } +} + +func WithProxyConnectHeader(header http.Header) Option { + return func(tr *transport) error { + if header != nil { + tr.ProxyConnectHeader = header + } + + return nil + } +} + +func WithMaxResponseHeaderBytes(bs int64) Option { + return func(tr *transport) error { + tr.MaxResponseHeaderBytes = bs + + return nil + } +} + +func WithWriteBufferSize(bs int64) Option { + return func(tr *transport) error { + tr.WriteBufferSize = int(bs) + + return nil + } +} + +func WithReadBufferSize(bs int64) Option { + return func(tr *transport) error { + tr.ReadBufferSize = int(bs) + + return nil + } +} + +func WithForceAttemptHTTP2(force bool) Option { + return func(tr *transport) error { + tr.ForceAttemptHTTP2 = force + + return nil + } +} + +func WithBackoffOpts(opts ...backoff.Option) Option { + return func(tr *transport) error { + if tr.backoffOpts == nil { + tr.backoffOpts = opts + } + + tr.backoffOpts = append(tr.backoffOpts, opts...) + + return nil + } +} diff --git a/pkg/agent/sidecar/usecase/initcontainer/initcontainer.go b/pkg/agent/sidecar/usecase/initcontainer/initcontainer.go index 3e9e46b26b..752bc4a4a0 100644 --- a/pkg/agent/sidecar/usecase/initcontainer/initcontainer.go +++ b/pkg/agent/sidecar/usecase/initcontainer/initcontainer.go @@ -27,6 +27,8 @@ import ( "github.com/vdaas/vald/internal/log" "github.com/vdaas/vald/internal/net/grpc" "github.com/vdaas/vald/internal/net/grpc/metric" + "github.com/vdaas/vald/internal/net/http/client" + "github.com/vdaas/vald/internal/net/tcp" "github.com/vdaas/vald/internal/observability" "github.com/vdaas/vald/internal/runner" "github.com/vdaas/vald/internal/safety" @@ -67,6 +69,30 @@ func New(cfg *config.Data) (r runner.Runner, err error) { // TODO observe something _ = obs } + + dialer, err := tcp.NewDialer(cfg.AgentSidecar.Client.TCP.Opts()...) + if err != nil { + return nil, err + } + + client, err := client.New( + client.WithDialContext(dialer.DialContext), + client.WithTLSHandshakeTimeout(cfg.AgentSidecar.Client.Transport.RoundTripper.TLSHandshakeTimeout), + client.WithMaxIdleConns(cfg.AgentSidecar.Client.Transport.RoundTripper.MaxIdleConns), + client.WithMaxIdleConnsPerHost(cfg.AgentSidecar.Client.Transport.RoundTripper.MaxIdleConnsPerHost), + client.WithMaxConnsPerHost(cfg.AgentSidecar.Client.Transport.RoundTripper.MaxConnsPerHost), + client.WithIdleConnTimeout(cfg.AgentSidecar.Client.Transport.RoundTripper.IdleConnTimeout), + client.WithResponseHeaderTimeout(cfg.AgentSidecar.Client.Transport.RoundTripper.ResponseHeaderTimeout), + client.WithExpectContinueTimeout(cfg.AgentSidecar.Client.Transport.RoundTripper.ExpectContinueTimeout), + client.WithMaxResponseHeaderBytes(cfg.AgentSidecar.Client.Transport.RoundTripper.MaxResponseHeaderSize), + client.WithWriteBufferSize(cfg.AgentSidecar.Client.Transport.RoundTripper.WriteBufferSize), + client.WithReadBufferSize(cfg.AgentSidecar.Client.Transport.RoundTripper.ReadBufferSize), + client.WithForceAttemptHTTP2(cfg.AgentSidecar.Client.Transport.RoundTripper.ForceAttemptHTTP2), + ) + if err != nil { + return nil, err + } + bs, err = storage.New( storage.WithErrGroup(eg), storage.WithType(cfg.AgentSidecar.BlobStorage.StorageType), @@ -90,6 +116,7 @@ func New(cfg *config.Data) (r runner.Runner, err error) { session.WithEnableContentMD5Validation(cfg.AgentSidecar.BlobStorage.S3.EnableContentMD5Validation), session.WithEnableEndpointDiscovery(cfg.AgentSidecar.BlobStorage.S3.EnableEndpointDiscovery), session.WithEnableEndpointHostPrefix(cfg.AgentSidecar.BlobStorage.S3.EnableEndpointHostPrefix), + session.WithHTTPClient(client), ), storage.WithS3Opts( s3.WithMaxPartSize(cfg.AgentSidecar.BlobStorage.S3.MaxPartSize), diff --git a/pkg/agent/sidecar/usecase/sidecar/sidecar.go b/pkg/agent/sidecar/usecase/sidecar/sidecar.go index 438a476e5f..f3576464a2 100644 --- a/pkg/agent/sidecar/usecase/sidecar/sidecar.go +++ b/pkg/agent/sidecar/usecase/sidecar/sidecar.go @@ -27,6 +27,8 @@ import ( "github.com/vdaas/vald/internal/log" "github.com/vdaas/vald/internal/net/grpc" "github.com/vdaas/vald/internal/net/grpc/metric" + "github.com/vdaas/vald/internal/net/http/client" + "github.com/vdaas/vald/internal/net/tcp" "github.com/vdaas/vald/internal/observability" "github.com/vdaas/vald/internal/runner" "github.com/vdaas/vald/internal/safety" @@ -67,6 +69,30 @@ func New(cfg *config.Data) (r runner.Runner, err error) { // TODO observe something _ = obs } + + dialer, err := tcp.NewDialer(cfg.AgentSidecar.Client.TCP.Opts()...) + if err != nil { + return nil, err + } + + client, err := client.New( + client.WithDialContext(dialer.DialContext), + client.WithTLSHandshakeTimeout(cfg.AgentSidecar.Client.Transport.RoundTripper.TLSHandshakeTimeout), + client.WithMaxIdleConns(cfg.AgentSidecar.Client.Transport.RoundTripper.MaxIdleConns), + client.WithMaxIdleConnsPerHost(cfg.AgentSidecar.Client.Transport.RoundTripper.MaxIdleConnsPerHost), + client.WithMaxConnsPerHost(cfg.AgentSidecar.Client.Transport.RoundTripper.MaxConnsPerHost), + client.WithIdleConnTimeout(cfg.AgentSidecar.Client.Transport.RoundTripper.IdleConnTimeout), + client.WithResponseHeaderTimeout(cfg.AgentSidecar.Client.Transport.RoundTripper.ResponseHeaderTimeout), + client.WithExpectContinueTimeout(cfg.AgentSidecar.Client.Transport.RoundTripper.ExpectContinueTimeout), + client.WithMaxResponseHeaderBytes(cfg.AgentSidecar.Client.Transport.RoundTripper.MaxResponseHeaderSize), + client.WithWriteBufferSize(cfg.AgentSidecar.Client.Transport.RoundTripper.WriteBufferSize), + client.WithReadBufferSize(cfg.AgentSidecar.Client.Transport.RoundTripper.ReadBufferSize), + client.WithForceAttemptHTTP2(cfg.AgentSidecar.Client.Transport.RoundTripper.ForceAttemptHTTP2), + ) + if err != nil { + return nil, err + } + bs, err = storage.New( storage.WithErrGroup(eg), storage.WithType(cfg.AgentSidecar.BlobStorage.StorageType), @@ -90,6 +116,7 @@ func New(cfg *config.Data) (r runner.Runner, err error) { session.WithEnableContentMD5Validation(cfg.AgentSidecar.BlobStorage.S3.EnableContentMD5Validation), session.WithEnableEndpointDiscovery(cfg.AgentSidecar.BlobStorage.S3.EnableEndpointDiscovery), session.WithEnableEndpointHostPrefix(cfg.AgentSidecar.BlobStorage.S3.EnableEndpointHostPrefix), + session.WithHTTPClient(client), ), storage.WithS3Opts( s3.WithMaxPartSize(cfg.AgentSidecar.BlobStorage.S3.MaxPartSize), From 27919fa4f9efd7d65441f531c73beca90af430c9 Mon Sep 17 00:00:00 2001 From: Rintaro Okamura Date: Fri, 12 Jun 2020 12:06:27 +0900 Subject: [PATCH 08/11] :white_check_mark: fix tests Signed-off-by: Rintaro Okamura --- .../db/storage/blob/s3/session/option_test.go | 115 + internal/net/http/client/client_test.go | 101 + internal/net/http/client/option_test.go | 1966 +++++++++++++++++ 3 files changed, 2182 insertions(+) create mode 100644 internal/net/http/client/client_test.go create mode 100644 internal/net/http/client/option_test.go diff --git a/internal/db/storage/blob/s3/session/option_test.go b/internal/db/storage/blob/s3/session/option_test.go index 4d171a1f2e..763cd34fbc 100644 --- a/internal/db/storage/blob/s3/session/option_test.go +++ b/internal/db/storage/blob/s3/session/option_test.go @@ -17,6 +17,7 @@ package session import ( + "net/http" "testing" "go.uber.org/goleak" @@ -1845,3 +1846,117 @@ func TestWithEnableEndpointHostPrefix(t *testing.T) { }) } } + +func TestWithHTTPClient(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + client *http.Client + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + client: nil, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + client: nil, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithHTTPClient(test.args.client) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithHTTPClient(test.args.client) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} diff --git a/internal/net/http/client/client_test.go b/internal/net/http/client/client_test.go new file mode 100644 index 0000000000..e9b5596809 --- /dev/null +++ b/internal/net/http/client/client_test.go @@ -0,0 +1,101 @@ +// +// Copyright (C) 2019-2020 Vdaas.org Vald team ( kpango, rinx, kmrmt ) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package client + +import ( + "net/http" + "reflect" + "testing" + + "github.com/vdaas/vald/internal/errors" + "go.uber.org/goleak" +) + +func TestNew(t *testing.T) { + type args struct { + opts []Option + } + type want struct { + want *http.Client + err error + } + type test struct { + name string + args args + want want + checkFunc func(want, *http.Client, error) error + beforeFunc func(args) + afterFunc func(args) + } + defaultCheckFunc := func(w want, got *http.Client, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got = %v, want %v", got, w.want) + } + return nil + } + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + opts: nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + opts: nil, + }, + want: want{}, + checkFunc: defaultCheckFunc, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got, err := New(test.args.opts...) + if err := test.checkFunc(test.want, got, err); err != nil { + tt.Errorf("error = %v", err) + } + + }) + } +} diff --git a/internal/net/http/client/option_test.go b/internal/net/http/client/option_test.go new file mode 100644 index 0000000000..654ae41f93 --- /dev/null +++ b/internal/net/http/client/option_test.go @@ -0,0 +1,1966 @@ +// +// Copyright (C) 2019-2020 Vdaas.org Vald team ( kpango, rinx, kmrmt ) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package client + +import ( + "context" + "net" + "net/http" + "net/url" + "testing" + + "github.com/vdaas/vald/internal/backoff" + "go.uber.org/goleak" +) + +func TestWithProxy(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + px func(*http.Request) (*url.URL, error) + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + px: nil, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + px: nil, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithProxy(test.args.px) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithProxy(test.args.px) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithDialContext(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + dx func(ctx context.Context, network, addr string) (net.Conn, error) + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + dx: nil, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + dx: nil, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithDialContext(test.args.dx) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithDialContext(test.args.dx) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithTLSHandshakeTimeout(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + dur string + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + dur: "", + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + dur: "", + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithTLSHandshakeTimeout(test.args.dur) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithTLSHandshakeTimeout(test.args.dur) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithEnableKeepAlives(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + enable bool + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + enable: false, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + enable: false, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithEnableKeepAlives(test.args.enable) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithEnableKeepAlives(test.args.enable) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithEnableCompression(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + enable bool + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + enable: false, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + enable: false, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithEnableCompression(test.args.enable) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithEnableCompression(test.args.enable) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithMaxIdleConns(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + cn int + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + cn: 0, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + cn: 0, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithMaxIdleConns(test.args.cn) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithMaxIdleConns(test.args.cn) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithMaxIdleConnsPerHost(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + cn int + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + cn: 0, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + cn: 0, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithMaxIdleConnsPerHost(test.args.cn) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithMaxIdleConnsPerHost(test.args.cn) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithMaxConnsPerHost(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + cn int + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + cn: 0, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + cn: 0, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithMaxConnsPerHost(test.args.cn) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithMaxConnsPerHost(test.args.cn) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithIdleConnTimeout(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + dur string + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + dur: "", + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + dur: "", + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithIdleConnTimeout(test.args.dur) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithIdleConnTimeout(test.args.dur) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithResponseHeaderTimeout(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + dur string + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + dur: "", + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + dur: "", + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithResponseHeaderTimeout(test.args.dur) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithResponseHeaderTimeout(test.args.dur) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithExpectContinueTimeout(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + dur string + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + dur: "", + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + dur: "", + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithExpectContinueTimeout(test.args.dur) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithExpectContinueTimeout(test.args.dur) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithProxyConnectHeader(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + header http.Header + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + header: nil, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + header: nil, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithProxyConnectHeader(test.args.header) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithProxyConnectHeader(test.args.header) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithMaxResponseHeaderBytes(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + bs int64 + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + bs: 0, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + bs: 0, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithMaxResponseHeaderBytes(test.args.bs) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithMaxResponseHeaderBytes(test.args.bs) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithWriteBufferSize(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + bs int64 + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + bs: 0, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + bs: 0, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithWriteBufferSize(test.args.bs) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithWriteBufferSize(test.args.bs) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithReadBufferSize(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + bs int64 + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + bs: 0, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + bs: 0, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithReadBufferSize(test.args.bs) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithReadBufferSize(test.args.bs) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithForceAttemptHTTP2(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + force bool + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + force: false, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + force: false, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithForceAttemptHTTP2(test.args.force) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithForceAttemptHTTP2(test.args.force) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} + +func TestWithBackoffOpts(t *testing.T) { + // Change interface type to the type of object you are testing + type T = interface{} + type args struct { + opts []backoff.Option + } + type want struct { + obj *T + // Uncomment this line if the option returns an error, otherwise delete it + // err error + } + type test struct { + name string + args args + want want + // Use the first line if the option returns an error. otherwise use the second line + // checkFunc func(want, *T, error) error + // checkFunc func(want, *T) error + beforeFunc func(args) + afterFunc func(args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got error = %v, want %v", err, w.err) + } + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + // Uncomment this block if the option do not returns an error, otherwise delete it + /* + defaultCheckFunc := func(w want, obj *T) error { + if !reflect.DeepEqual(obj, w.obj) { + return errors.Errorf("got = %v, want %v", obj, w.obj) + } + return nil + } + */ + + tests := []test{ + // TODO test cases + /* + { + name: "test_case_1", + args: args { + opts: nil, + }, + want: want { + obj: new(T), + }, + }, + */ + + // TODO test cases + /* + func() test { + return test { + name: "test_case_2", + args: args { + opts: nil, + }, + want: want { + obj: new(T), + }, + } + }(), + */ + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + + // Uncomment this block if the option returns an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + + got := WithBackoffOpts(test.args.opts...) + obj := new(T) + if err := test.checkFunc(test.want, obj, got(obj)); err != nil { + tt.Errorf("error = %v", err) + } + */ + + // Uncomment this block if the option do not return an error, otherwise delete it + /* + if test.checkFunc == nil { + test.checkFunc = defaultCheckFunc + } + got := WithBackoffOpts(test.args.opts...) + obj := new(T) + got(obj) + if err := test.checkFunc(test.want, obj); err != nil { + tt.Errorf("error = %v", err) + } + */ + }) + } +} From b3dc6a0522a1ad4f2c5f09158c240ff24d735399 Mon Sep 17 00:00:00 2001 From: Rintaro Okamura Date: Fri, 12 Jun 2020 12:23:51 +0900 Subject: [PATCH 09/11] :wrench: Revise default duration Signed-off-by: Rintaro Okamura --- charts/vald/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/vald/values.yaml b/charts/vald/values.yaml index f0f15fa60c..d0070f802a 100644 --- a/charts/vald/values.yaml +++ b/charts/vald/values.yaml @@ -1403,9 +1403,9 @@ agent: # agent.sidecar.config.restore_backoff.initial_duration -- restore backoff initial duration initial_duration: 1s # agent.sidecar.config.restore_backoff.backoff_time_limit -- restore backoff time limit - backoff_time_limit: 1m + backoff_time_limit: 30m # agent.sidecar.config.restore_backoff.maximum_duration -- restore backoff maximum duration - maximum_duration: 30s + maximum_duration: 1m # agent.sidecar.config.restore_backoff.jitter_limit -- restore backoff jitter limit jitter_limit: 10s # agent.sidecar.config.restore_backoff.backoff_factor -- restore backoff factor From 35fe98945e421ef7014ceeddb3b24bd44507aefd Mon Sep 17 00:00:00 2001 From: Rintaro Okamura Date: Fri, 12 Jun 2020 12:30:11 +0900 Subject: [PATCH 10/11] :wrench: add sigkill for errors on initcontainer Signed-off-by: Rintaro Okamura --- pkg/agent/sidecar/service/restorer/restorer.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/agent/sidecar/service/restorer/restorer.go b/pkg/agent/sidecar/service/restorer/restorer.go index 7d20be04ea..0c37da06b1 100644 --- a/pkg/agent/sidecar/service/restorer/restorer.go +++ b/pkg/agent/sidecar/service/restorer/restorer.go @@ -130,6 +130,9 @@ func (r *restorer) startRestore(ctx context.Context) (<-chan error, error) { return nil, nil }) + if err != nil { + return p.Signal(syscall.SIGKILL) // TODO: #403 + } return p.Signal(syscall.SIGTERM) // TODO: #403 })) From 5278819468883b4cd1d1414e112d5dd35c9f709f Mon Sep 17 00:00:00 2001 From: vdaas-ci Date: Fri, 12 Jun 2020 09:54:39 +0000 Subject: [PATCH 11/11] :robot: Update license headers / Format go codes and yaml files Signed-off-by: vdaas-ci --- internal/errors/blob_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/errors/blob_test.go b/internal/errors/blob_test.go index 1e3fc094a2..8217c192b0 100644 --- a/internal/errors/blob_test.go +++ b/internal/errors/blob_test.go @@ -21,7 +21,6 @@ import ( "reflect" "testing" - "go.uber.org/goleak" )