327 lines
9.8 KiB
Go
327 lines
9.8 KiB
Go
/**
|
|
MIT License
|
|
|
|
Copyright (c) 2023 API Testing Authors.
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
package pkg
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"log"
|
|
"strings"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
|
"github.com/aws/aws-sdk-go/service/s3"
|
|
"github.com/linuxsuren/api-testing/pkg/extension"
|
|
"github.com/linuxsuren/api-testing/pkg/server"
|
|
"github.com/linuxsuren/api-testing/pkg/testing"
|
|
"github.com/linuxsuren/api-testing/pkg/testing/remote"
|
|
"github.com/linuxsuren/api-testing/pkg/util"
|
|
"github.com/linuxsuren/api-testing/pkg/version"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type s3Client struct {
|
|
S3Creator S3Creator
|
|
remote.UnimplementedLoaderServer
|
|
}
|
|
|
|
func NewRemoteServer(s3Creator S3Creator) remote.LoaderServer {
|
|
return &s3Client{S3Creator: s3Creator}
|
|
}
|
|
|
|
func (s *s3Client) ListTestSuite(ctx context.Context, _ *server.Empty) (suites *remote.TestSuites, err error) {
|
|
suites = &remote.TestSuites{}
|
|
var client *s3WithBucket
|
|
if client, err = s.getClient(ctx); err != nil || client == nil {
|
|
return
|
|
}
|
|
|
|
var list *s3.ListObjectsOutput
|
|
if list, err = client.ListObjectsWithContext(ctx, &s3.ListObjectsInput{
|
|
Bucket: aws.String(client.bucket),
|
|
}); err == nil && list != nil {
|
|
suites, err = listObjectsOutputToTestSuite(ctx, list, client)
|
|
}
|
|
return
|
|
}
|
|
func listObjectsOutputToTestSuite(ctx context.Context, list *s3.ListObjectsOutput, client *s3WithBucket) (
|
|
suites *remote.TestSuites, err error) {
|
|
var suite *testing.TestSuite
|
|
suites = &remote.TestSuites{}
|
|
for _, obj := range list.Contents {
|
|
if !strings.HasSuffix(*obj.Key, ".yaml") {
|
|
continue
|
|
}
|
|
|
|
var objOutput *s3.GetObjectOutput
|
|
if objOutput, err = client.GetObjectWithContext(ctx, &s3.GetObjectInput{
|
|
Bucket: aws.String(client.bucket),
|
|
Key: obj.Key,
|
|
}); err == nil {
|
|
data := objOutput.Body
|
|
if suite, err = testing.ParseFromStream(data); err == nil {
|
|
suites.Data = append(suites.Data, remote.ConvertToGRPCTestSuite(suite))
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
func (s *s3Client) CreateTestSuite(ctx context.Context, testSuite *remote.TestSuite) (reply *server.Empty, err error) {
|
|
suite := remote.ConvertToNormalTestSuite(testSuite)
|
|
reply = &server.Empty{}
|
|
|
|
var data []byte
|
|
if data, err = yaml.Marshal(suite); err == nil {
|
|
var client *s3WithBucket
|
|
if client, err = s.getClient(ctx); err == nil {
|
|
_, err = client.PutObjectWithContext(ctx, &s3.PutObjectInput{
|
|
Bucket: aws.String(client.bucket),
|
|
Key: generateKey(suite.Name),
|
|
Body: bytes.NewReader(data),
|
|
})
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
func (s *s3Client) GetTestSuite(ctx context.Context, suite *remote.TestSuite) (reply *remote.TestSuite, err error) {
|
|
reply = &remote.TestSuite{}
|
|
var client *s3WithBucket
|
|
if client, err = s.getClient(ctx); err == nil && client != nil {
|
|
var objOutput *s3.GetObjectOutput
|
|
if objOutput, err = client.GetObjectWithContext(ctx, &s3.GetObjectInput{
|
|
Bucket: aws.String(client.bucket),
|
|
Key: generateKey(suite.Name),
|
|
}); err == nil && objOutput != nil {
|
|
data := objOutput.Body
|
|
|
|
var suite *testing.TestSuite
|
|
if suite, err = testing.ParseFromStream(data); err == nil {
|
|
reply = remote.ConvertToGRPCTestSuite(suite)
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
func (s *s3Client) UpdateTestSuite(ctx context.Context, suite *remote.TestSuite) (reply *remote.TestSuite, err error) {
|
|
reply = &remote.TestSuite{}
|
|
var oldSuite *remote.TestSuite
|
|
if oldSuite, err = s.GetTestSuite(ctx, suite); err == nil {
|
|
suite.Items = oldSuite.Items
|
|
_, err = s.CreateTestSuite(ctx, suite)
|
|
}
|
|
return
|
|
}
|
|
func (s *s3Client) DeleteTestSuite(ctx context.Context, suite *remote.TestSuite) (reply *server.Empty, err error) {
|
|
reply = &server.Empty{}
|
|
var client *s3WithBucket
|
|
if client, err = s.getClient(ctx); err == nil && client != nil {
|
|
_, err = client.DeleteObjectWithContext(ctx, &s3.DeleteObjectInput{
|
|
Bucket: aws.String(client.bucket),
|
|
Key: generateKey(suite.Name),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
func (s *s3Client) ListTestCases(ctx context.Context, suite *remote.TestSuite) (result *server.TestCases, err error) {
|
|
if suite, err = s.GetTestSuite(ctx, suite); err == nil {
|
|
result = &server.TestCases{
|
|
Data: suite.Items,
|
|
}
|
|
}
|
|
return
|
|
}
|
|
func (s *s3Client) CreateTestCase(ctx context.Context, testcase *server.TestCase) (reply *server.Empty, err error) {
|
|
reply = &server.Empty{}
|
|
|
|
var suite *remote.TestSuite
|
|
if suite, err = s.GetTestSuite(ctx, &remote.TestSuite{
|
|
Name: testcase.SuiteName,
|
|
}); err == nil {
|
|
suite.Items = append(suite.Items, testcase)
|
|
_, err = s.CreateTestSuite(ctx, suite)
|
|
}
|
|
return
|
|
}
|
|
func (s *s3Client) GetTestCase(ctx context.Context, testcase *server.TestCase) (result *server.TestCase, err error) {
|
|
var suite *remote.TestSuite
|
|
if suite, err = s.GetTestSuite(ctx, &remote.TestSuite{
|
|
Name: testcase.SuiteName,
|
|
}); err == nil {
|
|
result = getTestCaseByName(suite, testcase.Name)
|
|
}
|
|
return
|
|
}
|
|
func (s *s3Client) UpdateTestCase(ctx context.Context, testcase *server.TestCase) (reply *server.TestCase, err error) {
|
|
reply = &server.TestCase{}
|
|
var suite *remote.TestSuite
|
|
if suite, err = s.GetTestSuite(ctx, &remote.TestSuite{
|
|
Name: testcase.SuiteName,
|
|
}); err == nil {
|
|
suite = updateTestCase(suite, testcase)
|
|
_, err = s.CreateTestSuite(ctx, suite)
|
|
}
|
|
return
|
|
}
|
|
func (s *s3Client) DeleteTestCase(ctx context.Context, testcase *server.TestCase) (reply *server.Empty, err error) {
|
|
var suite *remote.TestSuite
|
|
if suite, err = s.GetTestSuite(ctx, &remote.TestSuite{
|
|
Name: testcase.SuiteName,
|
|
}); err == nil {
|
|
suite = removeTestCaseByName(suite, testcase.Name)
|
|
_, err = s.UpdateTestSuite(ctx, suite)
|
|
}
|
|
return
|
|
}
|
|
func (s *s3Client) Verify(ctx context.Context, in *server.Empty) (reply *server.ExtensionStatus, err error) {
|
|
_, clientErr := s.ListTestSuite(ctx, in)
|
|
reply = &server.ExtensionStatus{
|
|
Ready: err == nil,
|
|
Message: util.OKOrErrorMessage(clientErr),
|
|
Version: version.GetVersion(),
|
|
}
|
|
return
|
|
}
|
|
func (s *s3Client) PProf(ctx context.Context, in *server.PProfRequest) (data *server.PProfData, err error) {
|
|
log.Println("pprof", in.Name)
|
|
|
|
data = &server.PProfData{
|
|
Data: extension.LoadPProf(in.Name),
|
|
}
|
|
return
|
|
}
|
|
func (s *s3Client) getClient(ctx context.Context) (db *s3WithBucket, err error) {
|
|
store := remote.GetStoreFromContext(ctx)
|
|
if store == nil {
|
|
err = errors.New("no connect to s3 server")
|
|
} else {
|
|
var ok bool
|
|
if db, ok = clientCache[store.Name]; ok && db != nil {
|
|
return
|
|
}
|
|
|
|
options := mapToS3Options(store.Properties)
|
|
if options.AccessKeyID == "" {
|
|
options.AccessKeyID = store.Username
|
|
}
|
|
if options.SecretAccessKey == "" {
|
|
options.SecretAccessKey = store.Password
|
|
}
|
|
|
|
log.Println("s3 server", store.URL)
|
|
|
|
var sess *session.Session
|
|
sess, err = createClientFromSs3Options(options, store.URL)
|
|
if err == nil {
|
|
svc := s.S3Creator.New(sess)
|
|
db = &s3WithBucket{S3API: svc, bucket: options.Bucket}
|
|
clientCache[store.Name] = db
|
|
}
|
|
}
|
|
return
|
|
}
|
|
func createClientFromSs3Options(options s3Options, storeURL string) (sess *session.Session, err error) {
|
|
cred := credentials.NewStaticCredentials(options.AccessKeyID, options.SecretAccessKey, options.SessionToken)
|
|
|
|
config := aws.Config{
|
|
Region: aws.String(options.Region),
|
|
Endpoint: aws.String(storeURL),
|
|
DisableSSL: aws.Bool(options.DisableSSL),
|
|
S3ForcePathStyle: aws.Bool(options.ForcePathStyle),
|
|
Credentials: cred,
|
|
}
|
|
|
|
sess, err = session.NewSession(&config)
|
|
return
|
|
}
|
|
|
|
func mapToS3Options(data map[string]string) (opt s3Options) {
|
|
opt.AccessKeyID = data["accesskeyid"]
|
|
opt.SecretAccessKey = data["secretaccesskey"]
|
|
opt.SessionToken = data["sessiontoken"]
|
|
opt.Region = data["region"]
|
|
opt.DisableSSL = data["disablessl"] == "true"
|
|
opt.ForcePathStyle = data["forcepathstyle"] == "true"
|
|
opt.Bucket = data["bucket"]
|
|
return
|
|
}
|
|
|
|
func generateKey(name string) *string {
|
|
return aws.String(name + ".yaml")
|
|
}
|
|
|
|
func removeTestCaseByName(suite *remote.TestSuite, name string) *remote.TestSuite {
|
|
for i, item := range suite.Items {
|
|
if item.Name == name {
|
|
suite.Items = append(suite.Items[:i], suite.Items[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
return suite
|
|
}
|
|
|
|
func updateTestCase(suite *remote.TestSuite, testcase *server.TestCase) *remote.TestSuite {
|
|
for i, item := range suite.Items {
|
|
if item.Name == testcase.Name {
|
|
suite.Items[i] = testcase
|
|
break
|
|
}
|
|
}
|
|
return suite
|
|
}
|
|
|
|
func getTestCaseByName(suite *remote.TestSuite, name string) (result *server.TestCase) {
|
|
for _, item := range suite.Items {
|
|
if item.Name == name {
|
|
result = item
|
|
break
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
type s3Options struct {
|
|
// AWS Access key ID
|
|
AccessKeyID string `yaml:"accessKeyID"`
|
|
// AWS Secret Access Key
|
|
SecretAccessKey string `yaml:"secretAccessKey"`
|
|
// AWS Session Token
|
|
SessionToken string `yaml:"sessionToken"`
|
|
// AWS Region
|
|
Region string `yaml:"region"`
|
|
DisableSSL bool `yaml:"disableSSL"`
|
|
ForcePathStyle bool `yaml:"forcePathStyle"`
|
|
Bucket string `yaml:"bucket"`
|
|
}
|
|
|
|
type s3WithBucket struct {
|
|
S3API
|
|
bucket string
|
|
}
|
|
|
|
var clientCache map[string]*s3WithBucket = make(map[string]*s3WithBucket)
|