github.com/kubeshop/testkube@v1.17.23/pkg/api/v1/client/cloud_client.go (about)

     1  package client
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  func NewCloudClient[A All](httpClient *http.Client, apiURI, apiPathPrefix string) CloudClient[A] {
    16  	if apiPathPrefix == "" {
    17  		apiPathPrefix = "/" + Version
    18  	}
    19  
    20  	return CloudClient[A]{
    21  		client:        httpClient,
    22  		sseClient:     httpClient,
    23  		apiURI:        apiURI,
    24  		apiPathPrefix: apiPathPrefix,
    25  		DirectClient:  NewDirectClient[A](httpClient, apiURI, apiPathPrefix),
    26  	}
    27  }
    28  
    29  // CLoudClient is almost the same as Direct client, but has different GetFile method
    30  // which returns a download URL for the artifact instead of downloading it.
    31  type CloudClient[A All] struct {
    32  	client        *http.Client
    33  	sseClient     *http.Client
    34  	apiURI        string
    35  	apiPathPrefix string
    36  	DirectClient[A]
    37  }
    38  
    39  type ArtifactURL struct {
    40  	// Download URL for the artifact.
    41  	URL string `json:"url"`
    42  }
    43  
    44  // GetFile, in cloud we need to call non
    45  func (t CloudClient[A]) GetFile(uri, fileName, destination string, params map[string][]string) (name string, err error) {
    46  
    47  	cloudURI := strings.Replace(uri, "/agent", "", -1)
    48  	req, err := http.NewRequest(http.MethodGet, cloudURI, nil)
    49  	if err != nil {
    50  		return "", err
    51  	}
    52  
    53  	q := req.URL.Query()
    54  	for key, values := range params {
    55  		for _, value := range values {
    56  			if value != "" {
    57  				q.Add(key, value)
    58  			}
    59  		}
    60  	}
    61  	req.URL.RawQuery = q.Encode()
    62  
    63  	resp, err := t.client.Do(req)
    64  	if err != nil {
    65  		return name, err
    66  	}
    67  	defer resp.Body.Close()
    68  
    69  	if resp.StatusCode > 299 {
    70  		return name, fmt.Errorf("error: %d", resp.StatusCode)
    71  	}
    72  
    73  	var artifactURL ArtifactURL
    74  	err = json.NewDecoder(resp.Body).Decode(&artifactURL)
    75  	if err != nil {
    76  		return "", err
    77  	}
    78  
    79  	req, err = http.NewRequest(http.MethodGet, artifactURL.URL, nil)
    80  	if err != nil {
    81  		return "", err
    82  	}
    83  	resp, err = t.client.Do(req)
    84  	if err != nil {
    85  		return name, err
    86  	}
    87  	defer resp.Body.Close()
    88  
    89  	target := filepath.Join(destination, fileName)
    90  	dir := filepath.Dir(target)
    91  	if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) {
    92  		if err = os.MkdirAll(dir, os.ModePerm); err != nil {
    93  			return name, err
    94  		}
    95  	} else if err != nil {
    96  		return name, err
    97  	}
    98  
    99  	f, err := os.Create(target)
   100  	if err != nil {
   101  		return name, err
   102  	}
   103  
   104  	if _, err = io.Copy(f, resp.Body); err != nil {
   105  		return name, err
   106  	}
   107  
   108  	if err = t.responseError(resp); err != nil {
   109  		return name, fmt.Errorf("api/download-file returned error: %w", err)
   110  	}
   111  
   112  	return f.Name(), nil
   113  }