From e19a401889e68b8f701a64f2aaee03c6e67c7d4f Mon Sep 17 00:00:00 2001 From: Mohamed Bilel Besrour Date: Tue, 19 May 2026 16:50:38 +0200 Subject: [PATCH 01/10] feat(bootstrap): add Central collector endpoint configuration --- cli/cmd/bootstrap_gcp.go | 1 + internal/bootstrap/gcp/gcp.go | 1 + internal/bootstrap/gcp/install_config.go | 1 + internal/bootstrap/gcp/install_config_test.go | 2 ++ internal/installer/files/config_yaml.go | 1 + 5 files changed, 6 insertions(+) diff --git a/cli/cmd/bootstrap_gcp.go b/cli/cmd/bootstrap_gcp.go index 368746c7..3065fffd 100644 --- a/cli/cmd/bootstrap_gcp.go +++ b/cli/cmd/bootstrap_gcp.go @@ -74,6 +74,7 @@ func AddBootstrapGcpCmd(parent *cobra.Command, opts *GlobalOptions) { flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.OidcIssuerURL, "oidc-issuer-url", "", "OIDC OAuth provider issuer URL (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.OidcClientID, "oidc-client-id", "", "OIDC OAuth provider Client ID (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.OidcClientSecret, "oidc-client-secret", "", "OIDC OAuth provider Client Secret (optional)") + flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelEndpoint, "central-otel-endpoint", "", "Central OpenTelemetry collector endpoint. Needed when sending spans to central collector (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelUsername, "central-otel-username", "", "Central OpenTelemetry username. Needed when sending spans to central collector (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelPassword, "central-otel-password", "", "Central OpenTelemetry password. Needed when sending spans to central collector (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.GitHubPAT, "github-pat", "", "GitHub Personal Access Token used for direct image access and fetching team SSH keys. Required when using --github-team-org/--github-team-slug. Required scopes: read:packages, read:org.") diff --git a/internal/bootstrap/gcp/gcp.go b/internal/bootstrap/gcp/gcp.go index 0eff6500..58926d12 100644 --- a/internal/bootstrap/gcp/gcp.go +++ b/internal/bootstrap/gcp/gcp.go @@ -114,6 +114,7 @@ type CodesphereEnvironment struct { RegistryUser string `json:"-"` Experiments []string `json:"experiments"` FeatureFlags map[string]bool `json:"feature_flags"` + CentralOtelEndpoint string `json:"-"` CentralOtelUsername string `json:"-"` CentralOtelPassword string `json:"-"` ExternalLokiEndpoint string `json:"external_loki_endpoint,omitempty"` diff --git a/internal/bootstrap/gcp/install_config.go b/internal/bootstrap/gcp/install_config.go index 91908827..f95aa5eb 100644 --- a/internal/bootstrap/gcp/install_config.go +++ b/internal/bootstrap/gcp/install_config.go @@ -340,6 +340,7 @@ func (b *GCPBootstrapper) UpdateInstallConfig() error { } b.Env.InstallConfig.Cluster.Monitoring.CentralOtelExport = &files.CentralOtelConfig{ Enabled: true, + Endpoint: b.Env.CentralOtelEndpoint, Username: b.Env.CentralOtelUsername, Password: b.Env.CentralOtelPassword, } diff --git a/internal/bootstrap/gcp/install_config_test.go b/internal/bootstrap/gcp/install_config_test.go index 30156f90..02787f30 100644 --- a/internal/bootstrap/gcp/install_config_test.go +++ b/internal/bootstrap/gcp/install_config_test.go @@ -601,6 +601,7 @@ var _ = Describe("Installconfig & Secrets", func() { Context("When CentralOtel credentials are fully set", func() { BeforeEach(func() { + csEnv.CentralOtelEndpoint = "https://otel.example.com" csEnv.CentralOtelUsername = "otel-user" csEnv.CentralOtelPassword = "otel-password" }) @@ -617,6 +618,7 @@ var _ = Describe("Installconfig & Secrets", func() { centralOtel := bs.Env.InstallConfig.Cluster.Monitoring.CentralOtelExport Expect(centralOtel).NotTo(BeNil()) Expect(centralOtel.Enabled).To(BeTrue()) + Expect(centralOtel.Endpoint).To(Equal("https://otel.example.com")) Expect(centralOtel.Username).To(Equal("otel-user")) Expect(centralOtel.Password).To(Equal("otel-password")) }) diff --git a/internal/installer/files/config_yaml.go b/internal/installer/files/config_yaml.go index 5221e288..bf5c0002 100644 --- a/internal/installer/files/config_yaml.go +++ b/internal/installer/files/config_yaml.go @@ -595,6 +595,7 @@ type LokiConnectionConfig struct { type CentralOtelConfig struct { Enabled bool `yaml:"enabled"` + Endpoint string `yaml:"endpoint,omitempty"` Username string `yaml:"username,omitempty"` Password string `yaml:"-"` Override ChartOverride `yaml:"override,omitempty"` From fb16ff29642545218f22c055bc63059fdda0c771 Mon Sep 17 00:00:00 2001 From: Mohamed Bilel Besrour Date: Wed, 20 May 2026 12:22:17 +0200 Subject: [PATCH 02/10] Enable telemetry config --- cli/cmd/bootstrap_gcp.go | 9 +- internal/bootstrap/gcp/gcp.go | 9 +- internal/bootstrap/gcp/install_config.go | 10 ++- internal/bootstrap/gcp/install_config_test.go | 88 ++++++++++++++++++- internal/installer/files/config_yaml.go | 9 +- 5 files changed, 115 insertions(+), 10 deletions(-) diff --git a/cli/cmd/bootstrap_gcp.go b/cli/cmd/bootstrap_gcp.go index 3065fffd..5099b681 100644 --- a/cli/cmd/bootstrap_gcp.go +++ b/cli/cmd/bootstrap_gcp.go @@ -74,9 +74,6 @@ func AddBootstrapGcpCmd(parent *cobra.Command, opts *GlobalOptions) { flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.OidcIssuerURL, "oidc-issuer-url", "", "OIDC OAuth provider issuer URL (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.OidcClientID, "oidc-client-id", "", "OIDC OAuth provider Client ID (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.OidcClientSecret, "oidc-client-secret", "", "OIDC OAuth provider Client Secret (optional)") - flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelEndpoint, "central-otel-endpoint", "", "Central OpenTelemetry collector endpoint. Needed when sending spans to central collector (optional)") - flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelUsername, "central-otel-username", "", "Central OpenTelemetry username. Needed when sending spans to central collector (optional)") - flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelPassword, "central-otel-password", "", "Central OpenTelemetry password. Needed when sending spans to central collector (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.GitHubPAT, "github-pat", "", "GitHub Personal Access Token used for direct image access and fetching team SSH keys. Required when using --github-team-org/--github-team-slug. Required scopes: read:packages, read:org.") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.GitHubAppName, "github-app-name", "", "GitHub App Name (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.GitHubTeamOrg, "github-team-org", "", "GitHub organization used to fetch team SSH keys (optional, used with --github-team-slug). Requires --github-pat with at least the read:org scope.") @@ -119,6 +116,12 @@ func AddBootstrapGcpCmd(parent *cobra.Command, opts *GlobalOptions) { flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.OpenBaoUser, "openbao-user", "admin", "OpenBao username (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.OpenBaoPassword, "openbao-password", "", "OpenBao password (optional)") + flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelEndpoint, "central-otel-endpoint", "", "Central OpenTelemetry collector endpoint. Needed when sending spans to central collector (optional)") + flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelUsername, "central-otel-username", "", "Central OpenTelemetry username. Needed when sending spans to central collector (optional)") + flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelPassword, "central-otel-password", "", "Central OpenTelemetry password. Needed when sending spans to central collector (optional)") + flags.BoolVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelSpanMetrics, "central-otel-span-metrics", false, "Enable span metrics in Central OpenTelemetry export (default: false)") + flags.BoolVar(&bootstrapGcpCmd.CodesphereEnv.LocalTraceExport, "local-trace-export", false, "Enable local trace export (default: false)") + util.MarkFlagRequired(bootstrapGcpCmd.cmd, "project-name") util.MarkFlagRequired(bootstrapGcpCmd.cmd, "billing-account") util.MarkFlagRequired(bootstrapGcpCmd.cmd, "base-domain") diff --git a/internal/bootstrap/gcp/gcp.go b/internal/bootstrap/gcp/gcp.go index 58926d12..9614fab3 100644 --- a/internal/bootstrap/gcp/gcp.go +++ b/internal/bootstrap/gcp/gcp.go @@ -114,9 +114,6 @@ type CodesphereEnvironment struct { RegistryUser string `json:"-"` Experiments []string `json:"experiments"` FeatureFlags map[string]bool `json:"feature_flags"` - CentralOtelEndpoint string `json:"-"` - CentralOtelUsername string `json:"-"` - CentralOtelPassword string `json:"-"` ExternalLokiEndpoint string `json:"external_loki_endpoint,omitempty"` ExternalLokiSecret string `json:"-"` ExternalLokiUser string `json:"external_loki_user,omitempty"` @@ -127,6 +124,12 @@ type CodesphereEnvironment struct { OpenBaoUser string `json:"-"` OpenBaoPassword string `json:"-"` + CentralOtelEndpoint string `json:"-"` + CentralOtelUsername string `json:"-"` + CentralOtelPassword string `json:"-"` + CentralOtelSpanMetrics bool `json:"-"` + LocalTraceExport bool `json:"-"` + // Config InstallConfigPath string `json:"-"` SecretsFilePath string `json:"-"` diff --git a/internal/bootstrap/gcp/install_config.go b/internal/bootstrap/gcp/install_config.go index f95aa5eb..8d5d8099 100644 --- a/internal/bootstrap/gcp/install_config.go +++ b/internal/bootstrap/gcp/install_config.go @@ -319,6 +319,15 @@ func (b *GCPBootstrapper) UpdateInstallConfig() error { } } + if b.Env.CentralOtelPassword != "" || b.Env.LocalTraceExport != false { + b.Env.InstallConfig.Codesphere.TelemetryExport = &files.TelemetryExport{ + Endpoint: b.Env.CentralOtelEndpoint, + RemoteExport: b.Env.CentralOtelPassword != "", + Traces: b.Env.LocalTraceExport, + SpanMetrics: b.Env.CentralOtelSpanMetrics, + } + } + b.Env.InstallConfig.Codesphere.Experiments = b.Env.Experiments b.Env.InstallConfig.Codesphere.Features = b.Env.FeatureFlags b.applyExternalLokiConfig() @@ -340,7 +349,6 @@ func (b *GCPBootstrapper) UpdateInstallConfig() error { } b.Env.InstallConfig.Cluster.Monitoring.CentralOtelExport = &files.CentralOtelConfig{ Enabled: true, - Endpoint: b.Env.CentralOtelEndpoint, Username: b.Env.CentralOtelUsername, Password: b.Env.CentralOtelPassword, } diff --git a/internal/bootstrap/gcp/install_config_test.go b/internal/bootstrap/gcp/install_config_test.go index 02787f30..341bfdfe 100644 --- a/internal/bootstrap/gcp/install_config_test.go +++ b/internal/bootstrap/gcp/install_config_test.go @@ -601,7 +601,6 @@ var _ = Describe("Installconfig & Secrets", func() { Context("When CentralOtel credentials are fully set", func() { BeforeEach(func() { - csEnv.CentralOtelEndpoint = "https://otel.example.com" csEnv.CentralOtelUsername = "otel-user" csEnv.CentralOtelPassword = "otel-password" }) @@ -618,7 +617,6 @@ var _ = Describe("Installconfig & Secrets", func() { centralOtel := bs.Env.InstallConfig.Cluster.Monitoring.CentralOtelExport Expect(centralOtel).NotTo(BeNil()) Expect(centralOtel.Enabled).To(BeTrue()) - Expect(centralOtel.Endpoint).To(Equal("https://otel.example.com")) Expect(centralOtel.Username).To(Equal("otel-user")) Expect(centralOtel.Password).To(Equal("otel-password")) }) @@ -740,6 +738,92 @@ var _ = Describe("Installconfig & Secrets", func() { Expect(loki.User).To(BeEmpty()) }) }) + + Context("When CentralOtelPassword is set", func() { + BeforeEach(func() { + csEnv.CentralOtelPassword = "otel-secret" + csEnv.CentralOtelEndpoint = "https://otel.example.com" + csEnv.CentralOtelSpanMetrics = true + }) + It("sets TelemetryExport with RemoteExport true", func() { + icg.EXPECT().GenerateSecrets().Return(nil) + icg.EXPECT().WriteInstallConfig("fake-config-file", true).Return(nil) + icg.EXPECT().WriteVault("fake-secret", true).Return(nil) + nodeClient.EXPECT().CopyFile(mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice() + + err := bs.UpdateInstallConfig() + Expect(err).NotTo(HaveOccurred()) + + te := bs.Env.InstallConfig.Codesphere.TelemetryExport + Expect(te).NotTo(BeNil()) + Expect(te.Endpoint).To(Equal("https://otel.example.com")) + Expect(te.RemoteExport).To(BeTrue()) + Expect(te.Traces).To(BeFalse()) + Expect(te.SpanMetrics).To(BeTrue()) + }) + }) + + Context("When LocalTraceExport is true (no password)", func() { + BeforeEach(func() { + csEnv.LocalTraceExport = true + csEnv.CentralOtelEndpoint = "https://otel.example.com" + }) + It("sets TelemetryExport with Traces true and RemoteExport false", func() { + icg.EXPECT().GenerateSecrets().Return(nil) + icg.EXPECT().WriteInstallConfig("fake-config-file", true).Return(nil) + icg.EXPECT().WriteVault("fake-secret", true).Return(nil) + nodeClient.EXPECT().CopyFile(mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice() + + err := bs.UpdateInstallConfig() + Expect(err).NotTo(HaveOccurred()) + + te := bs.Env.InstallConfig.Codesphere.TelemetryExport + Expect(te).NotTo(BeNil()) + Expect(te.Endpoint).To(Equal("https://otel.example.com")) + Expect(te.RemoteExport).To(BeFalse()) + Expect(te.Traces).To(BeTrue()) + Expect(te.SpanMetrics).To(BeFalse()) + }) + }) + + Context("When both CentralOtelPassword and LocalTraceExport are set", func() { + BeforeEach(func() { + csEnv.CentralOtelPassword = "otel-secret" + csEnv.LocalTraceExport = true + csEnv.CentralOtelEndpoint = "https://otel.example.com" + csEnv.CentralOtelSpanMetrics = true + }) + It("sets TelemetryExport with both RemoteExport and Traces true", func() { + icg.EXPECT().GenerateSecrets().Return(nil) + icg.EXPECT().WriteInstallConfig("fake-config-file", true).Return(nil) + icg.EXPECT().WriteVault("fake-secret", true).Return(nil) + nodeClient.EXPECT().CopyFile(mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice() + + err := bs.UpdateInstallConfig() + Expect(err).NotTo(HaveOccurred()) + + te := bs.Env.InstallConfig.Codesphere.TelemetryExport + Expect(te).NotTo(BeNil()) + Expect(te.Endpoint).To(Equal("https://otel.example.com")) + Expect(te.RemoteExport).To(BeTrue()) + Expect(te.Traces).To(BeTrue()) + Expect(te.SpanMetrics).To(BeTrue()) + }) + }) + + Context("When neither CentralOtelPassword nor LocalTraceExport are set", func() { + It("leaves TelemetryExport nil", func() { + icg.EXPECT().GenerateSecrets().Return(nil) + icg.EXPECT().WriteInstallConfig("fake-config-file", true).Return(nil) + icg.EXPECT().WriteVault("fake-secret", true).Return(nil) + nodeClient.EXPECT().CopyFile(mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice() + + err := bs.UpdateInstallConfig() + Expect(err).NotTo(HaveOccurred()) + + Expect(bs.Env.InstallConfig.Codesphere.TelemetryExport).To(BeNil()) + }) + }) }) Describe("Invalid cases", func() { diff --git a/internal/installer/files/config_yaml.go b/internal/installer/files/config_yaml.go index bf5c0002..55a3fd57 100644 --- a/internal/installer/files/config_yaml.go +++ b/internal/installer/files/config_yaml.go @@ -315,6 +315,7 @@ type CodesphereConfig struct { ManagedServices []ManagedServiceConfig `yaml:"managedServices,omitempty"` OpenBao *OpenBaoConfig `yaml:"openBao,omitempty"` Migration *MigrationConfig `yaml:"migration,omitempty"` + TelemetryExport *TelemetryExport `yaml:"telemetryExport,omitempty"` Override ChartOverride `yaml:"override,omitempty"` DomainAuthPrivateKey string `yaml:"-"` @@ -399,6 +400,13 @@ type FlavorConfig struct { Pool map[int]int `yaml:"pool"` } +type TelemetryExport struct { + Endpoint string `yaml:"endpoint"` + RemoteExport bool `yaml:"remoteExport"` + Traces bool `yaml:"traces"` + SpanMetrics bool `yaml:"spanMetrics"` +} + type ChartOverride = map[string]interface{} type ImageRef struct { @@ -595,7 +603,6 @@ type LokiConnectionConfig struct { type CentralOtelConfig struct { Enabled bool `yaml:"enabled"` - Endpoint string `yaml:"endpoint,omitempty"` Username string `yaml:"username,omitempty"` Password string `yaml:"-"` Override ChartOverride `yaml:"override,omitempty"` From 75f26166f137bbff44b746b54349b8f43ad0d704 Mon Sep 17 00:00:00 2001 From: BBesrour <58034472+BBesrour@users.noreply.github.com> Date: Wed, 20 May 2026 10:24:44 +0000 Subject: [PATCH 03/10] chore(docs): Auto-update docs and licenses Signed-off-by: BBesrour <58034472+BBesrour@users.noreply.github.com> --- docs/oms_beta_bootstrap-gcp.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/oms_beta_bootstrap-gcp.md b/docs/oms_beta_bootstrap-gcp.md index b3f168cb..67505415 100644 --- a/docs/oms_beta_bootstrap-gcp.md +++ b/docs/oms_beta_bootstrap-gcp.md @@ -23,7 +23,9 @@ oms beta bootstrap-gcp [flags] --billing-account string GCP Billing Account ID (required) --bitbucket-app-client-id string Bitbucket App Client ID (optional) --bitbucket-app-client-secret string Bitbucket App Client Secret (optional) + --central-otel-endpoint string Central OpenTelemetry collector endpoint. Needed when sending spans to central collector (optional) --central-otel-password string Central OpenTelemetry password. Needed when sending spans to central collector (optional) + --central-otel-span-metrics Enable span metrics in Central OpenTelemetry export (default: false) --central-otel-username string Central OpenTelemetry username. Needed when sending spans to central collector (optional) --create-test-user Create a test user with API token on the bootstrapped instance for smoke testing (default: false) --custom-pg-ip string Custom PostgreSQL IP (optional) @@ -51,6 +53,7 @@ oms beta bootstrap-gcp [flags] --install-local string Install Codesphere from local package (default: none) -s, --install-skip-steps stringArray Installation steps to skip during Codesphere installation (optional) --install-version string Codesphere version to install (default: none) + --local-trace-export Enable local trace export (default: false) --oidc-client-id string OIDC OAuth provider Client ID (optional) --oidc-client-secret string OIDC OAuth provider Client Secret (optional) --oidc-issuer-url string OIDC OAuth provider issuer URL (optional) From 72e9d1205f59f0de0162a0e12d08d403b86bbe21 Mon Sep 17 00:00:00 2001 From: Mohamed Bilel Besrour Date: Wed, 20 May 2026 12:28:10 +0200 Subject: [PATCH 04/10] fix --- internal/bootstrap/gcp/install_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/bootstrap/gcp/install_config.go b/internal/bootstrap/gcp/install_config.go index 8d5d8099..c437c8bc 100644 --- a/internal/bootstrap/gcp/install_config.go +++ b/internal/bootstrap/gcp/install_config.go @@ -319,7 +319,7 @@ func (b *GCPBootstrapper) UpdateInstallConfig() error { } } - if b.Env.CentralOtelPassword != "" || b.Env.LocalTraceExport != false { + if b.Env.CentralOtelPassword != "" || b.Env.LocalTraceExport { b.Env.InstallConfig.Codesphere.TelemetryExport = &files.TelemetryExport{ Endpoint: b.Env.CentralOtelEndpoint, RemoteExport: b.Env.CentralOtelPassword != "", From 8d4ba9eda535033e34ca3e483a4de3e1c93c5eb8 Mon Sep 17 00:00:00 2001 From: Mohamed Bilel Besrour Date: Thu, 21 May 2026 17:07:16 +0200 Subject: [PATCH 05/10] feedbacl --- internal/bootstrap/gcp/gcp.go | 22 +++++++++++++++++++- internal/bootstrap/gcp/gcp_test.go | 32 ++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/internal/bootstrap/gcp/gcp.go b/internal/bootstrap/gcp/gcp.go index 9614fab3..35b3ae8e 100644 --- a/internal/bootstrap/gcp/gcp.go +++ b/internal/bootstrap/gcp/gcp.go @@ -407,7 +407,12 @@ func (b *GCPBootstrapper) ValidateInput() error { return err } - return b.validateExternalLokiParams() + err = b.validateExternalLokiParams() + if err != nil { + return err + } + + return b.validateCentralOtelParams() } // validateInstallVersion checks if the specified install version exists and contains the required installer artifact @@ -514,6 +519,21 @@ func (b *GCPBootstrapper) validateExternalLokiParams() error { return nil } +func (b *GCPBootstrapper) validateCentralOtelParams() error { + if b.Env.CentralOtelEndpoint != "" && b.Env.CentralOtelPassword == "" { + return fmt.Errorf("central OTel password is required when central OTel endpoint is set") + } + + if b.Env.CentralOtelUsername != "" && b.Env.CentralOtelPassword == "" { + return fmt.Errorf("central OTel username is set but password is missing") + } + if b.Env.CentralOtelPassword != "" && b.Env.CentralOtelUsername == "" { + return fmt.Errorf("central OTel password is set but username is missing") + } + + return nil +} + func (b *GCPBootstrapper) EnsureArtifactRegistry() error { repoName := "codesphere-registry" diff --git a/internal/bootstrap/gcp/gcp_test.go b/internal/bootstrap/gcp/gcp_test.go index 6247faf3..79af7f2a 100644 --- a/internal/bootstrap/gcp/gcp_test.go +++ b/internal/bootstrap/gcp/gcp_test.go @@ -574,6 +574,38 @@ var _ = Describe("GCP Bootstrapper", func() { Expect(err).To(MatchError(ContainSubstring("external Loki endpoint is required"))) }) }) + + Context("When central OTel endpoint is set but password is missing", func() { + BeforeEach(func() { + csEnv.CentralOtelEndpoint = "https://otel.example.com" + csEnv.CentralOtelUsername = "otel-user" + }) + It("returns an error", func() { + err := bs.ValidateInput() + Expect(err).To(MatchError(ContainSubstring("central OTel password is required when central OTel endpoint is set"))) + }) + }) + + Context("When central OTel username is set but password is missing", func() { + BeforeEach(func() { + csEnv.CentralOtelUsername = "otel-user" + }) + It("returns an error", func() { + err := bs.ValidateInput() + Expect(err).To(MatchError(ContainSubstring("central OTel username is set but password is missing"))) + }) + }) + + Context("When central OTel password is set but username is missing", func() { + BeforeEach(func() { + csEnv.CentralOtelPassword = "otel-secret" + csEnv.CentralOtelEndpoint = "https://otel.example.com" + }) + It("returns an error", func() { + err := bs.ValidateInput() + Expect(err).To(MatchError(ContainSubstring("central OTel password is set but username is missing"))) + }) + }) }) Describe("EnsureInstallConfig", func() { From f652419f213f3cb65403fd87875f6d8f6d8cb04c Mon Sep 17 00:00:00 2001 From: BBesrour <58034472+BBesrour@users.noreply.github.com> Date: Thu, 21 May 2026 15:09:14 +0000 Subject: [PATCH 06/10] chore(docs): Auto-update docs and licenses Signed-off-by: BBesrour <58034472+BBesrour@users.noreply.github.com> --- NOTICE | 8 ++++---- internal/tmpl/NOTICE | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/NOTICE b/NOTICE index e6030a6c..416b1387 100644 --- a/NOTICE +++ b/NOTICE @@ -1213,25 +1213,25 @@ License URL: https://github.com/protocolbuffers/protobuf-go/blob/f2248ac996af/LI Module: gopkg.in/evanphx/json-patch.v4 Version: v4.13.0 License: BSD-3-Clause -License URL: https://github.com/evanphx/json-patch/blob/v4.13.0/LICENSE +License URL: Unknown ---------- Module: gopkg.in/inf.v0 Version: v0.9.1 License: BSD-3-Clause -License URL: https://github.com/go-inf/inf/blob/v0.9.1/LICENSE +License URL: Unknown ---------- Module: gopkg.in/validator.v2 Version: v2.0.1 License: Apache-2.0 -License URL: https://github.com/go-validator/validator/blob/v2.0.1/LICENSE +License URL: Unknown ---------- Module: gopkg.in/yaml.v3 Version: v3.0.1 License: MIT -License URL: https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE +License URL: Unknown ---------- Module: helm.sh/helm/v4 diff --git a/internal/tmpl/NOTICE b/internal/tmpl/NOTICE index e6030a6c..416b1387 100644 --- a/internal/tmpl/NOTICE +++ b/internal/tmpl/NOTICE @@ -1213,25 +1213,25 @@ License URL: https://github.com/protocolbuffers/protobuf-go/blob/f2248ac996af/LI Module: gopkg.in/evanphx/json-patch.v4 Version: v4.13.0 License: BSD-3-Clause -License URL: https://github.com/evanphx/json-patch/blob/v4.13.0/LICENSE +License URL: Unknown ---------- Module: gopkg.in/inf.v0 Version: v0.9.1 License: BSD-3-Clause -License URL: https://github.com/go-inf/inf/blob/v0.9.1/LICENSE +License URL: Unknown ---------- Module: gopkg.in/validator.v2 Version: v2.0.1 License: Apache-2.0 -License URL: https://github.com/go-validator/validator/blob/v2.0.1/LICENSE +License URL: Unknown ---------- Module: gopkg.in/yaml.v3 Version: v3.0.1 License: MIT -License URL: https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE +License URL: Unknown ---------- Module: helm.sh/helm/v4 From ef41ef0b707db15af7caa76fdf79c14524e5fdba Mon Sep 17 00:00:00 2001 From: BBesrour <58034472+BBesrour@users.noreply.github.com> Date: Thu, 21 May 2026 15:10:49 +0000 Subject: [PATCH 07/10] chore(docs): Auto-update docs and licenses Signed-off-by: BBesrour <58034472+BBesrour@users.noreply.github.com> --- NOTICE | 8 ++++---- internal/tmpl/NOTICE | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/NOTICE b/NOTICE index 416b1387..e6030a6c 100644 --- a/NOTICE +++ b/NOTICE @@ -1213,25 +1213,25 @@ License URL: https://github.com/protocolbuffers/protobuf-go/blob/f2248ac996af/LI Module: gopkg.in/evanphx/json-patch.v4 Version: v4.13.0 License: BSD-3-Clause -License URL: Unknown +License URL: https://github.com/evanphx/json-patch/blob/v4.13.0/LICENSE ---------- Module: gopkg.in/inf.v0 Version: v0.9.1 License: BSD-3-Clause -License URL: Unknown +License URL: https://github.com/go-inf/inf/blob/v0.9.1/LICENSE ---------- Module: gopkg.in/validator.v2 Version: v2.0.1 License: Apache-2.0 -License URL: Unknown +License URL: https://github.com/go-validator/validator/blob/v2.0.1/LICENSE ---------- Module: gopkg.in/yaml.v3 Version: v3.0.1 License: MIT -License URL: Unknown +License URL: https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE ---------- Module: helm.sh/helm/v4 diff --git a/internal/tmpl/NOTICE b/internal/tmpl/NOTICE index 416b1387..e6030a6c 100644 --- a/internal/tmpl/NOTICE +++ b/internal/tmpl/NOTICE @@ -1213,25 +1213,25 @@ License URL: https://github.com/protocolbuffers/protobuf-go/blob/f2248ac996af/LI Module: gopkg.in/evanphx/json-patch.v4 Version: v4.13.0 License: BSD-3-Clause -License URL: Unknown +License URL: https://github.com/evanphx/json-patch/blob/v4.13.0/LICENSE ---------- Module: gopkg.in/inf.v0 Version: v0.9.1 License: BSD-3-Clause -License URL: Unknown +License URL: https://github.com/go-inf/inf/blob/v0.9.1/LICENSE ---------- Module: gopkg.in/validator.v2 Version: v2.0.1 License: Apache-2.0 -License URL: Unknown +License URL: https://github.com/go-validator/validator/blob/v2.0.1/LICENSE ---------- Module: gopkg.in/yaml.v3 Version: v3.0.1 License: MIT -License URL: Unknown +License URL: https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE ---------- Module: helm.sh/helm/v4 From a0535294daec442e262289464ab66717cf2ef784 Mon Sep 17 00:00:00 2001 From: Mohamed Bilel Besrour Date: Tue, 26 May 2026 09:53:51 +0200 Subject: [PATCH 08/10] update spec --- cli/cmd/bootstrap_gcp.go | 2 +- internal/bootstrap/gcp/gcp.go | 6 +++--- internal/bootstrap/gcp/install_config.go | 11 ++++++----- internal/bootstrap/gcp/install_config_test.go | 16 ++++++++-------- internal/installer/files/config_yaml.go | 9 +++++---- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/cli/cmd/bootstrap_gcp.go b/cli/cmd/bootstrap_gcp.go index 5099b681..6d0fbf75 100644 --- a/cli/cmd/bootstrap_gcp.go +++ b/cli/cmd/bootstrap_gcp.go @@ -119,8 +119,8 @@ func AddBootstrapGcpCmd(parent *cobra.Command, opts *GlobalOptions) { flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelEndpoint, "central-otel-endpoint", "", "Central OpenTelemetry collector endpoint. Needed when sending spans to central collector (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelUsername, "central-otel-username", "", "Central OpenTelemetry username. Needed when sending spans to central collector (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelPassword, "central-otel-password", "", "Central OpenTelemetry password. Needed when sending spans to central collector (optional)") + flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.LocalTraceEndpoint, "local-trace-endpoint", "", "Endpoint for exporting traces to an in-cluster storage (optional)") flags.BoolVar(&bootstrapGcpCmd.CodesphereEnv.CentralOtelSpanMetrics, "central-otel-span-metrics", false, "Enable span metrics in Central OpenTelemetry export (default: false)") - flags.BoolVar(&bootstrapGcpCmd.CodesphereEnv.LocalTraceExport, "local-trace-export", false, "Enable local trace export (default: false)") util.MarkFlagRequired(bootstrapGcpCmd.cmd, "project-name") util.MarkFlagRequired(bootstrapGcpCmd.cmd, "billing-account") diff --git a/internal/bootstrap/gcp/gcp.go b/internal/bootstrap/gcp/gcp.go index 35b3ae8e..e6ae656e 100644 --- a/internal/bootstrap/gcp/gcp.go +++ b/internal/bootstrap/gcp/gcp.go @@ -128,7 +128,7 @@ type CodesphereEnvironment struct { CentralOtelUsername string `json:"-"` CentralOtelPassword string `json:"-"` CentralOtelSpanMetrics bool `json:"-"` - LocalTraceExport bool `json:"-"` + LocalTraceEndpoint string `json:"-"` // Config InstallConfigPath string `json:"-"` @@ -412,7 +412,7 @@ func (b *GCPBootstrapper) ValidateInput() error { return err } - return b.validateCentralOtelParams() + return b.validateTelemetryExportParams() } // validateInstallVersion checks if the specified install version exists and contains the required installer artifact @@ -519,7 +519,7 @@ func (b *GCPBootstrapper) validateExternalLokiParams() error { return nil } -func (b *GCPBootstrapper) validateCentralOtelParams() error { +func (b *GCPBootstrapper) validateTelemetryExportParams() error { if b.Env.CentralOtelEndpoint != "" && b.Env.CentralOtelPassword == "" { return fmt.Errorf("central OTel password is required when central OTel endpoint is set") } diff --git a/internal/bootstrap/gcp/install_config.go b/internal/bootstrap/gcp/install_config.go index c437c8bc..e056896c 100644 --- a/internal/bootstrap/gcp/install_config.go +++ b/internal/bootstrap/gcp/install_config.go @@ -319,12 +319,13 @@ func (b *GCPBootstrapper) UpdateInstallConfig() error { } } - if b.Env.CentralOtelPassword != "" || b.Env.LocalTraceExport { + if b.Env.CentralOtelPassword != "" || b.Env.LocalTraceEndpoint != "" { b.Env.InstallConfig.Codesphere.TelemetryExport = &files.TelemetryExport{ - Endpoint: b.Env.CentralOtelEndpoint, - RemoteExport: b.Env.CentralOtelPassword != "", - Traces: b.Env.LocalTraceExport, - SpanMetrics: b.Env.CentralOtelSpanMetrics, + RemoteEndpoint: b.Env.CentralOtelEndpoint, + RemoteExport: b.Env.CentralOtelPassword != "", + Traces: b.Env.LocalTraceEndpoint != "", + TraceEndpoint: b.Env.LocalTraceEndpoint, + SpanMetrics: b.Env.CentralOtelSpanMetrics, } } diff --git a/internal/bootstrap/gcp/install_config_test.go b/internal/bootstrap/gcp/install_config_test.go index 341bfdfe..e59129ca 100644 --- a/internal/bootstrap/gcp/install_config_test.go +++ b/internal/bootstrap/gcp/install_config_test.go @@ -756,16 +756,16 @@ var _ = Describe("Installconfig & Secrets", func() { te := bs.Env.InstallConfig.Codesphere.TelemetryExport Expect(te).NotTo(BeNil()) - Expect(te.Endpoint).To(Equal("https://otel.example.com")) + Expect(te.RemoteEndpoint).To(Equal("https://otel.example.com")) Expect(te.RemoteExport).To(BeTrue()) Expect(te.Traces).To(BeFalse()) Expect(te.SpanMetrics).To(BeTrue()) }) }) - Context("When LocalTraceExport is true (no password)", func() { + Context("When LocalTraceEndpoint is set (no password)", func() { BeforeEach(func() { - csEnv.LocalTraceExport = true + csEnv.LocalTraceEndpoint = "http://localhost:4318" csEnv.CentralOtelEndpoint = "https://otel.example.com" }) It("sets TelemetryExport with Traces true and RemoteExport false", func() { @@ -779,17 +779,17 @@ var _ = Describe("Installconfig & Secrets", func() { te := bs.Env.InstallConfig.Codesphere.TelemetryExport Expect(te).NotTo(BeNil()) - Expect(te.Endpoint).To(Equal("https://otel.example.com")) + Expect(te.RemoteEndpoint).To(Equal("https://otel.example.com")) Expect(te.RemoteExport).To(BeFalse()) Expect(te.Traces).To(BeTrue()) + Expect(te.TraceEndpoint).To(Equal("http://localhost:4318")) Expect(te.SpanMetrics).To(BeFalse()) }) }) - Context("When both CentralOtelPassword and LocalTraceExport are set", func() { + Context("When both CentralOtelPassword and LocalTraceEndpoint are set", func() { BeforeEach(func() { csEnv.CentralOtelPassword = "otel-secret" - csEnv.LocalTraceExport = true csEnv.CentralOtelEndpoint = "https://otel.example.com" csEnv.CentralOtelSpanMetrics = true }) @@ -804,9 +804,9 @@ var _ = Describe("Installconfig & Secrets", func() { te := bs.Env.InstallConfig.Codesphere.TelemetryExport Expect(te).NotTo(BeNil()) - Expect(te.Endpoint).To(Equal("https://otel.example.com")) + Expect(te.RemoteEndpoint).To(Equal("https://otel.example.com")) Expect(te.RemoteExport).To(BeTrue()) - Expect(te.Traces).To(BeTrue()) + Expect(te.Traces).To(BeFalse()) Expect(te.SpanMetrics).To(BeTrue()) }) }) diff --git a/internal/installer/files/config_yaml.go b/internal/installer/files/config_yaml.go index 55a3fd57..599489d2 100644 --- a/internal/installer/files/config_yaml.go +++ b/internal/installer/files/config_yaml.go @@ -401,10 +401,11 @@ type FlavorConfig struct { } type TelemetryExport struct { - Endpoint string `yaml:"endpoint"` - RemoteExport bool `yaml:"remoteExport"` - Traces bool `yaml:"traces"` - SpanMetrics bool `yaml:"spanMetrics"` + RemoteEndpoint string `yaml:"endpoint"` + RemoteExport bool `yaml:"remoteExport"` + Traces bool `yaml:"traces"` + TraceEndpoint string `yaml:"traceEndpoint,omitempty"` + SpanMetrics bool `yaml:"spanMetrics"` } type ChartOverride = map[string]interface{} From 03ccd0c96b3a3a3d0448ffc3e202c8c06ab8bb4b Mon Sep 17 00:00:00 2001 From: Mohamed Bilel Besrour Date: Tue, 26 May 2026 09:56:08 +0200 Subject: [PATCH 09/10] fix --- internal/installer/files/config_yaml.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/installer/files/config_yaml.go b/internal/installer/files/config_yaml.go index 599489d2..d1cae982 100644 --- a/internal/installer/files/config_yaml.go +++ b/internal/installer/files/config_yaml.go @@ -401,7 +401,7 @@ type FlavorConfig struct { } type TelemetryExport struct { - RemoteEndpoint string `yaml:"endpoint"` + RemoteEndpoint string `yaml:"remoteEndpoint"` RemoteExport bool `yaml:"remoteExport"` Traces bool `yaml:"traces"` TraceEndpoint string `yaml:"traceEndpoint,omitempty"` From 2a884a0724b24897fc5885e98d26d8bd70f52594 Mon Sep 17 00:00:00 2001 From: BBesrour <58034472+BBesrour@users.noreply.github.com> Date: Tue, 26 May 2026 07:59:10 +0000 Subject: [PATCH 10/10] chore(docs): Auto-update docs and licenses Signed-off-by: BBesrour <58034472+BBesrour@users.noreply.github.com> --- docs/oms_beta_bootstrap-gcp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/oms_beta_bootstrap-gcp.md b/docs/oms_beta_bootstrap-gcp.md index 67505415..52f4e3e3 100644 --- a/docs/oms_beta_bootstrap-gcp.md +++ b/docs/oms_beta_bootstrap-gcp.md @@ -53,7 +53,7 @@ oms beta bootstrap-gcp [flags] --install-local string Install Codesphere from local package (default: none) -s, --install-skip-steps stringArray Installation steps to skip during Codesphere installation (optional) --install-version string Codesphere version to install (default: none) - --local-trace-export Enable local trace export (default: false) + --local-trace-endpoint string Endpoint for exporting traces to an in-cluster storage (optional) --oidc-client-id string OIDC OAuth provider Client ID (optional) --oidc-client-secret string OIDC OAuth provider Client Secret (optional) --oidc-issuer-url string OIDC OAuth provider issuer URL (optional)