Skip to content

feat: [AH-2353]: Updated storage layer initiation for default bucket#88

Open
abhinavcode wants to merge 2 commits into
mainfrom
AH-2353-gcs-integration
Open

feat: [AH-2353]: Updated storage layer initiation for default bucket#88
abhinavcode wants to merge 2 commits into
mainfrom
AH-2353-gcs-integration

Conversation

@abhinavcode

@abhinavcode abhinavcode commented Feb 21, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added Google Cloud Storage (GCS) as a supported storage backend option, enabling users to configure and store container images in GCS buckets alongside existing storage alternatives.
  • Improvements

    • Enhanced object copy operations for cloud storage backends with improved error handling and functionality.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai

coderabbitai Bot commented Feb 21, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request adds Google Cloud Storage (GCS) as a new storage backend option. It introduces GCS configuration support, implements the GCS driver with storage operations, adds a configuration helper, and updates the storage initialization wiring to support GCS selection via storage type configuration.

Changes

Cohort / File(s) Summary
GCS Storage Configuration
types/config.go, registry/config/helper.go
Introduces GCSStorage struct with Bucket field to the configuration and adds GetGCSStorageParameters helper function to extract GCS configuration as a parameter map.
GCS Driver Implementation
registry/app/driver/gcs/gcs.go
Adds public Register function for driver registration, introduces gcs client field, and implements CopyObject method with full copy operation logic, error handling, and content-type preservation.
Storage Initialization Wiring
registry/app/api/wire.go
Refactors storage type selection from if-else chain to switch statement with explicit cases for "filesystem", "gcs", and default "s3aws", integrating GCS driver registration and client initialization.

Sequence Diagram

sequenceDiagram
    participant App as Application
    participant Wire as wire.go
    participant Config as Config Helper
    participant GCSReg as GCS Registration
    participant Driver as GCS Driver
    participant Client as GCS Client

    App->>Wire: Initialize Storage (StorageType: "gcs")
    Wire->>Config: GetGCSStorageParameters(config)
    Config-->>Wire: GCS Parameters Map
    Wire->>GCSReg: Register(ctx)
    GCSReg->>Driver: Create Driver Instance
    Driver->>Client: Initialize Storage Client
    Client-->>Driver: Client Ready
    Driver-->>GCSReg: Driver Registered
    GCSReg-->>Wire: Registration Complete
    Wire-->>App: Storage Initialized
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A storage tale in clouds so high,
GCS joins the backend supply,
With buckets and clients all in place,
Our data finds its cloud-bound space,
Three storage paths now dance with glee!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title mentions 'storage layer initiation for default bucket' but the primary changes involve adding GCS (Google Cloud Storage) integration with explicit switch-case routing for multiple storage backends (filesystem, GCS, S3). The title is overly vague and doesn't reflect the main objective of introducing GCS support. Consider revising the title to explicitly mention GCS integration, such as 'feat: [AH-2353]: Add GCS storage backend integration with explicit provider routing' to better reflect the actual implementation.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch AH-2353-gcs-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
registry/app/api/wire.go (1)

98-104: GCS error handling uses log.Error + panic — consider using log.Fatal or just returning the error.

The filesystem case uses log.Fatal (which calls os.Exit), while gcs and s3 cases use log.Error + panic. panic in a provider function is harsh — if the wire injection catches panics differently than os.Exit, behavior will diverge. Returning (nil, err) would be more idiomatic and let the caller decide how to handle it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@registry/app/api/wire.go` around lines 98 - 104, The gcs case in
registry/app/api/wire.go calls gcs.Register(ctx) and then panics on
factory.Create error; instead of panic (or using log.Fatal), log the error with
the existing logger (including the error/stack) and return the error to the
caller (e.g., return nil, err) so behavior matches the idiomatic handling used
elsewhere (the filesystem case uses log.Fatal) and lets the caller decide
termination; update the error branch after factory.Create in the gcs case to log
the error and return (nil, err) rather than calling panic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@registry/app/api/wire.go`:
- Around line 90-112: The switch on c.Registry.Storage.StorageType currently
treats any unknown value as S3; add an explicit "s3aws" case that calls
s3.Register(ctx) and sets d, err = factory.Create(ctx, "s3aws",
config.GetS3StorageParameters(c)) (mirroring the existing S3 logic) and change
the switch default to surface a configuration error instead of initializing S3
(e.g., return or log.Fatalf with a clear message including the invalid
c.Registry.Storage.StorageType); update callers or function signature if needed
so the function returns an error rather than silently panicking or defaulting.

In `@registry/app/driver/gcs/gcs.go`:
- Around line 941-959: CopyObject currently resolves the source key with
d.pathToKey but uses destKey verbatim; make path handling consistent by applying
d.pathToKey to the destination (e.g., use
d.gcs.Bucket(destBucket).Object(d.pathToKey(destKey))) so destination objects
live under the same rootDirectory, and add a pre-check that destBucket is
non-empty (return a clear error if empty) before creating the destination
object; update any related error messages in CopyObject to reference the
resolved destination key.

In `@registry/config/helper.go`:
- Around line 49-53: GetGCSStorageParameters currently only returns the bucket
and omits the rootdirectory (and any other new GCS options), causing all objects
to be written to the bucket root; update GetGCSStorageParameters to read
c.Registry.Storage.GCSStorage.RootDirectory (and any other new fields) and
include them in the returned map (e.g., set "rootdirectory" =>
c.Registry.Storage.GCSStorage.RootDirectory) so the GCS driver receives the same
namespace/isolation params as GetS3StorageParameters and GetFilesystemParams.

In `@types/config.go`:
- Around line 548-552: The GCSStorage struct is missing RootDirectory (and other
driver params) so registry objects would be stored at the bucket root; update
the GCSStorage struct to add at least a RootDirectory string field (matching
envconfig tag pattern like GITNESS_REGISTRY_GCS_ROOT_DIRECTORY) and consider
adding optional fields supported by the driver such as ChunkSize,
MaxConcurrency, KeyFile, and Credentials to mirror S3Storage/FileSystemStorage;
ensure the struct field names and envconfig tags align with how FromParameters
expects keys so FromParameters can map rootdirectory, chunksize, maxconcurrency,
keyfile, and credentials into the GCSStorage config.

---

Nitpick comments:
In `@registry/app/api/wire.go`:
- Around line 98-104: The gcs case in registry/app/api/wire.go calls
gcs.Register(ctx) and then panics on factory.Create error; instead of panic (or
using log.Fatal), log the error with the existing logger (including the
error/stack) and return the error to the caller (e.g., return nil, err) so
behavior matches the idiomatic handling used elsewhere (the filesystem case uses
log.Fatal) and lets the caller decide termination; update the error branch after
factory.Create in the gcs case to log the error and return (nil, err) rather
than calling panic.

Comment thread registry/app/api/wire.go
Comment on lines +90 to 112
switch c.Registry.Storage.StorageType {
case "filesystem":
filesystem.Register(ctx)
d, err = factory.Create(ctx, "filesystem", config.GetFilesystemParams(c))
if err != nil {
log.Fatal().Stack().Err(err).Msgf("")
panic(err)
}
} else {
case "gcs":
gcs.Register(ctx)
d, err = factory.Create(ctx, "gcs", config.GetGCSStorageParameters(c))
if err != nil {
log.Error().Stack().Err(err).Msg("failed to init gcs Blob storage")
panic(err)
}
default:
s3.Register(ctx)
d, err = factory.Create(ctx, "s3aws", config.GetS3StorageParameters(c))
if err != nil {
log.Error().Stack().Err(err).Msg("failed to init s3 Blob storage ")
log.Error().Stack().Err(err).Msg("failed to init s3 Blob storage")
panic(err)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Unknown StorageType values silently fall through to S3.

If someone mistypes the storage type (e.g., "gc" instead of "gcs"), the default branch will quietly initialize S3 storage instead of surfacing a configuration error. Consider adding an explicit "s3aws" case and making default return an error for unrecognized values.

Suggested change
 switch c.Registry.Storage.StorageType {
 case "filesystem":
     filesystem.Register(ctx)
     d, err = factory.Create(ctx, "filesystem", config.GetFilesystemParams(c))
     if err != nil {
         log.Fatal().Stack().Err(err).Msgf("")
         panic(err)
     }
 case "gcs":
     gcs.Register(ctx)
     d, err = factory.Create(ctx, "gcs", config.GetGCSStorageParameters(c))
     if err != nil {
         log.Error().Stack().Err(err).Msg("failed to init gcs Blob storage")
         panic(err)
     }
-default:
+case "s3aws":
     s3.Register(ctx)
     d, err = factory.Create(ctx, "s3aws", config.GetS3StorageParameters(c))
     if err != nil {
         log.Error().Stack().Err(err).Msg("failed to init s3 Blob storage")
         panic(err)
     }
+default:
+    return nil, fmt.Errorf("unsupported registry storage type: %q", c.Registry.Storage.StorageType)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
switch c.Registry.Storage.StorageType {
case "filesystem":
filesystem.Register(ctx)
d, err = factory.Create(ctx, "filesystem", config.GetFilesystemParams(c))
if err != nil {
log.Fatal().Stack().Err(err).Msgf("")
panic(err)
}
} else {
case "gcs":
gcs.Register(ctx)
d, err = factory.Create(ctx, "gcs", config.GetGCSStorageParameters(c))
if err != nil {
log.Error().Stack().Err(err).Msg("failed to init gcs Blob storage")
panic(err)
}
default:
s3.Register(ctx)
d, err = factory.Create(ctx, "s3aws", config.GetS3StorageParameters(c))
if err != nil {
log.Error().Stack().Err(err).Msg("failed to init s3 Blob storage ")
log.Error().Stack().Err(err).Msg("failed to init s3 Blob storage")
panic(err)
}
}
switch c.Registry.Storage.StorageType {
case "filesystem":
filesystem.Register(ctx)
d, err = factory.Create(ctx, "filesystem", config.GetFilesystemParams(c))
if err != nil {
log.Fatal().Stack().Err(err).Msgf("")
panic(err)
}
case "gcs":
gcs.Register(ctx)
d, err = factory.Create(ctx, "gcs", config.GetGCSStorageParameters(c))
if err != nil {
log.Error().Stack().Err(err).Msg("failed to init gcs Blob storage")
panic(err)
}
case "s3aws":
s3.Register(ctx)
d, err = factory.Create(ctx, "s3aws", config.GetS3StorageParameters(c))
if err != nil {
log.Error().Stack().Err(err).Msg("failed to init s3 Blob storage")
panic(err)
}
default:
return nil, fmt.Errorf("unsupported registry storage type: %q", c.Registry.Storage.StorageType)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@registry/app/api/wire.go` around lines 90 - 112, The switch on
c.Registry.Storage.StorageType currently treats any unknown value as S3; add an
explicit "s3aws" case that calls s3.Register(ctx) and sets d, err =
factory.Create(ctx, "s3aws", config.GetS3StorageParameters(c)) (mirroring the
existing S3 logic) and change the switch default to surface a configuration
error instead of initializing S3 (e.g., return or log.Fatalf with a clear
message including the invalid c.Registry.Storage.StorageType); update callers or
function signature if needed so the function returns an error rather than
silently panicking or defaulting.

Comment on lines +941 to 959
func (d *driver) CopyObject(ctx context.Context, srcKey, destBucket, destKey string) error {
src := d.bucket.Object(d.pathToKey(srcKey))

dst := d.gcs.Bucket(destBucket).Object(destKey)

copier := dst.CopierFrom(src)
copier.ContentType = blobContentType

_, err := copier.Run(ctx)
if err != nil {
var status *googleapi.Error
if errors.As(err, &status) && status.Code == http.StatusNotFound {
return storagedriver.PathNotFoundError{Path: srcKey}
}
return fmt.Errorf("copy %q to %q/%q: %w", srcKey, destBucket, destKey, err)
}

return nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Asymmetric path handling: srcKey is resolved via pathToKey but destKey is used verbatim.

d.pathToKey(srcKey) prepends rootDirectory to the source, but destKey is passed directly to d.gcs.Bucket(destBucket).Object(destKey) without any root-directory prefix. If the caller isn't aware of this inconsistency, the destination object will end up at an unexpected path. Either document this contract clearly or apply consistent path resolution.

Additionally, consider whether this method should verify that destBucket is non-empty before proceeding, to avoid a confusing GCS error.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@registry/app/driver/gcs/gcs.go` around lines 941 - 959, CopyObject currently
resolves the source key with d.pathToKey but uses destKey verbatim; make path
handling consistent by applying d.pathToKey to the destination (e.g., use
d.gcs.Bucket(destBucket).Object(d.pathToKey(destKey))) so destination objects
live under the same rootDirectory, and add a pre-check that destBucket is
non-empty (return a clear error if empty) before creating the destination
object; update any related error messages in CopyObject to reference the
resolved destination key.

Comment thread registry/config/helper.go
Comment on lines +49 to +53
func GetGCSStorageParameters(c *types.Config) map[string]any {
gcsProperties := make(map[string]any)
gcsProperties["bucket"] = c.Registry.Storage.GCSStorage.Bucket
return gcsProperties
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Pass rootdirectory (once added to config) for namespace isolation.

This helper should forward the rootdirectory parameter (and any other newly-added GCS config fields) to the driver, matching the pattern used by GetS3StorageParameters and GetFilesystemParams. Currently all registry objects will be written directly under the bucket root.

Suggested change (once config is updated)
 func GetGCSStorageParameters(c *types.Config) map[string]any {
 	gcsProperties := make(map[string]any)
 	gcsProperties["bucket"] = c.Registry.Storage.GCSStorage.Bucket
+	gcsProperties["rootdirectory"] = c.Registry.Storage.GCSStorage.RootDirectory
 	return gcsProperties
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@registry/config/helper.go` around lines 49 - 53, GetGCSStorageParameters
currently only returns the bucket and omits the rootdirectory (and any other new
GCS options), causing all objects to be written to the bucket root; update
GetGCSStorageParameters to read c.Registry.Storage.GCSStorage.RootDirectory (and
any other new fields) and include them in the returned map (e.g., set
"rootdirectory" => c.Registry.Storage.GCSStorage.RootDirectory) so the GCS
driver receives the same namespace/isolation params as GetS3StorageParameters
and GetFilesystemParams.

Comment thread types/config.go
Comment on lines +548 to +552
// GCSStorage defines the configuration for the GCS storage if StorageType is `gcs`.
// Authentication is handled via workload identity (google.DefaultTokenSource).
GCSStorage struct {
Bucket string `envconfig:"GITNESS_REGISTRY_GCS_BUCKET"`
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

GCSStorage config is missing RootDirectory (and other driver parameters).

Both S3Storage and FileSystemStorage expose a RootDirectory field to isolate registry data under a path prefix. The GCS driver's FromParameters also supports rootdirectory, chunksize, maxconcurrency, keyfile, and credentials, but none of these are configurable here. At minimum, RootDirectory should be added for parity and to avoid storing objects at the bucket root with no namespace isolation.

Suggested minimal addition
 GCSStorage struct {
-    Bucket string `envconfig:"GITNESS_REGISTRY_GCS_BUCKET"`
+    Bucket        string `envconfig:"GITNESS_REGISTRY_GCS_BUCKET"`
+    RootDirectory string `envconfig:"GITNESS_REGISTRY_GCS_ROOT_DIRECTORY"`
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// GCSStorage defines the configuration for the GCS storage if StorageType is `gcs`.
// Authentication is handled via workload identity (google.DefaultTokenSource).
GCSStorage struct {
Bucket string `envconfig:"GITNESS_REGISTRY_GCS_BUCKET"`
}
// GCSStorage defines the configuration for the GCS storage if StorageType is `gcs`.
// Authentication is handled via workload identity (google.DefaultTokenSource).
GCSStorage struct {
Bucket string `envconfig:"GITNESS_REGISTRY_GCS_BUCKET"`
RootDirectory string `envconfig:"GITNESS_REGISTRY_GCS_ROOT_DIRECTORY"`
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@types/config.go` around lines 548 - 552, The GCSStorage struct is missing
RootDirectory (and other driver params) so registry objects would be stored at
the bucket root; update the GCSStorage struct to add at least a RootDirectory
string field (matching envconfig tag pattern like
GITNESS_REGISTRY_GCS_ROOT_DIRECTORY) and consider adding optional fields
supported by the driver such as ChunkSize, MaxConcurrency, KeyFile, and
Credentials to mirror S3Storage/FileSystemStorage; ensure the struct field names
and envconfig tags align with how FromParameters expects keys so FromParameters
can map rootdirectory, chunksize, maxconcurrency, keyfile, and credentials into
the GCSStorage config.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants