Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove obsolete DownloadImage() function, from sylabs159 #75

Merged
merged 10 commits into from
Apr 5, 2024
26 changes: 13 additions & 13 deletions client/api.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2018, Sylabs Inc. All rights reserved.
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.
Expand Down Expand Up @@ -138,10 +138,10 @@
}

for _, tag := range tags {
c.Logger.Logf("Setting tag %s", tag)
c.logger.Logf("Setting tag %s", tag)

if _, ok := existingTags[tag]; ok {
c.Logger.Logf("%s replaces an existing tag", tag)
c.logger.Logf("%s replaces an existing tag", tag)

Check warning on line 144 in client/api.go

View check run for this annotation

Codecov / codecov/patch

client/api.go#L144

Added line #L144 was not covered by tests
}

imgTag := ImageTag{
Expand All @@ -159,12 +159,12 @@
// getTags returns a tag map for the specified containerID
func (c *Client) getTags(ctx context.Context, containerID string) (TagMap, error) {
url := fmt.Sprintf("v1/tags/%s", containerID)
c.Logger.Logf("getTags calling %s", url)
c.logger.Logf("getTags calling %s", url)
req, err := c.newRequest(ctx, http.MethodGet, url, "", nil)
if err != nil {
return nil, fmt.Errorf("error creating request to server:\n\t%v", err)
}
res, err := c.HTTPClient.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("error making request to server:\n\t%v", err)
}
Expand All @@ -187,7 +187,7 @@
// setTag sets tag on specified containerID
func (c *Client) setTag(ctx context.Context, containerID string, t ImageTag) error {
url := "v1/tags/" + containerID
c.Logger.Logf("setTag calling %s", url)
c.logger.Logf("setTag calling %s", url)
s, err := json.Marshal(t)
if err != nil {
return fmt.Errorf("error encoding object to JSON:\n\t%v", err)
Expand All @@ -196,7 +196,7 @@
if err != nil {
return fmt.Errorf("error creating POST request:\n\t%v", err)
}
res, err := c.HTTPClient.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("error making request to server:\n\t%v", err)
}
Expand All @@ -220,10 +220,10 @@
}

for _, tag := range tags {
c.Logger.Logf("Setting tag %s", tag)
c.logger.Logf("Setting tag %s", tag)

if _, ok := existingTags[arch][tag]; ok {
c.Logger.Logf("%s replaces an existing tag for arch %s", tag, arch)
c.logger.Logf("%s replaces an existing tag for arch %s", tag, arch)

Check warning on line 226 in client/api.go

View check run for this annotation

Codecov / codecov/patch

client/api.go#L226

Added line #L226 was not covered by tests
}

imgTag := ArchImageTag{
Expand All @@ -242,12 +242,12 @@
// getTagsV2 returns a arch->tag map for the specified containerID
func (c *Client) getTagsV2(ctx context.Context, containerID string) (ArchTagMap, error) {
url := fmt.Sprintf("v2/tags/%s", containerID)
c.Logger.Logf("getTagsV2 calling %s", url)
c.logger.Logf("getTagsV2 calling %s", url)
req, err := c.newRequest(ctx, http.MethodGet, url, "", nil)
if err != nil {
return nil, fmt.Errorf("error creating request to server:\n\t%v", err)
}
res, err := c.HTTPClient.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("error making request to server:\n\t%v", err)
}
Expand All @@ -270,7 +270,7 @@
// setTag sets an arch->tag on specified containerID
func (c *Client) setTagV2(ctx context.Context, containerID string, t ArchImageTag) error {
url := "v2/tags/" + containerID
c.Logger.Logf("setTag calling %s", url)
c.logger.Logf("setTag calling %s", url)
s, err := json.Marshal(t)
if err != nil {
return fmt.Errorf("error encoding object to JSON:\n\t%v", err)
Expand All @@ -279,7 +279,7 @@
if err != nil {
return fmt.Errorf("error creating POST request:\n\t%v", err)
}
res, err := c.HTTPClient.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("error making request to server:\n\t%v", err)
}
Expand Down
55 changes: 23 additions & 32 deletions client/client.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2019-2022, Sylabs Inc. All rights reserved.
// Copyright (c) 2019-2023, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the LICENSE.md file
// distributed with the sources of this project regarding your rights to use or distribute this
// software.
Expand Down Expand Up @@ -35,16 +35,11 @@ var DefaultConfig = &Config{}

// Client describes the client details.
type Client struct {
// Base URL of the service.
BaseURL *url.URL
// Auth token to include in the Authorization header of each request (if supplied).
AuthToken string
// User agent to include in each request (if supplied).
UserAgent string
// HTTPClient to use to make HTTP requests.
HTTPClient *http.Client
// Logger to be used when output is generated
Logger log.Logger
baseURL *url.URL
authToken string
userAgent string
httpClient *http.Client
logger log.Logger
}

const defaultBaseURL = ""
Expand Down Expand Up @@ -81,52 +76,48 @@ func NewClient(cfg *Config) (*Client, error) {
}

c := &Client{
BaseURL: baseURL,
AuthToken: cfg.AuthToken,
UserAgent: cfg.UserAgent,
baseURL: baseURL,
authToken: cfg.AuthToken,
userAgent: cfg.UserAgent,
}

// Set HTTP client
if cfg.HTTPClient != nil {
c.HTTPClient = cfg.HTTPClient
c.httpClient = cfg.HTTPClient
} else {
c.HTTPClient = http.DefaultClient
c.httpClient = http.DefaultClient
}

if cfg.Logger != nil {
c.Logger = cfg.Logger
c.logger = cfg.Logger
} else {
c.Logger = log.DefaultLogger
c.logger = log.DefaultLogger
}

return c, nil
}

// newRequestWithURL returns a new Request given a method, url, and (optional) body.
func (c *Client) newRequestWithURL(ctx context.Context, method, url string, body io.Reader) (*http.Request, error) {
r, err := http.NewRequestWithContext(ctx, method, url, body)
// newRequest returns a new Request given a method, relative path, rawQuery, and (optional) body.
func (c *Client) newRequest(ctx context.Context, method, path, rawQuery string, body io.Reader) (*http.Request, error) {
u := c.baseURL.ResolveReference(&url.URL{
Path: path,
RawQuery: rawQuery,
})

r, err := http.NewRequestWithContext(ctx, method, u.String(), body)
if err != nil {
return nil, err
}

if v := c.AuthToken; v != "" {
if v := c.authToken; v != "" {
if err := (bearerTokenCredentials{authToken: v}).ModifyRequest(r); err != nil {
return nil, err
}
}

if v := c.UserAgent; v != "" {
if v := c.userAgent; v != "" {
r.Header.Set("User-Agent", v)
}

return r, nil
}

// newRequest returns a new Request given a method, relative path, rawQuery, and (optional) body.
func (c *Client) newRequest(ctx context.Context, method, path, rawQuery string, body io.Reader) (*http.Request, error) {
u := c.BaseURL.ResolveReference(&url.URL{
Path: path,
RawQuery: rawQuery,
})
return c.newRequestWithURL(ctx, method, u.String(), body)
}
30 changes: 25 additions & 5 deletions client/client_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2019-2022, Sylabs Inc. All rights reserved.
// Copyright (c) 2019-2023, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the LICENSE.md file
// distributed with the sources of this project regarding your rights to use or distribute this
// software.
Expand All @@ -7,8 +7,13 @@ package client

import (
"context"
crypto_rand "crypto/rand"
"encoding/binary"
"io"
"log"
math_rand "math/rand"
"net/http"
"os"
"strings"
"testing"
)
Expand Down Expand Up @@ -68,19 +73,19 @@ func TestNewClient(t *testing.T) {
}

if err == nil {
if got, want := c.BaseURL.String(), tt.wantURL; got != want {
if got, want := c.baseURL.String(), tt.wantURL; got != want {
t.Errorf("got host %v, want %v", got, want)
}

if got, want := c.AuthToken, tt.wantAuthToken; got != want {
if got, want := c.authToken, tt.wantAuthToken; got != want {
t.Errorf("got auth token %v, want %v", got, want)
}

if got, want := c.UserAgent, tt.wantUserAgent; got != want {
if got, want := c.userAgent, tt.wantUserAgent; got != want {
t.Errorf("got user agent %v, want %v", got, want)
}

if got, want := c.HTTPClient, tt.wantHTTPClient; got != want {
if got, want := c.httpClient, tt.wantHTTPClient; got != want {
t.Errorf("got HTTP client %v, want %v", got, want)
}
}
Expand Down Expand Up @@ -192,3 +197,18 @@ func TestNewRequest(t *testing.T) {
})
}
}

func seedRandomNumberGenerator() {
var b [8]byte
if _, err := crypto_rand.Read(b[:]); err != nil {
log.Fatalf("error seeding random number generator: %v", err)
}
math_rand.Seed(int64(binary.LittleEndian.Uint64(b[:])))
}

func TestMain(m *testing.M) {
// Total overkill seeding the random number generator
seedRandomNumberGenerator()

os.Exit(m.Run())
}
14 changes: 7 additions & 7 deletions client/downloader.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2021-2022, Sylabs Inc. All rights reserved.
// Copyright (c) 2021-2023, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.
Expand Down Expand Up @@ -58,7 +58,7 @@ func (c *Client) multipartDownload(ctx context.Context, u string, creds credenti
// Calculate # of parts
parts := uint(1 + (size-1)/spec.PartSize)

c.Logger.Logf("size: %d, parts: %d, streams: %d, partsize: %d", size, parts, spec.Concurrency, spec.PartSize)
c.logger.Logf("size: %d, parts: %d, streams: %d, partsize: %d", size, parts, spec.Concurrency, spec.PartSize)

g, ctx := errgroup.WithContext(ctx)

Expand All @@ -67,7 +67,7 @@ func (c *Client) multipartDownload(ctx context.Context, u string, creds credenti

// Create download part workers
for n := uint(0); n < spec.Concurrency; n++ {
g.Go(c.ociDownloadWorker(ctx, u, creds, ch, pb))
g.Go(c.downloadWorker(ctx, u, creds, ch, pb))
}

// Add part download requests
Expand All @@ -84,11 +84,11 @@ func (c *Client) multipartDownload(ctx context.Context, u string, creds credenti
return g.Wait()
}

func (c *Client) ociDownloadWorker(ctx context.Context, u string, creds credentials, ch chan filePartDescriptor, pb ProgressBar) func() error {
func (c *Client) downloadWorker(ctx context.Context, u string, creds credentials, ch chan filePartDescriptor, pb ProgressBar) func() error {
return func() error {
// Iterate on channel 'ch' to handle download part requests
for ps := range ch {
written, err := c.ociDownloadBlobPart(ctx, creds, u, &ps)
written, err := c.downloadBlobPart(ctx, creds, u, &ps)
if err != nil {
// Cleanly abort progress bar on error
pb.Abort(true)
Expand All @@ -103,7 +103,7 @@ func (c *Client) ociDownloadWorker(ctx context.Context, u string, creds credenti
}
}

func (c *Client) ociDownloadBlobPart(ctx context.Context, creds credentials, u string, ps *filePartDescriptor) (int64, error) {
func (c *Client) downloadBlobPart(ctx context.Context, creds credentials, u string, ps *filePartDescriptor) (int64, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
if err != nil {
return 0, err
Expand All @@ -117,7 +117,7 @@ func (c *Client) ociDownloadBlobPart(ctx context.Context, creds credentials, u s

req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", ps.start, ps.end))

res, err := c.HTTPClient.Do(req)
res, err := c.httpClient.Do(req)
if err != nil {
return 0, err
}
Expand Down
35 changes: 24 additions & 11 deletions client/downloader_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2018-2022, Sylabs Inc. All rights reserved.
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.
Expand All @@ -19,6 +19,16 @@ import (
"testing"
)

const (
basicAuthUsername = "user"
basicAuthPassword = "password"
)

var (
testLogger = &stdLogger{}
creds = &basicCredentials{username: basicAuthUsername, password: basicAuthPassword}
)

type inMemoryBuffer struct {
m sync.Mutex
buf []byte
Expand Down Expand Up @@ -49,6 +59,19 @@ func (l *stdLogger) Logf(f string, v ...interface{}) {
log.Printf(f, v...)
}

func TestParseContentRange(t *testing.T) {
const hdr = "bytes 0-1000/1000"

size, err := parseContentRange(hdr)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if got, want := size, int64(1000); got != want {
t.Fatalf("unexpected content length: got %v, want %v", got, want)
}
}

func parseRangeHeader(_ *testing.T, val string) (int64, int64) {
if val == "" {
return 0, 0
Expand All @@ -66,16 +89,6 @@ func parseRangeHeader(_ *testing.T, val string) (int64, int64) {
return start, end
}

const (
basicAuthUsername = "user"
basicAuthPassword = "password"
)

var (
testLogger = &stdLogger{}
creds = &basicCredentials{username: basicAuthUsername, password: basicAuthPassword}
)

func TestMultistreamDownloader(t *testing.T) {
const src = "123456789012345678901234567890"
size := int64(len(src))
Expand Down
Loading
Loading