Skip to content

Commit

Permalink
begin a new APK client (#1218)
Browse files Browse the repository at this point in the history
The idea here is to orient remote APK interactions around a **client**,
in order to give consumers the ability to handle dependency injection
properly (such as by injecting an HTTP client).

And the ultimate goal is to re-use this client whenever we need to
access remote indexes and packages. It'd be great if we can refine this
API enough where we can reuse this in all the places we need to interact
with APK repositories, to replace the many disparate implementations of
this logic we have in our tools today.

I haven't gone as far as to refactor other code in apko to use this
client, but I think that's a reasonable next step. So far this
implementation saves me from writing it again in an internal tool.

Feedback welcome. 😃 

cc: @imjasonh

---------

Signed-off-by: Dan Luhring <dluhring@chainguard.dev>
  • Loading branch information
luhring authored Jul 24, 2024
1 parent 402f0c6 commit 636d87f
Showing 1 changed file with 66 additions and 0 deletions.
66 changes: 66 additions & 0 deletions pkg/apk/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package client

import (
"context"
"fmt"
"net/http"
"net/url"

"chainguard.dev/apko/pkg/apk/apk"
"chainguard.dev/apko/pkg/apk/auth"
)

const (
WolfiAPKRepo = "https://packages.wolfi.dev/os"
ChainguardEnterpriseAPKRepo = "https://packages.cgr.dev/os"
ChainguardExtrasAPKRepo = "https://packages.cgr.dev/extras"
)

const (
Aarch64Arch = "aarch64"
X86_64Arch = "x86_64"
)

// Client is a client for interacting with an APK package repository.
type Client struct {
httpClient *http.Client
}

// New creates a new Client, suitable for accessing remote APK indexes and
// packages.
func New(httpClient *http.Client) *Client {
if httpClient == nil {
httpClient = http.DefaultClient
}
return &Client{httpClient: httpClient}
}

// GetRemoteIndex retrieves the index of APK packages from the specified remote
// repository.
func (c Client) GetRemoteIndex(ctx context.Context, apkRepo, arch string) (*apk.APKIndex, error) {
indexURL := apk.IndexURL(apkRepo, arch)

u, err := url.Parse(indexURL)
if err != nil {
return nil, fmt.Errorf("parsing %q: %w", indexURL, err)
}

req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil {
return nil, fmt.Errorf("GET %q: %w", u.Redacted(), err)
}
if err := auth.DefaultAuthenticators.AddAuth(ctx, req); err != nil {
return nil, fmt.Errorf("error adding auth: %w", err)
}

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("GET %q: %w", u.Redacted(), err)
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
return nil, fmt.Errorf("GET %q: status %d: %s", u.Redacted(), resp.StatusCode, resp.Status)
}

return apk.IndexFromArchive(resp.Body)
}

0 comments on commit 636d87f

Please sign in to comment.