download: Fix saving OCI bundles on disk

This commit fixes an issue related to zero-sized bundles being saved to disk,
which can cause OPA to fail to start if a remote OCI repository is unavailable.

Fixes: #6939

Signed-off-by: Sergey-Kizimov <serget.kizimov@hiya.com>
This commit is contained in:
Sergey-Kizimov
2024-08-21 14:49:34 -07:00
committed by GitHub
parent a6768777a3
commit f7b155288f
2 changed files with 61 additions and 3 deletions
+11 -3
View File
@@ -3,6 +3,7 @@
package download
import (
"bytes"
"context"
"encoding/json"
"fmt"
@@ -210,13 +211,14 @@ func (d *OCIDownloader) oneShot(ctx context.Context) error {
d.SetCache(resp.etag) // set the current etag sha to the cache
if d.f != nil {
d.f(ctx, Update{ETag: resp.etag, Bundle: resp.b, Error: nil, Metrics: m, Raw: resp.raw})
d.f(ctx, Update{ETag: resp.etag, Bundle: resp.b, Error: nil, Metrics: m, Raw: resp.raw, Size: resp.size})
}
return nil
}
func (d *OCIDownloader) download(ctx context.Context, m metrics.Metrics) (*downloaderResponse, error) {
d.logger.Debug("OCI - Download starting.")
var buf bytes.Buffer
preferences := []string{fmt.Sprintf("modes=%v,%v", defaultBundleMode, deltaBundleMode)}
@@ -256,10 +258,15 @@ func (d *OCIDownloader) download(ctx context.Context, m metrics.Metrics) (*downl
}, nil
}
fileReader, err := os.Open(bundleFilePath)
cnt := &count{}
r := io.TeeReader(fileReader, cnt)
tee := io.TeeReader(r, &buf)
if err != nil {
return nil, err
}
loader := bundle.NewTarballLoaderWithBaseURL(fileReader, d.localStorePath)
loader := bundle.NewTarballLoaderWithBaseURL(tee, d.localStorePath)
reader := bundle.NewCustomReader(loader).
WithMetrics(m).
WithBundleVerificationConfig(d.bvc).
@@ -274,9 +281,10 @@ func (d *OCIDownloader) download(ctx context.Context, m metrics.Metrics) (*downl
return &downloaderResponse{
b: &bundleInfo,
raw: fileReader,
raw: &buf,
etag: etag,
longPoll: false,
size: cnt.Bytes(),
}, nil
}
+50
View File
@@ -8,6 +8,7 @@ import (
"encoding/base64"
"fmt"
"net/http"
"reflect"
"strings"
"testing"
"time"
@@ -382,6 +383,55 @@ func TestOCICustomAuthPlugin(t *testing.T) {
}
}
func TestOCIValidateAndInjectDefaults(t *testing.T) {
ctx := context.Background()
fixture := newTestFixture(t)
fixture.server.expEtag = "sha256:c5834dbce332cabe6ae68a364de171a50bf5b08024c27d7c08cc72878b4df7ff"
updates := make(chan *Update)
config := Config{}
if err := config.ValidateAndInjectDefaults(); err != nil {
t.Fatal(err)
}
d := NewOCI(config, fixture.client, "ghcr.io/org/repo:latest", t.TempDir()).WithCallback(func(_ context.Context, u Update) {
updates <- &u
}).WithBundlePersistence(true)
d.Start(ctx)
// Give time for some download events to occur
time.Sleep(1 * time.Second)
u1 := <-updates
if u1.Size == 0 {
t.Fatal("expected non-0 size")
}
if u1.Raw == nil {
t.Fatal("expected bundle reader to be non-nil")
}
r := bundle.NewReader(u1.Raw)
b, err := r.Read()
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(b.Data, u1.Bundle.Data) {
t.Fatal("expected the bundle object and reader to have the same data")
}
if len(b.Modules) != len(u1.Bundle.Modules) {
t.Fatal("expected the bundle object and reader to have the same number of bundle modules")
}
d.Stop(ctx)
}
func mockAuthPluginLookup(string) rest.HTTPAuthPlugin {
return &mockAuthPlugin{}
}