mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 15:59:41 +02:00
feat(oci): oci helm support [r8s-361] (#787)
This commit is contained in:
parent
b6a6ce9aaf
commit
2697d6c5d7
80 changed files with 4264 additions and 812 deletions
130
api/http/proxy/factory/gitlab/client.go
Normal file
130
api/http/proxy/factory/gitlab/client.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
package gitlab
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/segmentio/encoding/json"
|
||||
"oras.land/oras-go/v2/registry/remote/retry"
|
||||
)
|
||||
|
||||
// Repository represents a GitLab registry repository
|
||||
type Repository struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
ProjectID int `json:"project_id"`
|
||||
Location string `json:"location"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// Client represents a GitLab API client
|
||||
type Client struct {
|
||||
httpClient *http.Client
|
||||
baseURL string
|
||||
}
|
||||
|
||||
// NewClient creates a new GitLab API client
|
||||
// it currently is an http client because only GetRegistryRepositoryNames is needed (oras supports other commands).
|
||||
// if we need to support other commands, consider using the gitlab client library.
|
||||
func NewClient(baseURL, token string) *Client {
|
||||
return &Client{
|
||||
httpClient: NewHTTPClient(token),
|
||||
baseURL: baseURL,
|
||||
}
|
||||
}
|
||||
|
||||
// GetRegistryRepositoryNames fetches registry repository names for a given project.
|
||||
// It's a small http client wrapper instead of using the gitlab client library because listing repositories is the only known operation that isn't directly supported by oras
|
||||
func (c *Client) GetRegistryRepositoryNames(ctx context.Context, projectID int) ([]string, error) {
|
||||
url := fmt.Sprintf("%s/api/v4/projects/%d/registry/repositories", c.baseURL, projectID)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("GitLab API returned status %d: %s", resp.StatusCode, resp.Status)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response body: %w", err)
|
||||
}
|
||||
|
||||
var repositories []Repository
|
||||
if err := json.Unmarshal(body, &repositories); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse response: %w", err)
|
||||
}
|
||||
|
||||
// Extract repository names
|
||||
names := make([]string, len(repositories))
|
||||
for i, repo := range repositories {
|
||||
// the full path is required for further repo operations
|
||||
names[i] = repo.Path
|
||||
}
|
||||
|
||||
return names, nil
|
||||
}
|
||||
|
||||
type Transport struct {
|
||||
httpTransport *http.Transport
|
||||
}
|
||||
|
||||
// NewTransport returns a pointer to a new instance of Transport that implements the HTTP Transport
|
||||
// interface for proxying requests to the Gitlab API.
|
||||
func NewTransport() *Transport {
|
||||
return &Transport{
|
||||
httpTransport: &http.Transport{},
|
||||
}
|
||||
}
|
||||
|
||||
// RoundTrip is the implementation of the http.RoundTripper interface
|
||||
func (transport *Transport) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
token := request.Header.Get("Private-Token")
|
||||
if token == "" {
|
||||
return nil, errors.New("no gitlab token provided")
|
||||
}
|
||||
|
||||
r, err := http.NewRequest(request.Method, request.URL.String(), request.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.Header.Set("Private-Token", token)
|
||||
return transport.httpTransport.RoundTrip(r)
|
||||
}
|
||||
|
||||
// NewHTTPClient creates a new HTTP client configured for GitLab API requests
|
||||
func NewHTTPClient(token string) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: &tokenTransport{
|
||||
token: token,
|
||||
transport: retry.NewTransport(&http.Transport{}), // Use ORAS retry transport for consistent rate limiting and error handling
|
||||
},
|
||||
Timeout: 1 * time.Minute,
|
||||
}
|
||||
}
|
||||
|
||||
// tokenTransport automatically adds the Private-Token header to requests
|
||||
type tokenTransport struct {
|
||||
token string
|
||||
transport http.RoundTripper
|
||||
}
|
||||
|
||||
func (t *tokenTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
req.Header.Set("Private-Token", t.token)
|
||||
return t.transport.RoundTrip(req)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue