2022-05-12 19:37:38 +00:00
|
|
|
package s3
|
|
|
|
|
|
|
|
import (
|
2022-11-30 14:47:31 +00:00
|
|
|
"crypto/tls"
|
2022-05-12 19:37:38 +00:00
|
|
|
"fmt"
|
2022-11-30 14:47:31 +00:00
|
|
|
"net/http"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
2022-05-12 19:37:38 +00:00
|
|
|
|
2024-01-23 15:21:51 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/stats"
|
2022-05-12 19:37:38 +00:00
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/config"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
|
|
"go.k6.io/k6/js/modules"
|
|
|
|
"go.k6.io/k6/metrics"
|
|
|
|
)
|
|
|
|
|
|
|
|
// RootModule is the global module object type. It is instantiated once per test
|
2022-12-29 14:52:55 +00:00
|
|
|
// run and will be used to create k6/x/frostfs/s3 module instances for each VU.
|
2022-05-12 19:37:38 +00:00
|
|
|
type RootModule struct{}
|
|
|
|
|
|
|
|
// S3 represents an instance of the module for every VU.
|
|
|
|
type S3 struct {
|
|
|
|
vu modules.VU
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the interfaces are implemented correctly.
|
|
|
|
var (
|
|
|
|
_ modules.Instance = &S3{}
|
|
|
|
_ modules.Module = &RootModule{}
|
|
|
|
|
2024-01-24 09:44:16 +00:00
|
|
|
objPutSuccess, objPutFails, objPutDuration, objPutData *metrics.Metric
|
|
|
|
objGetSuccess, objGetFails, objGetDuration, objGetData *metrics.Metric
|
|
|
|
objDeleteSuccess, objDeleteFails, objDeleteDuration *metrics.Metric
|
|
|
|
createBucketSuccess, createBucketFails, createBucketDuration *metrics.Metric
|
2022-05-12 19:37:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
2022-12-29 14:52:55 +00:00
|
|
|
modules.Register("k6/x/frostfs/s3", new(RootModule))
|
2022-05-12 19:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewModuleInstance implements the modules.Module interface and returns
|
|
|
|
// a new instance for each VU.
|
|
|
|
func (r *RootModule) NewModuleInstance(vu modules.VU) modules.Instance {
|
|
|
|
mi := &S3{vu: vu}
|
|
|
|
return mi
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exports implements the modules.Instance interface and returns the exports
|
|
|
|
// of the JS module.
|
|
|
|
func (s *S3) Exports() modules.Exports {
|
|
|
|
return modules.Exports{Default: s}
|
|
|
|
}
|
|
|
|
|
2022-11-30 14:47:31 +00:00
|
|
|
func (s *S3) Connect(endpoint string, params map[string]string) (*Client, error) {
|
2022-05-12 19:37:38 +00:00
|
|
|
resolver := aws.EndpointResolverWithOptionsFunc(func(_, _ string, _ ...interface{}) (aws.Endpoint, error) {
|
|
|
|
return aws.Endpoint{
|
|
|
|
URL: endpoint,
|
|
|
|
}, nil
|
|
|
|
})
|
|
|
|
|
|
|
|
cfg, err := config.LoadDefaultConfig(s.vu.Context(), config.WithEndpointResolverWithOptions(resolver))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("configuration error: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-11-30 14:47:31 +00:00
|
|
|
var noVerifySSL bool
|
|
|
|
if noVerifySSLStr, ok := params["no_verify_ssl"]; ok {
|
|
|
|
if noVerifySSL, err = strconv.ParseBool(noVerifySSLStr); err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid value for 'no_verify_ssl': '%s'", noVerifySSLStr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var timeout time.Duration
|
|
|
|
if timeoutStr, ok := params["timeout"]; ok {
|
|
|
|
if timeout, err = time.ParseDuration(timeoutStr); err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid value for 'timeout': '%s'", timeoutStr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-12 19:37:38 +00:00
|
|
|
cli := s3.NewFromConfig(cfg, func(options *s3.Options) {
|
|
|
|
// use 'domain/bucket/key' instead of default 'bucket.domain/key' scheme
|
|
|
|
options.UsePathStyle = true
|
|
|
|
// do not retry failed requests, by default client does up to 3 retry
|
|
|
|
options.Retryer = aws.NopRetryer{}
|
2022-11-30 14:47:31 +00:00
|
|
|
// s3 sometimes use self-signed certs
|
|
|
|
options.HTTPClient = &http.Client{
|
|
|
|
Transport: &http.Transport{
|
|
|
|
TLSClientConfig: &tls.Config{
|
|
|
|
InsecureSkipVerify: noVerifySSL,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Timeout: timeout,
|
|
|
|
}
|
2022-05-12 19:37:38 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
// register metrics
|
2024-01-24 09:44:16 +00:00
|
|
|
objPutSuccess, _ = stats.Registry.NewMetric("aws_obj_put_success", metrics.Counter)
|
2024-01-23 15:21:51 +00:00
|
|
|
objPutFails, _ = stats.Registry.NewMetric("aws_obj_put_fails", metrics.Counter)
|
|
|
|
objPutDuration, _ = stats.Registry.NewMetric("aws_obj_put_duration", metrics.Trend, metrics.Time)
|
2024-01-24 09:44:16 +00:00
|
|
|
objPutData, _ = stats.Registry.NewMetric("aws_obj_put_bytes", metrics.Counter, metrics.Data)
|
2024-01-23 15:21:51 +00:00
|
|
|
|
2024-01-24 09:44:16 +00:00
|
|
|
objGetSuccess, _ = stats.Registry.NewMetric("aws_obj_get_success", metrics.Counter)
|
2024-01-23 15:21:51 +00:00
|
|
|
objGetFails, _ = stats.Registry.NewMetric("aws_obj_get_fails", metrics.Counter)
|
|
|
|
objGetDuration, _ = stats.Registry.NewMetric("aws_obj_get_duration", metrics.Trend, metrics.Time)
|
2024-01-24 09:44:16 +00:00
|
|
|
objGetData, _ = stats.Registry.NewMetric("aws_obj_get_bytes", metrics.Counter, metrics.Data)
|
2024-01-23 15:21:51 +00:00
|
|
|
|
2024-01-24 09:44:16 +00:00
|
|
|
objDeleteSuccess, _ = stats.Registry.NewMetric("aws_obj_delete_success", metrics.Counter)
|
2024-01-23 15:21:51 +00:00
|
|
|
objDeleteFails, _ = stats.Registry.NewMetric("aws_obj_delete_fails", metrics.Counter)
|
|
|
|
objDeleteDuration, _ = stats.Registry.NewMetric("aws_obj_delete_duration", metrics.Trend, metrics.Time)
|
|
|
|
|
2024-01-24 09:44:16 +00:00
|
|
|
createBucketSuccess, _ = stats.Registry.NewMetric("aws_create_bucket_success", metrics.Counter)
|
2024-01-23 15:21:51 +00:00
|
|
|
createBucketFails, _ = stats.Registry.NewMetric("aws_create_bucket_fails", metrics.Counter)
|
|
|
|
createBucketDuration, _ = stats.Registry.NewMetric("aws_create_bucket_duration", metrics.Trend, metrics.Time)
|
2022-06-17 10:37:43 +00:00
|
|
|
|
2022-05-12 19:37:38 +00:00
|
|
|
return &Client{
|
|
|
|
vu: s.vu,
|
|
|
|
cli: cli,
|
|
|
|
}, nil
|
|
|
|
}
|