Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 34 additions & 15 deletions daprovider/anytrust/google_cloud_storage_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ package anytrust

import (
"context"
"errors"
"fmt"
"io"
"math"
"sort"
"time"

googlestorage "cloud.google.com/go/storage"
"github.com/google/go-cmp/cmp"
"github.com/spf13/pflag"
"google.golang.org/api/option"

Expand Down Expand Up @@ -133,7 +132,12 @@ func (gcs *GoogleCloudStorageService) GetByHash(ctx context.Context, key common.
log.Trace("anytrust.GoogleCloudStorageService.GetByHash", "key", pretty.PrettyHash(key), "this", gcs)
buf, err := gcs.operator.Download(ctx, gcs.bucket, gcs.objectPrefix, key)
if err != nil {
log.Error("anytrust.GoogleCloudStorageService.GetByHash", "err", err)
// context.Canceled is expected when RedundantStorageService cancels
// the shared sub-context after a faster inner service already returned
// successfully. Logging it at ERROR level would be misleading.
if !errors.Is(err, context.Canceled) {
log.Error("anytrust.GoogleCloudStorageService.GetByHash", "err", err)
}
return nil, err
}
return buf, nil
Expand All @@ -159,22 +163,37 @@ func (gcs *GoogleCloudStorageService) String() string {
}

func (gcs *GoogleCloudStorageService) HealthCheck(ctx context.Context) error {
// GCP's testIamPermissions API does not evaluate conditional IAM bindings,
// so it always returns empty results when permissions are granted via a
// condition (e.g. resource.name.startsWith(...)). Instead, we verify
// storage access by performing an actual write/read/delete round-trip on a
// small probe object within our configured prefix.
probeKey := gcs.objectPrefix + ".health-check-probe"
bucket := gcs.operator.Bucket(gcs.bucket)
// check if we have bucket permissions
permissions := []string{
"storage.objects.create",
"storage.objects.delete",
"storage.objects.list",
"storage.objects.get",

// write
w := bucket.Object(probeKey).NewWriter(ctx)
if _, err := w.Write([]byte("health-check")); err != nil {
return fmt.Errorf("health check: failed to write probe object: %w", err)
}
if err := w.Close(); err != nil {
return fmt.Errorf("health check: failed to close probe object writer: %w", err)
}
perms, err := bucket.IAM().TestPermissions(ctx, permissions)

// read
r, err := bucket.Object(probeKey).NewReader(ctx)
if err != nil {
return fmt.Errorf("could not check permissions: %w", err)
return fmt.Errorf("health check: failed to read probe object: %w", err)
}
if _, err := io.ReadAll(r); err != nil {
r.Close()
return fmt.Errorf("health check: failed to read probe object data: %w", err)
}
sort.Strings(permissions)
sort.Strings(perms)
if !cmp.Equal(perms, permissions) {
return fmt.Errorf("permissions mismatch (-want +got):\n%s", cmp.Diff(permissions, perms))
r.Close()

// delete
if err := bucket.Object(probeKey).Delete(ctx); err != nil {
return fmt.Errorf("health check: failed to delete probe object: %w", err)
}

return nil
Expand Down