From 7a666b56a450cb99b806d30fe0c66ca63a2b6d24 Mon Sep 17 00:00:00 2001 From: Patricia Salajova Date: Mon, 27 Apr 2026 12:45:58 +0200 Subject: [PATCH] Add dptp collection --- cmd/ci-secret-bootstrap/main.go | 20 ++++----- cmd/ci-secret-bootstrap/main_test.go | 15 +------ cmd/ci-secret-generator/main_test.go | 2 +- pkg/api/gsm.go | 41 +++++++++++++++---- pkg/api/gsm_test.go | 25 +++++++---- .../multi_stage/gsm_bundle_resolver_test.go | 3 +- 6 files changed, 65 insertions(+), 41 deletions(-) diff --git a/cmd/ci-secret-bootstrap/main.go b/cmd/ci-secret-bootstrap/main.go index dbc60bdafb..1b952b328b 100644 --- a/cmd/ci-secret-bootstrap/main.go +++ b/cmd/ci-secret-bootstrap/main.go @@ -457,38 +457,38 @@ func constructDockerConfigJSONFromVault(client secrets.ReadOnlyClient, dockerCon } // constructDockerConfigJSONFromGSM constructs a .dockerconfigjson from GSM secrets cache -func constructDockerConfigJSONFromGSM(secretsCache map[gsmSecretRef]fetchedSecret, registries []api.RegistryAuthData) ([]byte, error) { +func constructDockerConfigJSONFromGSM(secretsCache map[gsmSecretRef]fetchedSecret, registries []api.RegistryAuthData, gsmDPTPCollection string) ([]byte, error) { auths := make(map[string]secretbootstrap.DockerAuth) for _, reg := range registries { authData := secretbootstrap.DockerAuth{} authRef := gsmSecretRef{ - collection: reg.Collection, + collection: gsmDPTPCollection, group: reg.Group, field: reg.AuthField, } fetchedAuth, exists := secretsCache[authRef] if !exists { - return nil, fmt.Errorf("auth field '%s' (collection: %s, group: %s) not found in fetched secrets", reg.AuthField, reg.Collection, reg.Group) + return nil, fmt.Errorf("auth field '%s' (collection: %s, group: %s) not found in fetched secrets", reg.AuthField, gsmDPTPCollection, reg.Group) } if fetchedAuth.err != nil { - return nil, fmt.Errorf("couldn't get auth field '%s' (collection: %s, group: %s): %w", reg.AuthField, reg.Collection, reg.Group, fetchedAuth.err) + return nil, fmt.Errorf("couldn't get auth field '%s' (collection: %s, group: %s): %w", reg.AuthField, gsmDPTPCollection, reg.Group, fetchedAuth.err) } authData.Auth = string(bytes.TrimSpace(fetchedAuth.payload)) if reg.EmailField != "" { emailRef := gsmSecretRef{ - collection: reg.Collection, + collection: gsmDPTPCollection, group: reg.Group, field: reg.EmailField, } fetchedEmail, exists := secretsCache[emailRef] if !exists { - return nil, fmt.Errorf("email field '%s' (collection: %s, group: %s) not found in fetched secrets", reg.EmailField, reg.Collection, reg.Group) + return nil, fmt.Errorf("email field '%s' (collection: %s, group: %s) not found in fetched secrets", reg.EmailField, gsmDPTPCollection, reg.Group) } if fetchedEmail.err != nil { - return nil, fmt.Errorf("couldn't get email field '%s' (collection: %s, group: %s): %w", reg.EmailField, reg.Collection, reg.Group, fetchedEmail.err) + return nil, fmt.Errorf("couldn't get email field '%s' (collection: %s, group: %s): %w", reg.EmailField, gsmDPTPCollection, reg.Group, fetchedEmail.err) } authData.Email = string(fetchedEmail.payload) } @@ -1375,7 +1375,7 @@ func constructSecretsFromGSM( } for _, registryEntry := range bundle.DockerConfig.Registries { s := gsmSecretRef{ - collection: registryEntry.Collection, + collection: gsmConfig.DPTPCollection, group: registryEntry.Group, field: registryEntry.AuthField, } @@ -1383,7 +1383,7 @@ func constructSecretsFromGSM( if registryEntry.EmailField != "" { s := gsmSecretRef{ - collection: registryEntry.Collection, + collection: gsmConfig.DPTPCollection, group: registryEntry.Group, field: registryEntry.EmailField, } @@ -1496,7 +1496,7 @@ func constructSecretsFromGSM( } if bundle.DockerConfig != nil { - dockerConfigData, err := constructDockerConfigJSONFromGSM(fetchedGsmSecretsMap, bundle.DockerConfig.Registries) + dockerConfigData, err := constructDockerConfigJSONFromGSM(fetchedGsmSecretsMap, bundle.DockerConfig.Registries, gsmConfig.DPTPCollection) if err != nil { logrus.WithError(err).Errorf("skipping bundle %s: failed to construct dockerconfig", bundle.Name) errs = append(errs, fmt.Errorf("bundle %s: failed to construct dockerconfig: %w", bundle.Name, err)) diff --git a/cmd/ci-secret-bootstrap/main_test.go b/cmd/ci-secret-bootstrap/main_test.go index 031ec0af26..af8e94a972 100644 --- a/cmd/ci-secret-bootstrap/main_test.go +++ b/cmd/ci-secret-bootstrap/main_test.go @@ -3841,6 +3841,7 @@ func TestConstructSecretsFromGSM(t *testing.T) { { name: "docker config from GSM", config: api.GSMConfig{ + DPTPCollection: "test-infra", Bundles: []api.GSMBundle{ { Name: "docker-secret", @@ -3856,13 +3857,11 @@ func TestConstructSecretsFromGSM(t *testing.T) { As: "pull-secret", Registries: []api.RegistryAuthData{ { - Collection: "test-infra", Group: "build-farm", RegistryURL: "registry.ci.openshift.org", AuthField: "auth-token", }, { - Collection: "test-infra", Group: "build-farm", RegistryURL: "quay.io", AuthField: "quay-auth", @@ -4173,7 +4172,6 @@ func TestConstructDockerConfigJSONFromGSM(t *testing.T) { }, registries: []api.RegistryAuthData{ { - Collection: "test", Group: "grp", RegistryURL: "quay.io", AuthField: "auth", @@ -4189,7 +4187,6 @@ func TestConstructDockerConfigJSONFromGSM(t *testing.T) { }, registries: []api.RegistryAuthData{ { - Collection: "test", Group: "grp", RegistryURL: "quay.io", AuthField: "auth", @@ -4207,13 +4204,11 @@ func TestConstructDockerConfigJSONFromGSM(t *testing.T) { }, registries: []api.RegistryAuthData{ { - Collection: "test", Group: "grp", RegistryURL: "quay.io", AuthField: "auth1", }, { - Collection: "test", Group: "grp", RegistryURL: "registry.ci.openshift.org", AuthField: "auth2", @@ -4229,7 +4224,6 @@ func TestConstructDockerConfigJSONFromGSM(t *testing.T) { }, registries: []api.RegistryAuthData{ { - Collection: "test", Group: "grp", RegistryURL: "quay.io", AuthField: "auth", @@ -4242,7 +4236,6 @@ func TestConstructDockerConfigJSONFromGSM(t *testing.T) { secretsCache: map[gsmSecretRef]fetchedSecret{}, registries: []api.RegistryAuthData{ { - Collection: "test", Group: "grp", RegistryURL: "quay.io", AuthField: "missing", @@ -4257,7 +4250,6 @@ func TestConstructDockerConfigJSONFromGSM(t *testing.T) { }, registries: []api.RegistryAuthData{ { - Collection: "test", Group: "grp", RegistryURL: "quay.io", AuthField: "auth", @@ -4272,7 +4264,6 @@ func TestConstructDockerConfigJSONFromGSM(t *testing.T) { }, registries: []api.RegistryAuthData{ { - Collection: "test", Group: "grp", RegistryURL: "quay.io", AuthField: "auth", @@ -4289,7 +4280,6 @@ func TestConstructDockerConfigJSONFromGSM(t *testing.T) { }, registries: []api.RegistryAuthData{ { - Collection: "test", Group: "grp", RegistryURL: "quay.io", AuthField: "auth", @@ -4305,7 +4295,6 @@ func TestConstructDockerConfigJSONFromGSM(t *testing.T) { }, registries: []api.RegistryAuthData{ { - Collection: "test", Group: "grp", RegistryURL: "quay.io", AuthField: "auth", @@ -4317,7 +4306,7 @@ func TestConstructDockerConfigJSONFromGSM(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - actual, err := constructDockerConfigJSONFromGSM(tc.secretsCache, tc.registries) + actual, err := constructDockerConfigJSONFromGSM(tc.secretsCache, tc.registries, "test") if tc.expectedError != "" { if err == nil { diff --git a/cmd/ci-secret-generator/main_test.go b/cmd/ci-secret-generator/main_test.go index 36e6381127..a3ecdc9c46 100644 --- a/cmd/ci-secret-generator/main_test.go +++ b/cmd/ci-secret-generator/main_test.go @@ -302,6 +302,7 @@ func TestValidateContexts(t *testing.T) { Secrets: []secretbootstrap.SecretConfig{{}}, }, gsmConfig: api.GSMConfig{ + DPTPCollection: api.DPTPGSMCollection, Components: map[string][]api.GSMSecretRef{ "some-component": { { @@ -325,7 +326,6 @@ func TestValidateContexts(t *testing.T) { DockerConfig: &api.DockerConfigSpec{ Registries: []api.RegistryAuthData{ { - Collection: "test-platform", Group: "build_cluster", AuthField: "target-dc-field", RegistryURL: "https://registry.io", diff --git a/pkg/api/gsm.go b/pkg/api/gsm.go index 56ee64bd76..f23c3cfa27 100644 --- a/pkg/api/gsm.go +++ b/pkg/api/gsm.go @@ -14,11 +14,17 @@ import ( "github.com/openshift/ci-tools/pkg/util/gzip" ) +const ( + // DPTPGSMCollection is the default GSM collection for DPTP-managed secrets (dockerconfig items) + DPTPGSMCollection = "test-platform-infra" +) + // GSMConfig is the top-level configuration for GSM-based secrets type GSMConfig struct { - ClusterGroups map[string][]string `json:"cluster_groups,omitempty"` - Components map[string][]GSMSecretRef `json:"components,omitempty"` - Bundles []GSMBundle `json:"bundles"` + ClusterGroups map[string][]string `json:"cluster_groups,omitempty"` + DPTPCollection string `json:"dptp_collection,omitempty"` + Components map[string][]GSMSecretRef `json:"components,omitempty"` + Bundles []GSMBundle `json:"bundles,omitempty"` } // GSMBundle defines a logical group of GSM secrets @@ -67,8 +73,8 @@ type DockerConfigSpec struct { } // RegistryAuthData specifies registry credentials +// Collection is always DPTPGSMCollection, which matches dptp_collection in the GSM config type RegistryAuthData struct { - Collection string `json:"collection"` Group string `json:"group"` RegistryURL string `json:"registry_url"` AuthField string `json:"auth_field"` @@ -119,6 +125,15 @@ func (c *GSMConfig) UnmarshalJSON(d []byte) error { func (c *GSMConfig) resolve() error { var errs []error + if c.DPTPCollection == "" { + for _, bundle := range c.Bundles { + if bundle.DockerConfig != nil { + c.DPTPCollection = DPTPGSMCollection + break + } + } + } + // Expand cluster_groups to concrete cluster names for bundleIdx := range c.Bundles { bundle := &c.Bundles[bundleIdx] @@ -263,6 +278,21 @@ type bundleKey struct { func (c *GSMConfig) Validate() error { var errs []error + // Validate that dptp_collection is set if any bundle uses dockerconfig + hasDockerConfig := false + for _, bundle := range c.Bundles { + if bundle.DockerConfig != nil { + hasDockerConfig = true + break + } + } + if hasDockerConfig && c.DPTPCollection == "" { + errs = append(errs, fmt.Errorf("dptp_collection must be set when bundles use dockerconfig")) + } + if c.DPTPCollection != "" && !gsmvalidation.ValidateCollectionName(c.DPTPCollection) { + errs = append(errs, fmt.Errorf("dptp_collection has invalid collection name: %s", c.DPTPCollection)) + } + // Validate components componentNames := make(map[string]bool) @@ -423,9 +453,6 @@ func validateDockerConfig(dc *DockerConfigSpec, bundleIdx int, bundleName string } for i, reg := range dc.Registries { - if !gsmvalidation.ValidateCollectionName(reg.Collection) { - errs = append(errs, fmt.Errorf("bundle[%d] %s dockerconfig registry[%d] has invalid collection string", bundleIdx, bundleName, i)) - } if !gsmvalidation.ValidateGroupName(reg.Group) { errs = append(errs, fmt.Errorf("bundle[%d] %s dockerconfig registry[%d] has invalid group string", bundleIdx, bundleName, i)) } diff --git a/pkg/api/gsm_test.go b/pkg/api/gsm_test.go index 00d35f9fd3..7567e3f227 100644 --- a/pkg/api/gsm_test.go +++ b/pkg/api/gsm_test.go @@ -1284,13 +1284,14 @@ func TestGSMConfigValidate(t *testing.T) { { name: "valid config - dockerconfig with empty 'as' field defaults to .dockerconfigjson", config: GSMConfig{ + DPTPCollection: DPTPGSMCollection, Bundles: []GSMBundle{ { Name: "test-bundle", DockerConfig: &DockerConfigSpec{ As: "", Registries: []RegistryAuthData{ - {Collection: "creds", Group: "group1", RegistryURL: "quay.io", AuthField: "auth"}, + {Group: "group1", RegistryURL: "quay.io", AuthField: "auth"}, }, }, SyncToCluster: true, @@ -1305,6 +1306,7 @@ func TestGSMConfigValidate(t *testing.T) { { name: "error: dockerconfig with no registries", config: GSMConfig{ + DPTPCollection: DPTPGSMCollection, Bundles: []GSMBundle{ { Name: "test-bundle", @@ -1323,14 +1325,14 @@ func TestGSMConfigValidate(t *testing.T) { errorContains: "dockerconfig has no registries", }, { - name: "error: dockerconfig registry with empty collection", + name: "error: dockerconfig without dptp_collection set", config: GSMConfig{ Bundles: []GSMBundle{ { Name: "test-bundle", DockerConfig: &DockerConfigSpec{ Registries: []RegistryAuthData{ - {Collection: "", Group: "group1", RegistryURL: "quay.io", AuthField: "auth"}, + {Group: "group1", RegistryURL: "quay.io", AuthField: "auth"}, }, }, SyncToCluster: true, @@ -1341,18 +1343,19 @@ func TestGSMConfigValidate(t *testing.T) { }, }, expectError: true, - errorContains: "bundle[0] test-bundle dockerconfig registry[0] has invalid collection string", + errorContains: "dptp_collection must be set when bundles use dockerconfig", }, { name: "error: dockerconfig registry with empty registry_url", config: GSMConfig{ + DPTPCollection: DPTPGSMCollection, Bundles: []GSMBundle{ { Name: "test-bundle", DockerConfig: &DockerConfigSpec{ As: ".dockerconfigjson", Registries: []RegistryAuthData{ - {Collection: "creds", Group: "group1", RegistryURL: "", AuthField: "auth"}, + {Group: "group1", RegistryURL: "", AuthField: "auth"}, }, }, SyncToCluster: true, @@ -1368,13 +1371,14 @@ func TestGSMConfigValidate(t *testing.T) { { name: "error: dockerconfig registry with empty auth_field", config: GSMConfig{ + DPTPCollection: DPTPGSMCollection, Bundles: []GSMBundle{ { Name: "test-bundle", DockerConfig: &DockerConfigSpec{ As: ".dockerconfigjson", Registries: []RegistryAuthData{ - {Collection: "creds", Group: "group1", RegistryURL: "quay.io", AuthField: ""}, + {Group: "group1", RegistryURL: "quay.io", AuthField: ""}, }, }, SyncToCluster: true, @@ -1406,12 +1410,13 @@ func TestGSMConfigValidate(t *testing.T) { { name: "error: sync_to_cluster true but no targets (dockerconfig bundle)", config: GSMConfig{ + DPTPCollection: DPTPGSMCollection, Bundles: []GSMBundle{ { Name: "test-dockerconfig", DockerConfig: &DockerConfigSpec{ Registries: []RegistryAuthData{ - {Collection: "test-platform-infra", Group: "build_farm", RegistryURL: "registry.ci.openshift.org", AuthField: "token"}, + {Group: "group1", RegistryURL: "quay.io", AuthField: "auth"}, }, }, SyncToCluster: true, @@ -1425,12 +1430,13 @@ func TestGSMConfigValidate(t *testing.T) { { name: "error: target with empty cluster (dockerconfig bundle)", config: GSMConfig{ + DPTPCollection: DPTPGSMCollection, Bundles: []GSMBundle{ { Name: "test-dockerconfig", DockerConfig: &DockerConfigSpec{ Registries: []RegistryAuthData{ - {Collection: "test-platform-infra", Group: "build_farm", RegistryURL: "registry.ci.openshift.org", AuthField: "token"}, + {Group: "group1", RegistryURL: "quay.io", AuthField: "auth"}, }, }, SyncToCluster: true, @@ -1446,12 +1452,13 @@ func TestGSMConfigValidate(t *testing.T) { { name: "error: dockerconfig with sync_to_cluster false", config: GSMConfig{ + DPTPCollection: DPTPGSMCollection, Bundles: []GSMBundle{ { Name: "test-dockerconfig-bundle", DockerConfig: &DockerConfigSpec{ Registries: []RegistryAuthData{ - {Collection: "test-platform-infra", Group: "build_farm", RegistryURL: "registry.ci.openshift.org", AuthField: "token"}, + {Group: "group1", RegistryURL: "quay.io", AuthField: "auth"}, }, }, SyncToCluster: false, diff --git a/pkg/steps/multi_stage/gsm_bundle_resolver_test.go b/pkg/steps/multi_stage/gsm_bundle_resolver_test.go index 7528f56bb1..73b55336f0 100644 --- a/pkg/steps/multi_stage/gsm_bundle_resolver_test.go +++ b/pkg/steps/multi_stage/gsm_bundle_resolver_test.go @@ -244,6 +244,7 @@ func TestResolveCredentialReferences(t *testing.T) { }, }, gsmConfig: &api.GSMConfig{ + DPTPCollection: "test-platform-infra", Bundles: []api.GSMBundle{ { Name: "docker-bundle", @@ -253,7 +254,7 @@ func TestResolveCredentialReferences(t *testing.T) { }, DockerConfig: &api.DockerConfigSpec{ Registries: []api.RegistryAuthData{ - {Collection: "creds", Group: "registry", RegistryURL: "registry.ci.openshift.org", AuthField: "auth"}, + {Group: "registry", RegistryURL: "registry.ci.openshift.org", AuthField: "auth"}, }, }, },