github.com/kubeshop/testkube@v1.17.23/pkg/cloud/data/artifact/uploader.go (about)

     1  package artifact
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"encoding/json"
     7  	"io"
     8  	"net/http"
     9  
    10  	"github.com/kubeshop/testkube/pkg/api/v1/testkube"
    11  
    12  	"github.com/kubeshop/testkube/pkg/log"
    13  
    14  	"github.com/pkg/errors"
    15  
    16  	"github.com/kubeshop/testkube/pkg/cloud/data/executor"
    17  	"github.com/kubeshop/testkube/pkg/executor/scraper"
    18  )
    19  
    20  type CloudUploader struct {
    21  	executor executor.Executor
    22  	// skipVerify is used to skip TLS verification when artifacts
    23  	skipVerify bool
    24  }
    25  
    26  func NewCloudUploader(executor executor.Executor, skipVerify bool) *CloudUploader {
    27  	return &CloudUploader{executor: executor, skipVerify: skipVerify}
    28  }
    29  
    30  func (u *CloudUploader) Upload(ctx context.Context, object *scraper.Object, execution testkube.Execution) error {
    31  	log.DefaultLogger.Infow("cloud uploader is requesting signed URL", "file", object.Name, "folder", execution.Id, "size", object.Size)
    32  	req := &PutObjectSignedURLRequest{
    33  		Object:        object.Name,
    34  		ExecutionID:   execution.Id,
    35  		TestName:      execution.TestName,
    36  		TestSuiteName: execution.TestSuiteName,
    37  	}
    38  	signedURL, err := u.getSignedURL(ctx, req)
    39  	if err != nil {
    40  		return errors.Wrapf(err, "failed to get signed URL for object [%s]", req.Object)
    41  	}
    42  
    43  	log.DefaultLogger.Infow("cloud uploader is uploading file", "file", object.Name, "folder", req.ExecutionID, "size", object.Size)
    44  	if err := u.putObject(ctx, signedURL, object.Data); err != nil {
    45  		return errors.Wrapf(err, "failed to send object [%s] to cloud", req.Object)
    46  	}
    47  
    48  	return nil
    49  }
    50  
    51  func (u *CloudUploader) getSignedURL(ctx context.Context, req *PutObjectSignedURLRequest) (string, error) {
    52  	response, err := u.executor.Execute(ctx, CmdScraperPutObjectSignedURL, req)
    53  	if err != nil {
    54  		return "", err
    55  	}
    56  	var commandResponse PutObjectSignedURLResponse
    57  	if err := json.Unmarshal(response, &commandResponse); err != nil {
    58  		return "", err
    59  	}
    60  	return commandResponse.URL, nil
    61  }
    62  
    63  func (u *CloudUploader) putObject(ctx context.Context, url string, data io.Reader) error {
    64  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, data)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	req.Header.Set("Content-Type", "application/octet-stream")
    69  	tr := http.DefaultTransport.(*http.Transport).Clone()
    70  	tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: u.skipVerify}
    71  	client := &http.Client{Transport: tr}
    72  	rsp, err := client.Do(req)
    73  	if err != nil {
    74  		return errors.Wrap(err, "failed to send file to cloud")
    75  	}
    76  	if rsp.StatusCode != http.StatusOK {
    77  		return errors.Errorf("error getting file from presigned url: expected 200 OK response code, got %d", rsp.StatusCode)
    78  	}
    79  	return nil
    80  }
    81  
    82  func (u *CloudUploader) Close() error {
    83  	return u.executor.Close()
    84  }