Skip to content

Commit

Permalink
Merge pull request #1228 from fluxcd/bucket-prefix
Browse files Browse the repository at this point in the history
bucket: Add prefix filtering capability
  • Loading branch information
stefanprodan authored Oct 17, 2023
2 parents 3073c2c + 674c287 commit f2a1814
Show file tree
Hide file tree
Showing 11 changed files with 60 additions and 13 deletions.
5 changes: 5 additions & 0 deletions api/v1beta2/bucket_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/fluxcd/pkg/apis/acl"
"github.com/fluxcd/pkg/apis/meta"

apiv1 "github.com/fluxcd/source-controller/api/v1"
)

Expand Down Expand Up @@ -73,6 +74,10 @@ type BucketSpec struct {
// +optional
Region string `json:"region,omitempty"`

// Prefix to use for server-side filtering of files in the Bucket.
// +optional
Prefix string `json:"prefix,omitempty"`

// SecretRef specifies the Secret containing authentication credentials
// for the Bucket.
// +optional
Expand Down
4 changes: 4 additions & 0 deletions config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,10 @@ spec:
to ensure efficient use of resources.
pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$
type: string
prefix:
description: Prefix to use for server-side filtering of files in the
Bucket.
type: string
provider:
default: generic
description: Provider of the object storage bucket. Defaults to 'generic',
Expand Down
24 changes: 24 additions & 0 deletions docs/api/v1beta2/source.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ string
</tr>
<tr>
<td>
<code>prefix</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>Prefix to use for server-side filtering of files in the Bucket.</p>
</td>
</tr>
<tr>
<td>
<code>secretRef</code><br>
<em>
<a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
Expand Down Expand Up @@ -1422,6 +1434,18 @@ string
</tr>
<tr>
<td>
<code>prefix</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>Prefix to use for server-side filtering of files in the Bucket.</p>
</td>
</tr>
<tr>
<td>
<code>secretRef</code><br>
<em>
<a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
Expand Down
9 changes: 9 additions & 0 deletions docs/spec/v1beta2/buckets.md
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,15 @@ credentials for the object storage. For some `.spec.provider` implementations
the presence of the field is required, see [Provider](#provider) for more
details and examples.

### Prefix

`.spec.prefix` is an optional field to enable server-side filtering
of files in the Bucket.

**Note:** The server-side filtering works only with the `generic`, `aws`
and `gcp` [provider](#provider) and is preferred over [`.spec.ignore`](#ignore)
as a more efficient way of excluding files.

### Ignore

`.spec.ignore` is an optional field to specify rules in [the `.gitignore`
Expand Down
4 changes: 2 additions & 2 deletions internal/controller/bucket_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ type BucketProvider interface {
// bucket, calling visit for every item.
// If the underlying client or the visit callback returns an error,
// it returns early.
VisitObjects(ctx context.Context, bucketName string, visit func(key, etag string) error) error
VisitObjects(ctx context.Context, bucketName string, prefix string, visit func(key, etag string) error) error
// ObjectIsNotFound returns true if the given error indicates an object
// could not be found.
ObjectIsNotFound(error) bool
Expand Down Expand Up @@ -742,7 +742,7 @@ func fetchEtagIndex(ctx context.Context, provider BucketProvider, obj *bucketv1.
matcher := sourceignore.NewMatcher(ps)

// Build up index
err = provider.VisitObjects(ctxTimeout, obj.Spec.BucketName, func(key, etag string) error {
err = provider.VisitObjects(ctxTimeout, obj.Spec.BucketName, obj.Spec.Prefix, func(key, etag string) error {
if strings.HasSuffix(key, "/") || key == sourceignore.IgnoreFile {
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/bucket_controller_fetch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (m mockBucketClient) ObjectIsNotFound(e error) bool {
return e == errMockNotFound
}

func (m mockBucketClient) VisitObjects(_ context.Context, _ string, f func(key, etag string) error) error {
func (m mockBucketClient) VisitObjects(_ context.Context, _ string, _ string, f func(key, etag string) error) error {
for key, obj := range m.objects {
if err := f(key, obj.etag); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion pkg/azure/blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ func (c *BlobClient) FGetObject(ctx context.Context, bucketName, objectName, loc
// bucket, calling visit for every item.
// If the underlying client or the visit callback returns an error,
// it returns early.
func (c *BlobClient) VisitObjects(ctx context.Context, bucketName string, visit func(path, etag string) error) error {
func (c *BlobClient) VisitObjects(ctx context.Context, bucketName string, prefix string, visit func(path, etag string) error) error {
items := c.NewListBlobsFlatPager(bucketName, nil)
for items.More() {
resp, err := items.NextPage(ctx)
Expand Down
6 changes: 4 additions & 2 deletions pkg/gcp/gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,10 @@ func (c *GCSClient) FGetObject(ctx context.Context, bucketName, objectName, loca
// bucket, calling visit for every item.
// If the underlying client or the visit callback returns an error,
// it returns early.
func (c *GCSClient) VisitObjects(ctx context.Context, bucketName string, visit func(path, etag string) error) error {
items := c.Client.Bucket(bucketName).Objects(ctx, nil)
func (c *GCSClient) VisitObjects(ctx context.Context, bucketName string, prefix string, visit func(path, etag string) error) error {
items := c.Client.Bucket(bucketName).Objects(ctx, &gcpstorage.Query{
Prefix: prefix,
})
for {
object, err := items.Next()
if err == IteratorDone {
Expand Down
6 changes: 3 additions & 3 deletions pkg/gcp/gcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func TestVisitObjects(t *testing.T) {
}
keys := []string{}
etags := []string{}
err := gcpClient.VisitObjects(context.Background(), bucketName, func(key, etag string) error {
err := gcpClient.VisitObjects(context.Background(), bucketName, "", func(key, etag string) error {
keys = append(keys, key)
etags = append(etags, etag)
return nil
Expand All @@ -185,7 +185,7 @@ func TestVisitObjectsErr(t *testing.T) {
Client: client,
}
badBucketName := "bad-bucket"
err := gcpClient.VisitObjects(context.Background(), badBucketName, func(key, etag string) error {
err := gcpClient.VisitObjects(context.Background(), badBucketName, "", func(key, etag string) error {
return nil
})
assert.Error(t, err, fmt.Sprintf("listing objects from bucket '%s' failed: storage: bucket doesn't exist", badBucketName))
Expand All @@ -196,7 +196,7 @@ func TestVisitObjectsCallbackErr(t *testing.T) {
Client: client,
}
mockErr := fmt.Errorf("mock")
err := gcpClient.VisitObjects(context.Background(), bucketName, func(key, etag string) error {
err := gcpClient.VisitObjects(context.Background(), bucketName, "", func(key, etag string) error {
return mockErr
})
assert.Error(t, err, mockErr.Error())
Expand Down
3 changes: 2 additions & 1 deletion pkg/minio/minio.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ func (c *MinioClient) FGetObject(ctx context.Context, bucketName, objectName, lo
// bucket, calling visit for every item.
// If the underlying client or the visit callback returns an error,
// it returns early.
func (c *MinioClient) VisitObjects(ctx context.Context, bucketName string, visit func(key, etag string) error) error {
func (c *MinioClient) VisitObjects(ctx context.Context, bucketName string, prefix string, visit func(key, etag string) error) error {
for object := range c.Client.ListObjects(ctx, bucketName, minio.ListObjectsOptions{
Recursive: true,
Prefix: prefix,
UseV1: s3utils.IsGoogleEndpoint(*c.Client.EndpointURL()),
}) {
if object.Err != nil {
Expand Down
8 changes: 5 additions & 3 deletions pkg/minio/minio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (

"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/sourceignore"

sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
)

Expand All @@ -62,6 +63,7 @@ var (

var (
bucketName = "test-bucket-minio" + uuid.New().String()
prefix = ""
secret = corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Name: "minio-secret",
Expand Down Expand Up @@ -228,7 +230,7 @@ func TestFGetObjectNotExists(t *testing.T) {
func TestVisitObjects(t *testing.T) {
keys := []string{}
etags := []string{}
err := testMinioClient.VisitObjects(context.TODO(), bucketName, func(key, etag string) error {
err := testMinioClient.VisitObjects(context.TODO(), bucketName, prefix, func(key, etag string) error {
keys = append(keys, key)
etags = append(etags, etag)
return nil
Expand All @@ -241,15 +243,15 @@ func TestVisitObjects(t *testing.T) {
func TestVisitObjectsErr(t *testing.T) {
ctx := context.Background()
badBucketName := "bad-bucket"
err := testMinioClient.VisitObjects(ctx, badBucketName, func(string, string) error {
err := testMinioClient.VisitObjects(ctx, badBucketName, prefix, func(string, string) error {
return nil
})
assert.Error(t, err, fmt.Sprintf("listing objects from bucket '%s' failed: The specified bucket does not exist", badBucketName))
}

func TestVisitObjectsCallbackErr(t *testing.T) {
mockErr := fmt.Errorf("mock")
err := testMinioClient.VisitObjects(context.TODO(), bucketName, func(key, etag string) error {
err := testMinioClient.VisitObjects(context.TODO(), bucketName, prefix, func(key, etag string) error {
return mockErr
})
assert.Error(t, err, mockErr.Error())
Expand Down

0 comments on commit f2a1814

Please sign in to comment.