From ac861ff2e96e21bd6e44faf9ade3b2ff426251c2 Mon Sep 17 00:00:00 2001 From: wlthomson Date: Mon, 9 Feb 2026 16:54:38 +1300 Subject: [PATCH 1/5] Add support for GCS storage feeds --- pkg/feeds/feed_resource.go | 3 ++ pkg/feeds/feed_types.go | 1 + pkg/feeds/feed_utilities.go | 23 ++++++++++++++ pkg/feeds/gcs_storage_feed.go | 56 +++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 pkg/feeds/gcs_storage_feed.go diff --git a/pkg/feeds/feed_resource.go b/pkg/feeds/feed_resource.go index 0fbb15c1..4c2a6a78 100644 --- a/pkg/feeds/feed_resource.go +++ b/pkg/feeds/feed_resource.go @@ -30,6 +30,9 @@ type FeedResource struct { LayoutRegex string `json:"LayoutRegex,omitempty"` Repository string `json:"Repository,omitempty"` UseMachineCredentials bool `json:"UseMachineCredentials,omitempty"` + UseServiceAccountKey bool `json:"UseServiceAccountKey,omitempty"` + ServiceAccountJsonKey *core.SensitiveValue `json:"ServiceAccountJsonKey,omitempty"` + Project string `json:"Project,omitempty"` resources.Resource } diff --git a/pkg/feeds/feed_types.go b/pkg/feeds/feed_types.go index 2de81ac3..e36fd391 100644 --- a/pkg/feeds/feed_types.go +++ b/pkg/feeds/feed_types.go @@ -16,4 +16,5 @@ const ( FeedTypeArtifactoryGeneric = FeedType("ArtifactoryGeneric") FeedTypeS3 = FeedType("S3") FeedTypeOCIRegistry = FeedType("OciRegistry") + FeedTypeGcsStorage = FeedType("GcsStorage") ) diff --git a/pkg/feeds/feed_utilities.go b/pkg/feeds/feed_utilities.go index eb8b21cc..40f99f0d 100644 --- a/pkg/feeds/feed_utilities.go +++ b/pkg/feeds/feed_utilities.go @@ -144,6 +144,18 @@ func ToFeed(feedResource *FeedResource) (IFeed, error) { return nil, err } feed = s3Feed + case FeedTypeGcsStorage: + var googleOidc *GoogleContainerRegistryOidcAuthentication + if feedResource.OidcAuthentication != nil { + if google, ok := feedResource.OidcAuthentication.GetGoogle(); ok { + googleOidc = google + } + } + gcsFeed, err := NewGcsStorageFeed(feedResource.GetName(), feedResource.UseServiceAccountKey, feedResource.ServiceAccountJsonKey, feedResource.Project, googleOidc) + if err != nil { + return nil, err + } + feed = gcsFeed case FeedTypeOCIRegistry: ociFeed, err := NewOCIRegistryFeed(feedResource.GetName()) if err != nil { @@ -271,6 +283,17 @@ func ToFeedResource(feed IFeed) (*FeedResource, error) { feedResource.AccessKey = s3Feed.AccessKey feedResource.SecretKey = s3Feed.SecretKey feedResource.UseMachineCredentials = s3Feed.UseMachineCredentials + case FeedTypeGcsStorage: + gcsFeed := feed.(*GcsStorageFeed) + feedResource.UseServiceAccountKey = gcsFeed.UseServiceAccountKey + feedResource.ServiceAccountJsonKey = gcsFeed.ServiceAccountJsonKey + feedResource.Project = gcsFeed.Project + if gcsFeed.OidcAuthentication != nil { + feedResource.OidcAuthentication = NewGoogleOidcAuthentication( + gcsFeed.OidcAuthentication.Audience, + gcsFeed.OidcAuthentication.SubjectKeys, + ) + } case FeedTypeOCIRegistry: ociFeed := feed.(*OCIRegistryFeed) feedResource.FeedURI = ociFeed.FeedURI diff --git a/pkg/feeds/gcs_storage_feed.go b/pkg/feeds/gcs_storage_feed.go new file mode 100644 index 00000000..0fdf2d1c --- /dev/null +++ b/pkg/feeds/gcs_storage_feed.go @@ -0,0 +1,56 @@ +package feeds + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/v2/internal" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/go-playground/validator/v10" + "github.com/go-playground/validator/v10/non-standard/validators" +) + +type GcsStorageFeed struct { + UseServiceAccountKey bool `json:"UseServiceAccountKey"` + ServiceAccountJsonKey *core.SensitiveValue `json:"ServiceAccountJsonKey,omitempty"` + Project string `json:"Project,omitempty"` + OidcAuthentication *GoogleContainerRegistryOidcAuthentication `json:"OidcAuthentication,omitempty"` + + feed +} + +func NewGcsStorageFeed(name string, useServiceAccountKey bool, serviceAccountJsonKey *core.SensitiveValue, project string, oidcAuthentication *GoogleContainerRegistryOidcAuthentication) (*GcsStorageFeed, error) { + if internal.IsEmpty(name) { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError("name") + } + + if useServiceAccountKey { + if serviceAccountJsonKey == nil || !serviceAccountJsonKey.HasValue { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError("serviceAccountJsonKey") + } + } else { + if oidcAuthentication == nil { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError("oidcAuthentication") + } + } + + feed := GcsStorageFeed{ + UseServiceAccountKey: useServiceAccountKey, + ServiceAccountJsonKey: serviceAccountJsonKey, + Project: project, + OidcAuthentication: oidcAuthentication, + feed: *newFeed(name, FeedTypeGcsStorage), + } + + if err := feed.Validate(); err != nil { + return nil, err + } + + return &feed, nil +} + +func (g *GcsStorageFeed) Validate() error { + v := validator.New() + err := v.RegisterValidation("notblank", validators.NotBlank) + if err != nil { + return err + } + return v.Struct(g) +} From 682f4e5a5b68226bf844336c1ad72b47bc653217 Mon Sep 17 00:00:00 2001 From: wlthomson Date: Tue, 10 Feb 2026 14:19:51 +1300 Subject: [PATCH 2/5] Update feed struct with retry fields --- pkg/feeds/gcs_storage_feed.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/pkg/feeds/gcs_storage_feed.go b/pkg/feeds/gcs_storage_feed.go index 0fdf2d1c..14924bcc 100644 --- a/pkg/feeds/gcs_storage_feed.go +++ b/pkg/feeds/gcs_storage_feed.go @@ -8,10 +8,12 @@ import ( ) type GcsStorageFeed struct { - UseServiceAccountKey bool `json:"UseServiceAccountKey"` - ServiceAccountJsonKey *core.SensitiveValue `json:"ServiceAccountJsonKey,omitempty"` - Project string `json:"Project,omitempty"` - OidcAuthentication *GoogleContainerRegistryOidcAuthentication `json:"OidcAuthentication,omitempty"` + UseServiceAccountKey bool `json:"UseServiceAccountKey"` + ServiceAccountJsonKey *core.SensitiveValue `json:"ServiceAccountJsonKey,omitempty"` + Project string `json:"Project,omitempty"` + OidcAuthentication *GoogleContainerRegistryOidcAuthentication `json:"OidcAuthentication,omitempty"` + DownloadAttempts int `json:"DownloadAttempts"` + DownloadRetryBackoffSeconds int `json:"DownloadRetryBackoffSeconds"` feed } @@ -32,11 +34,13 @@ func NewGcsStorageFeed(name string, useServiceAccountKey bool, serviceAccountJso } feed := GcsStorageFeed{ - UseServiceAccountKey: useServiceAccountKey, - ServiceAccountJsonKey: serviceAccountJsonKey, - Project: project, - OidcAuthentication: oidcAuthentication, - feed: *newFeed(name, FeedTypeGcsStorage), + UseServiceAccountKey: useServiceAccountKey, + ServiceAccountJsonKey: serviceAccountJsonKey, + Project: project, + OidcAuthentication: oidcAuthentication, + DownloadAttempts: 5, + DownloadRetryBackoffSeconds: 10, + feed: *newFeed(name, FeedTypeGcsStorage), } if err := feed.Validate(); err != nil { From 635d9b273cd1283170542908c97830c2d9b8abe3 Mon Sep 17 00:00:00 2001 From: wlthomson Date: Wed, 11 Feb 2026 15:24:05 +1300 Subject: [PATCH 3/5] Fix hardcoded retry values --- pkg/feeds/gcs_storage_feed.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/feeds/gcs_storage_feed.go b/pkg/feeds/gcs_storage_feed.go index 14924bcc..fdde85b9 100644 --- a/pkg/feeds/gcs_storage_feed.go +++ b/pkg/feeds/gcs_storage_feed.go @@ -38,8 +38,6 @@ func NewGcsStorageFeed(name string, useServiceAccountKey bool, serviceAccountJso ServiceAccountJsonKey: serviceAccountJsonKey, Project: project, OidcAuthentication: oidcAuthentication, - DownloadAttempts: 5, - DownloadRetryBackoffSeconds: 10, feed: *newFeed(name, FeedTypeGcsStorage), } From 9e4a69890c846b0e846b45a931f884ff3291dd4b Mon Sep 17 00:00:00 2001 From: wlthomson Date: Wed, 11 Feb 2026 15:28:17 +1300 Subject: [PATCH 4/5] Revert removal of default download retry values --- pkg/feeds/gcs_storage_feed.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/feeds/gcs_storage_feed.go b/pkg/feeds/gcs_storage_feed.go index fdde85b9..14924bcc 100644 --- a/pkg/feeds/gcs_storage_feed.go +++ b/pkg/feeds/gcs_storage_feed.go @@ -38,6 +38,8 @@ func NewGcsStorageFeed(name string, useServiceAccountKey bool, serviceAccountJso ServiceAccountJsonKey: serviceAccountJsonKey, Project: project, OidcAuthentication: oidcAuthentication, + DownloadAttempts: 5, + DownloadRetryBackoffSeconds: 10, feed: *newFeed(name, FeedTypeGcsStorage), } From 46b9ff712c5da00970318aa295c2fe93a30e0e69 Mon Sep 17 00:00:00 2001 From: wlthomson Date: Wed, 11 Feb 2026 15:34:26 +1300 Subject: [PATCH 5/5] Fix missing retry setters for GCS storage feeds --- pkg/feeds/feed_utilities.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/feeds/feed_utilities.go b/pkg/feeds/feed_utilities.go index fd083d67..412836ab 100644 --- a/pkg/feeds/feed_utilities.go +++ b/pkg/feeds/feed_utilities.go @@ -164,6 +164,8 @@ func ToFeed(feedResource *FeedResource) (IFeed, error) { if err != nil { return nil, err } + gcsFeed.DownloadAttempts = feedResource.DownloadAttempts + gcsFeed.DownloadRetryBackoffSeconds = feedResource.DownloadRetryBackoffSeconds feed = gcsFeed case FeedTypeOCIRegistry: ociFeed, err := NewOCIRegistryFeed(feedResource.GetName()) @@ -299,6 +301,8 @@ func ToFeedResource(feed IFeed) (*FeedResource, error) { feedResource.UseMachineCredentials = s3Feed.UseMachineCredentials case FeedTypeGcsStorage: gcsFeed := feed.(*GcsStorageFeed) + feedResource.DownloadAttempts = gcsFeed.DownloadAttempts + feedResource.DownloadRetryBackoffSeconds = gcsFeed.DownloadRetryBackoffSeconds feedResource.UseServiceAccountKey = gcsFeed.UseServiceAccountKey feedResource.ServiceAccountJsonKey = gcsFeed.ServiceAccountJsonKey feedResource.Project = gcsFeed.Project