This is done by inspecting the current hostname to determine if tenancy is enabled. The attestation commands also accepts a --hostname parameter, that is used to pick the current host, similar to how the GH_HOST variable can be used. Signed-off-by: Fredrik Skogman <kommendorkapten@github.com>
120 lines
3.3 KiB
Go
120 lines
3.3 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/cli/cli/v2/api"
|
|
ioconfig "github.com/cli/cli/v2/pkg/cmd/attestation/io"
|
|
)
|
|
|
|
const (
|
|
DefaultLimit = 30
|
|
maxLimitForFlag = 1000
|
|
maxLimitForFetch = 100
|
|
)
|
|
|
|
type apiClient interface {
|
|
REST(hostname, method, p string, body io.Reader, data interface{}) error
|
|
RESTWithNext(hostname, method, p string, body io.Reader, data interface{}) (string, error)
|
|
}
|
|
|
|
type Client interface {
|
|
GetByRepoAndDigest(repo, digest string, limit int) ([]*Attestation, error)
|
|
GetByOwnerAndDigest(owner, digest string, limit int) ([]*Attestation, error)
|
|
GetTrustDomain() (string, error)
|
|
}
|
|
|
|
type LiveClient struct {
|
|
api apiClient
|
|
host string
|
|
logger *ioconfig.Handler
|
|
}
|
|
|
|
func NewLiveClient(hc *http.Client, host string, l *ioconfig.Handler) *LiveClient {
|
|
return &LiveClient{
|
|
api: api.NewClientFromHTTP(hc),
|
|
host: strings.TrimSuffix(host, "/"),
|
|
logger: l,
|
|
}
|
|
}
|
|
|
|
func (c *LiveClient) BuildRepoAndDigestURL(repo, digest string) string {
|
|
repo = strings.Trim(repo, "/")
|
|
return fmt.Sprintf(GetAttestationByRepoAndSubjectDigestPath, repo, digest)
|
|
}
|
|
|
|
// GetByRepoAndDigest fetches the attestation by repo and digest
|
|
func (c *LiveClient) GetByRepoAndDigest(repo, digest string, limit int) ([]*Attestation, error) {
|
|
url := c.BuildRepoAndDigestURL(repo, digest)
|
|
return c.getAttestations(url, repo, digest, limit)
|
|
}
|
|
|
|
func (c *LiveClient) BuildOwnerAndDigestURL(owner, digest string) string {
|
|
owner = strings.Trim(owner, "/")
|
|
return fmt.Sprintf(GetAttestationByOwnerAndSubjectDigestPath, owner, digest)
|
|
}
|
|
|
|
// GetByOwnerAndDigest fetches attestation by owner and digest
|
|
func (c *LiveClient) GetByOwnerAndDigest(owner, digest string, limit int) ([]*Attestation, error) {
|
|
url := c.BuildOwnerAndDigestURL(owner, digest)
|
|
return c.getAttestations(url, owner, digest, limit)
|
|
}
|
|
|
|
// GetTrustDomain returns the current trust domain. If the default is used
|
|
// the empty string is returned
|
|
func (c *LiveClient) GetTrustDomain() (string, error) {
|
|
return c.getTrustDomain(MetaPath)
|
|
}
|
|
|
|
func (c *LiveClient) getAttestations(url, name, digest string, limit int) ([]*Attestation, error) {
|
|
c.logger.VerbosePrintf("Fetching attestations for artifact digest %s\n\n", digest)
|
|
|
|
perPage := limit
|
|
if perPage <= 0 || perPage > maxLimitForFlag {
|
|
return nil, fmt.Errorf("limit must be greater than 0 and less than or equal to %d", maxLimitForFlag)
|
|
}
|
|
|
|
if perPage > maxLimitForFetch {
|
|
perPage = maxLimitForFetch
|
|
}
|
|
|
|
// ref: https://github.com/cli/go-gh/blob/d32c104a9a25c9de3d7c7b07a43ae0091441c858/example_gh_test.go#L96
|
|
url = fmt.Sprintf("%s?per_page=%d", url, perPage)
|
|
|
|
var attestations []*Attestation
|
|
var resp AttestationsResponse
|
|
var err error
|
|
// if no attestation or less than limit, then keep fetching
|
|
for url != "" && len(attestations) < limit {
|
|
url, err = c.api.RESTWithNext(c.host, http.MethodGet, url, nil, &resp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
attestations = append(attestations, resp.Attestations...)
|
|
}
|
|
|
|
if len(attestations) == 0 {
|
|
return nil, newErrNoAttestations(name, digest)
|
|
}
|
|
|
|
if len(attestations) > limit {
|
|
return attestations[:limit], nil
|
|
}
|
|
|
|
return attestations, nil
|
|
}
|
|
|
|
func (c *LiveClient) getTrustDomain(url string) (string, error) {
|
|
var resp MetaResponse
|
|
|
|
err := c.api.REST(c.host, http.MethodGet, url, nil, &resp)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return resp.Domains.ArtifactAttestations.TrustDomain, nil
|
|
}
|