Skip to content
Open
Show file tree
Hide file tree
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
17 changes: 12 additions & 5 deletions cli/cmd/bootstrap_gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ func AddBootstrapGcpCmd(parent *cobra.Command, opts *util.GlobalOptions) {
flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.InstallHash, "install-hash", "", "Codesphere package hash to install (default: none)")
flags.StringArrayVarP(&bootstrapGcpCmd.CodesphereEnv.InstallSkipSteps, "install-skip-steps", "s", []string{}, "Installation steps to skip during Codesphere installation (optional)")
flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.RemoteOmsBinaryPath, "remote-oms-binary", "", "Path to a local Linux amd64 OMS binary to copy to and use on the jumpbox instead of downloading a release (optional)")
flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.RegistryUser, "registry-user", "", "Custom Registry username (only for GitHub registry type) (optional)")
flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.RegistryUsername, "registry-user", "", "Username for direct registry access")
flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.RegistryPassword, "registry-password", "", "Password or token for direct access to an alternative registry")
flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.ContainerRegistryURL, "registry", "", "Alternative container registry used for Codesphere images and charts")
flags.StringVar(&bootstrapGcpCmd.InputRegistryType, "registry-type", "local-container", "Container registry type to use (options: local-container, artifact-registry) (default: local-container)")
flags.StringArrayVar(&bootstrapGcpCmd.CodesphereEnv.InternalFlags, "internal-flags", gcp.DefaultInternalFlags, "Internal flags to enable in Codesphere installation (optional)")
flags.StringArrayVar(&bootstrapGcpCmd.experiments, "experiments", []string{}, "Deprecated: use --internal-flags instead. Values are added to the internal flags.")
Expand Down Expand Up @@ -196,9 +198,14 @@ func (c *BootstrapGcpCmd) BootstrapGcp() error {
c.CodesphereEnv.RegistryType = gcp.RegistryType(c.InputRegistryType)

c.CodesphereEnv.OmsWorkdir = c.Env.GetOmsWorkdir()
if c.CodesphereEnv.GitHubPAT != "" {
if c.CodesphereEnv.ContainerRegistryURL != "" {
c.CodesphereEnv.RegistryType = gcp.RegistryTypeExternal
if c.CodesphereEnv.RegistryUsername == "" || c.CodesphereEnv.RegistryPassword == "" {
return fmt.Errorf("registry-user and registry-password must be set when using an alternative registry")
}
} else if c.CodesphereEnv.GitHubPAT != "" {
c.CodesphereEnv.RegistryType = gcp.RegistryTypeGitHub
if c.CodesphereEnv.RegistryUser == "" {
if c.CodesphereEnv.RegistryUsername == "" {
return fmt.Errorf("registry-user must be set when using GitHub registry type")
}
}
Expand Down Expand Up @@ -238,8 +245,8 @@ func (c *BootstrapGcpCmd) BootstrapGcp() error {
packageName := "<package-name>-installer"
installCmd := "oms install codesphere -c /etc/codesphere/config.yaml -k /etc/codesphere/secrets/age_key.txt --vault /etc/codesphere/secrets/prod.vault.yaml"

if gcp.RegistryType(bs.Env.RegistryType) == gcp.RegistryTypeGitHub {
log.Printf("You set a GitHub PAT for direct image access. Make sure to use a lite package, as VM root disk sizes are reduced.")
if gcp.RegistryType(bs.Env.RegistryType) == gcp.RegistryTypeGitHub || gcp.RegistryType(bs.Env.RegistryType) == gcp.RegistryTypeExternal {
log.Printf("You configured direct registry access. Make sure to use a lite package, as VM root disk sizes are reduced.")

installCmd += " -s load-container-images"
packageName += "-lite"
Expand Down
3 changes: 1 addition & 2 deletions cli/cmd/bootstrap_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func AddBootstrapLocalCmd(parent *cobra.Command) {
flags.StringVar(&bootstrapLocalCmd.CodesphereEnv.InstallLocal, "install-local", "", "Path to a local installer package (tar.gz or unpacked directory)")
// Registry
flags.StringVar(&bootstrapLocalCmd.CodesphereEnv.RegistryUser, "registry-user", "", "Custom Registry username")
flags.StringVar(&bootstrapLocalCmd.CodesphereEnv.ContainerRegistryURL, "registry", "", "Alternative container registry used for Codesphere images and charts")

// Codesphere Environment
flags.StringVar(&bootstrapLocalCmd.CodesphereEnv.BaseDomain, "base-domain", "cs.local", "Base domain for Codesphere")
Expand All @@ -97,8 +98,6 @@ func AddBootstrapLocalCmd(parent *cobra.Command) {
flags.StringVar(&bootstrapLocalCmd.CodesphereEnv.SecretsFilePath, "secrets-file", "", "Path to secrets file (default: <install-dir>/prod.vault.yaml)")
flags.StringVar(&bootstrapLocalCmd.CodesphereEnv.CephDeviceFilter, "ceph-device-filter", "", "Regular expression selecting Ceph block devices by name")
flags.StringVar(&bootstrapLocalCmd.CodesphereEnv.CephDevicePathFilter, "ceph-device-path-filter", "", "Regular expression selecting Ceph block devices by path")
// ArgoCD integration
flags.StringVar(&bootstrapLocalCmd.CodesphereEnv.ArgoCDRegistryURL, "registry-url", "oci://ghcr.io/codesphere-cloud/charts", "OCI registry URL used for the ArgoCD helm pull secret")
bootstrapLocalCmd.cmd.RunE = bootstrapLocalCmd.RunE

util.MarkFlagRequired(bootstrapLocalCmd.cmd, "registry-user")
Expand Down
16 changes: 14 additions & 2 deletions cli/cmd/codesphere/install_codesphere_dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"os"
"runtime"
"strings"

argov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/codesphere-cloud/cs-go/pkg/io"
Expand Down Expand Up @@ -118,6 +119,16 @@ func installArgoCDAndApps(opts *InstallCodesphereOpts, cfg files.RootConfig, pm
return fmt.Errorf("failed to parse installer BOM: %w", err)
}

configuredRegistryURL := ""
if cfg.Registry != nil {
configuredRegistryURL = strings.TrimSuffix(strings.TrimPrefix(cfg.Registry.Server, "oci://"), "/")
if configuredRegistryURL != "" && configuredRegistryURL != "ghcr.io" {
if err := bomConfig.UseRegistry(configuredRegistryURL); err != nil {
return fmt.Errorf("failed to configure installer BOM registry: %w", err)
}
}
}

var install *argocdinstaller.AppInstaller

if err := stlog.Substep("Load vault data", func() error {
Expand All @@ -133,8 +144,8 @@ func installArgoCDAndApps(opts *InstallCodesphereOpts, cfg files.RootConfig, pm
return fmt.Errorf("registry password not found in vault (secret %q)", files.SecretRegistryPassword)
}
registryURL := opts.ArgoCDRegistryURL
if registryURL == "" && cfg.Registry != nil {
registryURL = cfg.Registry.Server + "/codesphere-cloud/charts"
if registryURL == "" && configuredRegistryURL != "" {
registryURL = configuredRegistryURL + "/codesphere-cloud/charts"
}
argoCDInstall, err := argocdinstaller.NewInstaller(argocdinstaller.InstallerConfig{
Version: opts.ArgoCDVersion,
Expand All @@ -145,6 +156,7 @@ func installArgoCDAndApps(opts *InstallCodesphereOpts, cfg files.RootConfig, pm
FullInstall: true,
ForceConflicts: opts.ArgoCDForceConflicts,
RepoURL: opts.ArgoCDRepoURL,
BOM: bomConfig,
ValueFiles: opts.ArgoCDValues,
RESTConfig: restConfig,
})
Expand Down
4 changes: 3 additions & 1 deletion docs/oms_beta_bootstrap-gcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ oms beta bootstrap-gcp [flags]
--prometheus-remote-write-user string Prometheus remote write username (optional)
--recover-config Recover previously generated install config from the jumpbox. This will overwrite the local config! (default: false)
--region string GCP Region (default: europe-west4) (default "europe-west4")
--registry string Alternative container registry used for Codesphere images and charts
--registry-password string Password or token for direct access to an alternative registry
--registry-type string Container registry type to use (options: local-container, artifact-registry) (default: local-container) (default "local-container")
--registry-user string Custom Registry username (only for GitHub registry type) (optional)
--registry-user string Username for direct registry access
--remote-oms-binary string Path to a local Linux amd64 OMS binary to copy to and use on the jumpbox instead of downloading a release (optional)
--root-disk-size int Instance root disk size in GB (default: 50) (default 50)
--secrets-dir string Directory for secrets (default: /etc/codesphere/secrets) (default "/etc/codesphere/secrets")
Expand Down
2 changes: 1 addition & 1 deletion docs/oms_beta_bootstrap-local.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ oms beta bootstrap-local [flags]
--pod-cidr string Service CIDR of the Kubernetes cluster. If not specified, OMS will try to determine it.
--preview-flags stringArray Preview flags to enable in Codesphere installation (optional) (default [openfga-authz,cluster-admin,secret-management,sub-path-mount,workspace-ssh,virtual-machines])
--profile string Profile to apply to the install config like resources (supported: dev, minimal, prod) (default "dev")
--registry-url string OCI registry URL used for the ArgoCD helm pull secret (default "oci://ghcr.io/codesphere-cloud/charts")
--registry string Alternative container registry used for Codesphere images and charts
--registry-user string Custom Registry username
--secrets-file string Path to secrets file (default: <install-dir>/prod.vault.yaml)
--service-cidr string Service CIDR of the Kubernetes cluster. If not specified, OMS will try to determine it.
Expand Down
48 changes: 36 additions & 12 deletions internal/bootstrap/gcp/gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ const (
// bootstrapping only configures GitHub access and installs the lite
// package.
RegistryTypeGitHub RegistryType = "github"

// RegistryTypeExternal pulls images directly from a user-provided registry
// with explicit credentials and installs the lite package.
RegistryTypeExternal RegistryType = "external"
)

// CheckOMSManagedLabel checks if the given labels map indicates an OMS-managed project.
Expand Down Expand Up @@ -161,7 +165,6 @@ type CodesphereEnvironment struct {
GitHubAppName string `json:"-"`
GitHubTeamOrg string `json:"github_team_org"`
GitHubTeamSlug string `json:"github_team_slug"`
RegistryUser string `json:"-"`
InternalFlags []string `json:"internal"`
PreviewFlags []string `json:"preview"`
FeatureFlags []string `json:"feature_flags"`
Expand Down Expand Up @@ -355,8 +358,8 @@ func (b *GCPBootstrapper) Bootstrap() error {
}
}

if b.Env.RegistryType == RegistryTypeGitHub {
err = b.stlog.Step("Ensure GitHub access configured", b.EnsureGitHubAccessConfigured)
if b.Env.RegistryType == RegistryTypeGitHub || b.Env.RegistryType == RegistryTypeExternal {
err = b.stlog.Step("Ensure registry access configured", b.EnsureRegistryAccessConfigured)
if err != nil {
return fmt.Errorf("failed to update install config: %w", err)
}
Expand Down Expand Up @@ -551,7 +554,7 @@ func (b *GCPBootstrapper) validateInstallVersion() error {
}

requiredFilename := "installer.tar.gz"
if b.Env.RegistryType == RegistryTypeGitHub {
if b.Env.RegistryType == RegistryTypeGitHub || b.Env.RegistryType == RegistryTypeExternal {
requiredFilename = "installer-lite.tar.gz"
}

Expand Down Expand Up @@ -1068,14 +1071,35 @@ func (b *GCPBootstrapper) EnsureLocalContainerRegistry() error {
return nil
}

func (b *GCPBootstrapper) EnsureGitHubAccessConfigured() error {
if b.Env.GitHubPAT == "" {
return fmt.Errorf("GitHub PAT is not set")
// EnsureRegistryAccessConfigured stores credentials and configures direct access
// to either GitHub Container Registry or an explicitly selected external registry.
func (b *GCPBootstrapper) EnsureRegistryAccessConfigured() error {
registryPassword := b.Env.RegistryPassword
if b.Env.RegistryType == RegistryTypeGitHub {
if b.Env.GitHubPAT == "" {
return fmt.Errorf("GitHub PAT is not set")
}

registryPassword = b.Env.GitHubPAT
} else {
registryURL := strings.TrimSuffix(strings.TrimPrefix(b.Env.ContainerRegistryURL, "oci://"), "/")
if registryURL == "" {
return fmt.Errorf("external registry URL is not set")
}

b.Env.InstallConfig.Registry.Server = registryURL
}

b.Env.InstallConfig.Registry.Server = "ghcr.io"
b.icg.GetVault().SetSecret(files.SecretEntry{Name: files.SecretRegistryUsername, Fields: &files.SecretFields{Password: b.Env.RegistryUser}})
b.icg.GetVault().SetSecret(files.SecretEntry{Name: files.SecretRegistryPassword, Fields: &files.SecretFields{Password: b.Env.GitHubPAT}})
if b.Env.RegistryUsername == "" {
return fmt.Errorf("registry username is not set")
}

if registryPassword == "" {
return fmt.Errorf("registry password is not set")
}

b.icg.GetVault().SetSecret(files.SecretEntry{Name: files.SecretRegistryUsername, Fields: &files.SecretFields{Password: b.Env.RegistryUsername}})
b.icg.GetVault().SetSecret(files.SecretEntry{Name: files.SecretRegistryPassword, Fields: &files.SecretFields{Password: registryPassword}})
b.Env.InstallConfig.Registry.ReplaceImagesInBom = false
b.Env.InstallConfig.Registry.LoadContainerImages = false

Expand Down Expand Up @@ -1158,7 +1182,7 @@ func (b *GCPBootstrapper) codespherePackageFilename() string {
}

func (b *GCPBootstrapper) codespherePackageArchiveName() string {
if b.Env.RegistryType == RegistryTypeGitHub {
if b.Env.RegistryType == RegistryTypeGitHub || b.Env.RegistryType == RegistryTypeExternal {
return "installer-lite.tar.gz"
}

Expand Down Expand Up @@ -1211,7 +1235,7 @@ func (b *GCPBootstrapper) generateSkipStepsArg() string {
skipSteps := []string{"kubernetes"}
skipSteps = util.AppendUnique(skipSteps, b.Env.InstallSkipSteps...)

if b.Env.RegistryType == RegistryTypeGitHub {
if b.Env.RegistryType == RegistryTypeGitHub || b.Env.RegistryType == RegistryTypeExternal {
skipSteps = util.AppendUnique(skipSteps, "load-container-images")
}

Expand Down
55 changes: 48 additions & 7 deletions internal/bootstrap/gcp/gcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -977,34 +977,61 @@ var _ = Describe("GCP Bootstrapper", func() {
})
})

Describe("EnsureGitHubAccessConfigured", func() {
Describe("EnsureRegistryAccessConfigured", func() {
BeforeEach(func() {
csEnv.GitHubPAT = "fake-pat"
csEnv.RegistryUser = "custom-registry"
csEnv.RegistryUsername = "custom-registry"
csEnv.RegistryType = gcp.RegistryTypeGitHub
})
It("sets configuration options in installconfig", func() {
vault := &files.InstallVault{}
icg.EXPECT().GetVault().Return(vault)

err := bs.EnsureGitHubAccessConfigured()
err := bs.EnsureRegistryAccessConfigured()
Expect(err).NotTo(HaveOccurred())
Expect(bs.Env.InstallConfig.Registry.Server).To(Equal("ghcr.io"))
Expect(vault.GetSecret(files.SecretRegistryUsername).Fields.Password).To(Equal(csEnv.RegistryUser))
Expect(bs.Env.InstallConfig.Registry.Server).To(BeEmpty())
Expect(vault.GetSecret(files.SecretRegistryUsername).Fields.Password).To(Equal(csEnv.RegistryUsername))
Expect(vault.GetSecret(files.SecretRegistryPassword).Fields.Password).To(Equal(csEnv.GitHubPAT))
Expect(bs.Env.InstallConfig.Registry.LoadContainerImages).To(BeFalse())
Expect(bs.Env.InstallConfig.Registry.ReplaceImagesInBom).To(BeFalse())
})

It("uses explicit credentials for an external registry", func() {
csEnv.ContainerRegistryURL = "oci://registry.example.com/mirror/"
csEnv.RegistryType = gcp.RegistryTypeExternal
csEnv.RegistryPassword = "registry-password"
vault := &files.InstallVault{}
icg.EXPECT().GetVault().Return(vault)

Expect(bs.EnsureRegistryAccessConfigured()).To(Succeed())
Expect(bs.Env.InstallConfig.Registry.Server).To(Equal("registry.example.com/mirror"))
Expect(vault.GetSecret(files.SecretRegistryUsername).Fields.Password).To(Equal(csEnv.RegistryUsername))
Expect(vault.GetSecret(files.SecretRegistryPassword).Fields.Password).To(Equal(csEnv.RegistryPassword))
Expect(bs.Env.InstallConfig.Registry.LoadContainerImages).To(BeFalse())
Expect(bs.Env.InstallConfig.Registry.ReplaceImagesInBom).To(BeFalse())
})

Context("When GitHub PAT is missing", func() {
BeforeEach(func() {
csEnv.GitHubPAT = ""
})
It("returns an error", func() {
err := bs.EnsureGitHubAccessConfigured()
err := bs.EnsureRegistryAccessConfigured()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("GitHub PAT is not set"))
})
})

Context("When an external registry password is missing", func() {
BeforeEach(func() {
csEnv.ContainerRegistryURL = "registry.example.com"
csEnv.RegistryType = gcp.RegistryTypeExternal
})

It("returns an error", func() {
Expect(bs.EnsureRegistryAccessConfigured()).To(MatchError("registry password is not set"))
})
})
})

Describe("EnsureVPC", func() {
Expand Down Expand Up @@ -1381,7 +1408,7 @@ var _ = Describe("GCP Bootstrapper", func() {
Context("Direct GitHub access", func() {
BeforeEach(func() {
csEnv.GitHubPAT = "fake-pat"
csEnv.RegistryUser = "fake-user"
csEnv.RegistryUsername = "fake-user"
csEnv.RegistryType = "github"
})
It("downloads and installs lite package", func() {
Expand All @@ -1397,6 +1424,20 @@ var _ = Describe("GCP Bootstrapper", func() {
})
})

Context("External registry access", func() {
BeforeEach(func() {
csEnv.RegistryType = gcp.RegistryTypeExternal
})

It("downloads and installs the lite package", func() {
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root", "oms download package -f installer-lite.tar.gz -H abc1234567890 v1.2.3").Return(nil)
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root",
"oms install codesphere -c /etc/codesphere/config.yaml -k /etc/codesphere/secrets/age_key.txt --vault /etc/codesphere/secrets/prod.vault.yaml -p v1.2.3-abc1234567890-installer-lite.tar.gz -s kubernetes,load-container-images").Return(nil)

Expect(bs.InstallCodesphere()).To(Succeed())
})
})

Context("without explicit hash", func() {
BeforeEach(func() {
// Simulate that ValidateInput has populated the hash
Expand Down
2 changes: 1 addition & 1 deletion internal/bootstrap/gcp/install_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func (b *GCPBootstrapper) UpdateInstallConfig() error {
b.Env.InstallConfig.Datacenter.CountryCode = "DE"

b.Env.InstallConfig.Secrets.BaseDir = b.Env.SecretsDir
if b.Env.RegistryType != RegistryTypeGitHub {
if b.Env.RegistryType != RegistryTypeGitHub && b.Env.RegistryType != RegistryTypeExternal {
b.Env.InstallConfig.Registry.ReplaceImagesInBom = true
b.Env.InstallConfig.Registry.LoadContainerImages = true
}
Expand Down
Loading