Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8b77910
chore: try bootstrapping 1.77.2 with oms
joka134 Apr 24, 2026
b5cf9da
chore: add more postgres ms details
joka134 Apr 24, 2026
da348ed
chore(docs): Auto-update docs and licenses
joka134 Apr 27, 2026
95135cc
fix: omitempty
joka134 Apr 27, 2026
4d2534c
update(deps): update module k8s.io/endpointslice to v0.36.0 (#344)
CodesphereBot Apr 23, 2026
e459f6e
update(deps): update module k8s.io/externaljwt to v0.36.0 (#345)
CodesphereBot Apr 24, 2026
3c403ec
update(deps): update module k8s.io/kms to v0.36.0 (#346)
CodesphereBot Apr 24, 2026
e6010f0
update(deps): update module github.com/google/go-github/v74 to v85 (#…
CodesphereBot Apr 24, 2026
83cc48a
update(deps): update github.com/rook/rook/pkg/apis digest to b87c434 …
CodesphereBot Apr 24, 2026
09fa6d3
update(deps): update module github.com/onsi/ginkgo/v2 to v2.28.2 (#350)
CodesphereBot Apr 27, 2026
db26737
feat: add command to create a test user for automated testing (#329)
joka134 Apr 27, 2026
8baf243
Merge remote-tracking branch 'origin/main' into HEAD
joka134 Apr 27, 2026
720b8c7
fix: enable critical components for pc
joka134 Apr 27, 2026
b5139e0
Merge branch 'main' into feat/bootstrap-support-lts-1.77.2
joka134 Apr 30, 2026
9d502dc
chore(docs): Auto-update docs and licenses
joka134 Apr 30, 2026
8897e02
chore(docs): Auto-update docs and licenses
joka134 Apr 30, 2026
81a5205
fix: clear ManagedServices in LTS 1.77.2 compat mode
OliverTrautvetter May 4, 2026
3993470
feat: add LTS 1.77.2 compatibility support and related tests
OliverTrautvetter May 6, 2026
f2be9be
Merge branch 'main' into feat/bootstrap-support-lts-1.77.2
OliverTrautvetter May 6, 2026
0e512c1
chore(docs): Auto-update docs and licenses
joka134 May 6, 2026
2e6ab35
fix: lint
OliverTrautvetter May 6, 2026
89ce4fe
feat: handle HTTP 429 rate limit response in PortalClient
OliverTrautvetter May 6, 2026
0afb316
feat: implement LTS 1.77.2 compatibility and add BuildOmsLinuxBinary …
OliverTrautvetter May 7, 2026
b19e7f3
feat: add support for LTS 1.77.2 compatibility and refactor related f…
OliverTrautvetter May 8, 2026
1317d9a
feat: enhance download functionality with retry logic for stale files…
OliverTrautvetter May 8, 2026
5cc3c45
fix: lint
OliverTrautvetter May 8, 2026
8142975
Merge branch 'main' into feat/bootstrap-support-lts-1.77.2
OliverTrautvetter May 8, 2026
a576ef9
fix: tests
OliverTrautvetter May 8, 2026
ee60530
feat: add SSHAddress and SSHPort fields to K8sNode for enhanced confi…
OliverTrautvetter May 11, 2026
5921182
test: update Dockerfile test to write content to temp file before reo…
OliverTrautvetter May 11, 2026
61ad470
fix: use os.Pipe() in update_dockerfile tests to avoid EBADF on Linux
OliverTrautvetter May 11, 2026
78626eb
Merge branch 'main' into feat/bootstrap-support-lts-1.77.2
OliverTrautvetter May 13, 2026
a13cb71
chore(docs): Auto-update docs and licenses
OliverTrautvetter May 13, 2026
0c34f8d
fix: improve error handling and retry logic in DownloadBuild method
OliverTrautvetter May 13, 2026
54a82cd
Merge remote-tracking branch 'origin/main' into feat/bootstrap-suppor…
OliverTrautvetter May 18, 2026
72be25e
fix: reset download package
OliverTrautvetter May 18, 2026
033e14e
fix: reset download package test
OliverTrautvetter May 18, 2026
6d1d543
ref: remove commented-out schema definitions in applyCommonProperties
OliverTrautvetter May 20, 2026
02b27bb
Merge branch 'main' into feat/bootstrap-support-lts-1.77.2
OliverTrautvetter May 20, 2026
3482aad
feat: enhance PostgreSQL and S3 managed services with new configurati…
OliverTrautvetter May 20, 2026
811f21f
feat: enhance LTS support with new config handling and Ceph master wa…
OliverTrautvetter May 20, 2026
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
6 changes: 5 additions & 1 deletion cli/cmd/init_install_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,11 @@ func (c *InitInstallConfigCmd) updateConfigFromOpts(config *files.RootConfig) *f
if len(c.Opts.CephHosts) > 0 {
cephHosts := []files.CephHost{}
for _, hostCfg := range c.Opts.CephHosts {
cephHosts = append(cephHosts, files.CephHost(hostCfg))
cephHosts = append(cephHosts, files.CephHost{
Hostname: hostCfg.Hostname,
IPAddress: hostCfg.IPAddress,
IsMaster: hostCfg.IsMaster,
})
}
config.Ceph.Hosts = cephHosts
}
Expand Down
60 changes: 20 additions & 40 deletions cli/cmd/update_dockerfile_test.go
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Did this cause some issues? Why changing it in this PR?

Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,12 @@ var _ = Describe("UpdateDockerfileCmd", func() {
mockImageManager := system.NewMockImageManager(GinkgoT())
mockFileIO := util.NewMockFileIO(GinkgoT())

// Create a temporary file for the Dockerfile
tempFile, err := os.CreateTemp("", "dockerfile-test-*")
pr, pw, err := os.Pipe()
Expect(err).To(BeNil())
DeferCleanup(func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
})
_, err = tempFile.WriteString(sampleDockerfileContent)
_, err = pw.WriteString(sampleDockerfileContent)
Expect(err).To(BeNil())
// Reset file position to beginning
_, _ = tempFile.Seek(0, 0)
Expect(pw.Close()).To(Succeed())
DeferCleanup(func() { _ = pr.Close() })

c.Opts.Dockerfile = "Dockerfile"
c.Opts.Baseimage = ""
Expand All @@ -160,7 +155,7 @@ var _ = Describe("UpdateDockerfileCmd", func() {
mockPackageManager.EXPECT().GetBaseimagePath("", false).Return("/test/workdir/deps/codesphere/images/workspace-agent-24.04.tar", nil)
mockImageManager.EXPECT().LoadImage("/test/workdir/deps/codesphere/images/workspace-agent-24.04.tar").Return(nil)
mockPackageManager.EXPECT().FileIO().Return(mockFileIO)
mockFileIO.EXPECT().Open("Dockerfile").Return(tempFile, nil)
mockFileIO.EXPECT().Open("Dockerfile").Return(pr, nil)
mockFileIO.EXPECT().WriteFile("Dockerfile", []byte("FROM ubuntu:24.04\nRUN apt-get update && apt-get install -y curl\nWORKDIR /app\nCOPY . .\nCMD [\"./start.sh\"]"), os.FileMode(0644)).Return(errors.New("write failed"))

err = c.UpdateDockerfile(mockPackageManager, mockImageManager, []string{})
Expand All @@ -173,17 +168,12 @@ var _ = Describe("UpdateDockerfileCmd", func() {
mockImageManager := system.NewMockImageManager(GinkgoT())
mockFileIO := util.NewMockFileIO(GinkgoT())

// Create a temporary file for the Dockerfile
tempFile, err := os.CreateTemp("", "dockerfile-test-*")
pr, pw, err := os.Pipe()
Expect(err).To(BeNil())
DeferCleanup(func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
})
_, err = tempFile.WriteString(sampleDockerfileContent)
_, err = pw.WriteString(sampleDockerfileContent)
Expect(err).To(BeNil())
// Reset file position to beginning
_, _ = tempFile.Seek(0, 0)
Expect(pw.Close()).To(Succeed())
DeferCleanup(func() { _ = pr.Close() })

c.Opts.Dockerfile = "Dockerfile"
c.Opts.Baseimage = ""
Expand All @@ -193,7 +183,7 @@ var _ = Describe("UpdateDockerfileCmd", func() {
mockPackageManager.EXPECT().GetFullImageTag("").Return("ubuntu:24.04", nil)
mockPackageManager.EXPECT().GetBaseimagePath("", false).Return("/test/workdir/deps/codesphere/images/workspace-agent-24.04.tar", nil)
mockPackageManager.EXPECT().FileIO().Return(mockFileIO)
mockFileIO.EXPECT().Open("Dockerfile").Return(tempFile, nil)
mockFileIO.EXPECT().Open("Dockerfile").Return(pr, nil)
mockFileIO.EXPECT().WriteFile("Dockerfile", []byte("FROM ubuntu:24.04\nRUN apt-get update && apt-get install -y curl\nWORKDIR /app\nCOPY . .\nCMD [\"./start.sh\"]"), os.FileMode(0644)).Return(nil)
mockImageManager.EXPECT().LoadImage("/test/workdir/deps/codesphere/images/workspace-agent-24.04.tar").Return(nil)

Expand All @@ -206,17 +196,12 @@ var _ = Describe("UpdateDockerfileCmd", func() {
mockImageManager := system.NewMockImageManager(GinkgoT())
mockFileIO := util.NewMockFileIO(GinkgoT())

// Create a temporary file for the Dockerfile
tempFile, err := os.CreateTemp("", "dockerfile-test-*")
pr, pw, err := os.Pipe()
Expect(err).To(BeNil())
DeferCleanup(func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
})
_, err = tempFile.WriteString(sampleDockerfileContent)
_, err = pw.WriteString(sampleDockerfileContent)
Expect(err).To(BeNil())
// Reset file position to beginning
_, _ = tempFile.Seek(0, 0)
Expect(pw.Close()).To(Succeed())
DeferCleanup(func() { _ = pr.Close() })

c.Opts.Dockerfile = "Dockerfile"
c.Opts.Baseimage = "workspace-agent-20.04.tar"
Expand All @@ -226,7 +211,7 @@ var _ = Describe("UpdateDockerfileCmd", func() {
mockPackageManager.EXPECT().GetFullImageTag("workspace-agent-20.04.tar").Return("ubuntu:20.04", nil)
mockPackageManager.EXPECT().GetBaseimagePath("workspace-agent-20.04.tar", true).Return("/test/workdir/deps/codesphere/images/workspace-agent-20.04.tar", nil)
mockPackageManager.EXPECT().FileIO().Return(mockFileIO)
mockFileIO.EXPECT().Open("Dockerfile").Return(tempFile, nil)
mockFileIO.EXPECT().Open("Dockerfile").Return(pr, nil)
mockFileIO.EXPECT().WriteFile("Dockerfile", []byte("FROM ubuntu:20.04\nRUN apt-get update && apt-get install -y curl\nWORKDIR /app\nCOPY . .\nCMD [\"./start.sh\"]"), os.FileMode(0644)).Return(nil)
mockImageManager.EXPECT().LoadImage("/test/workdir/deps/codesphere/images/workspace-agent-20.04.tar").Return(nil)

Expand All @@ -239,17 +224,12 @@ var _ = Describe("UpdateDockerfileCmd", func() {
mockImageManager := system.NewMockImageManager(GinkgoT())
mockFileIO := util.NewMockFileIO(GinkgoT())

// Create a temporary file for the Dockerfile
tempFile, err := os.CreateTemp("", "dockerfile-test-*")
pr, pw, err := os.Pipe()
Expect(err).To(BeNil())
DeferCleanup(func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
})
_, err = tempFile.WriteString(sampleDockerfileContent)
_, err = pw.WriteString(sampleDockerfileContent)
Expect(err).To(BeNil())
// Reset file position to beginning
_, _ = tempFile.Seek(0, 0)
Expect(pw.Close()).To(Succeed())
DeferCleanup(func() { _ = pr.Close() })

c.Opts.Dockerfile = "custom/Dockerfile"
c.Opts.Baseimage = "workspace-agent-24.04.tar"
Expand All @@ -259,7 +239,7 @@ var _ = Describe("UpdateDockerfileCmd", func() {
mockPackageManager.EXPECT().GetFullImageTag("workspace-agent-24.04.tar").Return("registry.example.com/workspace-agent:24.04", nil)
mockPackageManager.EXPECT().GetBaseimagePath("workspace-agent-24.04.tar", false).Return("/test/workdir/deps/codesphere/images/workspace-agent-24.04.tar", nil)
mockPackageManager.EXPECT().FileIO().Return(mockFileIO)
mockFileIO.EXPECT().Open("custom/Dockerfile").Return(tempFile, nil)
mockFileIO.EXPECT().Open("custom/Dockerfile").Return(pr, nil)
mockFileIO.EXPECT().WriteFile("custom/Dockerfile", []byte("FROM registry.example.com/workspace-agent:24.04\nRUN apt-get update && apt-get install -y curl\nWORKDIR /app\nCOPY . .\nCMD [\"./start.sh\"]"), os.FileMode(0644)).Return(nil)
mockImageManager.EXPECT().LoadImage("/test/workdir/deps/codesphere/images/workspace-agent-24.04.tar").Return(nil)

Expand Down
2 changes: 1 addition & 1 deletion docs/oms_beta_bootstrap-gcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ oms beta bootstrap-gcp [flags]
--datacenter-name string Datacenter name (default: dev) (default "dev")
--dns-project-id string GCP Project ID for Cloud DNS (optional)
--dns-zone-name string Cloud DNS Zone Name (optional) (default "oms-testing")
--experiments stringArray Experiments to enable in Codesphere installation (optional) (default [managed-services,headless-services,vcluster,custom-service-image,ms-in-ls,secret-management,sub-path-mount])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why disable them by default? Aren't they required for new versions?

--experiments stringArray Experiments to enable in Codesphere installation (optional) (default [managed-services,custom-service-image,ms-in-ls,secret-management,sub-path-mount])
--external-loki-endpoint string External Loki endpoint for Grafana Alloy log forwarding (optional)
--external-loki-secret string External Loki password stored in the generated vault (optional)
--external-loki-user string External Loki username for Grafana Alloy log forwarding (optional)
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 @@ -17,7 +17,7 @@ oms beta bootstrap-local [flags]

```
--base-domain string Base domain for Codesphere (default "cs.local")
--experiments stringArray Experiments to enable in Codesphere installation (optional) (default [managed-services,headless-services,vcluster,custom-service-image,ms-in-ls,secret-management,sub-path-mount])
--experiments stringArray Experiments to enable in Codesphere installation (optional) (default [managed-services,custom-service-image,ms-in-ls,secret-management,sub-path-mount])
--feature-flags stringArray Feature flags to enable in Codesphere installation (optional)
-h, --help help for bootstrap-local
--install-config string Path to install config file (default: <install-dir>/config.yaml)
Expand Down
103 changes: 87 additions & 16 deletions internal/bootstrap/gcp/gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ func GetDNSRecordNames(baseDomain string) []struct {

var DefaultExperiments []string = []string{
"managed-services",
"headless-services",
"vcluster",
"custom-service-image",
"ms-in-ls",
"secret-management",
Expand All @@ -80,9 +78,10 @@ type GCPBootstrapper struct {
// Environment
Env *CodesphereEnvironment
// SSH command runner
NodeClient node.NodeClient
PortalClient portal.Portal
GitHubClient github.GitHubClient
NodeClient node.NodeClient
PortalClient portal.Portal
GitHubClient github.GitHubClient
OmsBinaryBuilder func() (string, func(), error)
}

type CodesphereEnvironment struct {
Expand Down Expand Up @@ -182,16 +181,17 @@ func NewGCPBootstrapper(
gitHubClient github.GitHubClient,
) (*GCPBootstrapper, error) {
return &GCPBootstrapper{
ctx: ctx,
stlog: stlog,
fw: fw,
icg: icg,
GCPClient: gcpClient,
Env: CodesphereEnv,
NodeClient: sshRunner,
PortalClient: portalClient,
Time: time,
GitHubClient: gitHubClient,
ctx: ctx,
stlog: stlog,
fw: fw,
icg: icg,
GCPClient: gcpClient,
Env: CodesphereEnv,
NodeClient: sshRunner,
PortalClient: portalClient,
Time: time,
GitHubClient: gitHubClient,
OmsBinaryBuilder: BuildOmsLinuxBinary,
}, nil
}

Expand Down Expand Up @@ -922,6 +922,16 @@ func (b *GCPBootstrapper) InstallCodesphere() error {
return fmt.Errorf("failed to ensure Codesphere package on jumpbox: %w", err)
}

if ltsSpec := FindLTSSpec(b.Env.InstallVersion); ltsSpec != nil {
if ltsSpec.RequiresOmsBinaryUpdate {
if err := b.ensureNewOmsBinaryOnJumpbox(); err != nil {
return fmt.Errorf("failed to update OMS binary on jumpbox for %s: %w", b.Env.InstallVersion, err)
}
}
b.startLTSCephMasterWatcher()
defer b.stopLTSCephMasterWatcher()
}

err = b.runInstallCommand(fullPackageFilename)
if err != nil {
return fmt.Errorf("failed to install Codesphere from jumpbox: %w", err)
Expand All @@ -930,6 +940,61 @@ func (b *GCPBootstrapper) InstallCodesphere() error {
return nil
}

// ensureNewOmsBinaryOnJumpbox copies a freshly-built linux/amd64 OMS binary to
// the jumpbox, replacing the old installed version.
func (b *GCPBootstrapper) ensureNewOmsBinaryOnJumpbox() error {
b.stlog.Logf("Updating OMS binary on jumpbox for %s compatibility...", b.Env.InstallVersion)

binaryPath, cleanup, err := b.OmsBinaryBuilder()
if err != nil {
return fmt.Errorf("failed to prepare OMS linux binary: %w", err)
}
defer cleanup()

const remoteTmpPath = "/tmp/oms-new"
if err := b.Env.Jumpbox.NodeClient.CopyFile(b.Env.Jumpbox, binaryPath, remoteTmpPath); err != nil {
return fmt.Errorf("failed to copy OMS binary to jumpbox: %w", err)
}

if err := b.Env.Jumpbox.RunSSHCommand("root", fmt.Sprintf("chmod +x %s && mv %s /usr/local/bin/oms", remoteTmpPath, remoteTmpPath)); err != nil {
return fmt.Errorf("failed to install OMS binary on jumpbox: %w", err)
}

return nil
}

// startLTSCephMasterWatcher starts a background process on the ceph master node that continuously
// re-adds the master to the Ceph orchestrator host inventory. This is required for LTS versions
// because the installer's configureHosts step applies a declarative host spec containing only the
// non-master nodes, which removes the master from the inventory. The watcher restores it within
// seconds, before the subsequent configureMonitors step runs.
func (b *GCPBootstrapper) startLTSCephMasterWatcher() {
if len(b.Env.CephNodes) == 0 || len(b.Env.InstallConfig.Ceph.Hosts) == 0 {
return
}
masterHost := b.Env.InstallConfig.Ceph.Hosts[0]
// Use cephadm shell (same as the installer) so the command runs inside the ceph container,
// bypassing any standalone-binary or keyring availability issues on the host.
// The FSID is auto-detected from /var/lib/ceph/; all output is logged for diagnostics.
cmd := fmt.Sprintf(
`nohup bash -c "while true; do FSID=\$(ls /var/lib/ceph/ 2>/dev/null | head -1); [ -n \"\$FSID\" ] && [ -x /usr/local/bin/cephadm ] && /usr/local/bin/cephadm shell --fsid \"\$FSID\" -- ceph orch host add %s %s 2>&1; sleep 3; done" > /tmp/ceph-host-watcher.log 2>&1 & echo $! > /tmp/ceph-host-watcher.pid`,
masterHost.Hostname,
masterHost.IPAddress,
)
if err := b.Env.CephNodes[0].RunSSHCommand("root", cmd); err != nil {
b.stlog.Logf("Note: could not start ceph master host watcher on %s: %v", masterHost.Hostname, err)
}
}

// stopLTSCephMasterWatcher stops the background watcher started by startLTSCephMasterWatcher.
func (b *GCPBootstrapper) stopLTSCephMasterWatcher() {
if len(b.Env.CephNodes) == 0 || len(b.Env.InstallConfig.Ceph.Hosts) == 0 {
return
}
cmd := `kill $(cat /tmp/ceph-host-watcher.pid 2>/dev/null) 2>/dev/null; rm -f /tmp/ceph-host-watcher.pid /tmp/ceph-host-watcher.log`
_ = b.Env.CephNodes[0].RunSSHCommand("root", cmd)
}

func (b *GCPBootstrapper) ensureCodespherePackageOnJumpbox() (string, error) {
packageFilename := "installer.tar.gz"
if b.Env.RegistryType == RegistryTypeGitHub {
Expand Down Expand Up @@ -959,7 +1024,13 @@ func (b *GCPBootstrapper) ensureCodespherePackageOnJumpbox() (string, error) {
packageFilename, b.Env.InstallHash, b.Env.InstallVersion)
err := b.Env.Jumpbox.RunSSHCommand("root", downloadCmd)
if err != nil {
return "", fmt.Errorf("failed to download Codesphere package from jumpbox: %w", err)
// A stale partial file from a previous (different) build can cause MD5 verification to
// fail even after a successful byte-range resume. Delete it and retry from scratch.
b.stlog.Logf("Download failed; removing any stale partial file and retrying from scratch...")
cleanAndRetryCmd := fmt.Sprintf("rm -f %s && %s", fullPackageFilename, downloadCmd)
if retryErr := b.Env.Jumpbox.RunSSHCommand("root", cleanAndRetryCmd); retryErr != nil {
return "", fmt.Errorf("failed to download Codesphere package from jumpbox: %w", retryErr)
}
Comment on lines +1027 to +1033
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

after the discussion today: this should simply fail

}

return fullPackageFilename, nil
Expand Down
68 changes: 66 additions & 2 deletions internal/bootstrap/gcp/gcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ var _ = Describe("GCP Bootstrapper", func() {
Expect(cpNode.GetInternalIP()).To(Equal("10.0.0.1"))
}

Expect(len(bs.Env.InstallConfig.Codesphere.ManagedServices)).To(Equal(4))
Expect(len(bs.Env.InstallConfig.Codesphere.ManagedServices)).To(Equal(5))
})
})

Expand Down Expand Up @@ -1227,6 +1227,55 @@ var _ = Describe("GCP Bootstrapper", func() {
Expect(err).NotTo(HaveOccurred())
})

Context("LTS 1.77.2", func() {
BeforeEach(func() {
csEnv.InstallVersion = "codesphere-lts-v1.77.2"
})
JustBeforeEach(func() {
// Inject a stub binary builder so tests don't invoke `go build`.
bs.OmsBinaryBuilder = func() (string, func(), error) {
f, err := os.CreateTemp("", "oms-test-binary-*")
Expect(err).NotTo(HaveOccurred())
Expect(f.Close()).To(Succeed())
return f.Name(), func() { Expect(os.Remove(f.Name())).To(Succeed()) }, nil
}
})
It("downloads package, updates OMS binary, and installs codesphere", func() {
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root",
"oms download package -f installer.tar.gz -H abc1234567890 codesphere-lts-v1.77.2").Return(nil)
nodeClient.EXPECT().CopyFile(mock.MatchedBy(jumpboxMatcher), mock.Anything, "/tmp/oms-new").Return(nil)
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root",
"chmod +x /tmp/oms-new && mv /tmp/oms-new /usr/local/bin/oms").Return(nil)
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root",
"oms install codesphere -c /etc/codesphere/config.yaml -k /etc/codesphere/secrets/age_key.txt -p codesphere-lts-v1.77.2-abc1234567890-installer.tar.gz").Return(nil)

err := bs.InstallCodesphere()
Expect(err).NotTo(HaveOccurred())
})

It("fails when OmsBinaryBuilder fails", func() {
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root",
"oms download package -f installer.tar.gz -H abc1234567890 codesphere-lts-v1.77.2").Return(nil)
bs.OmsBinaryBuilder = func() (string, func(), error) {
return "", func() {}, fmt.Errorf("build failed")
}

err := bs.InstallCodesphere()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed to update OMS binary on jumpbox for codesphere-lts-v1.77.2"))
})

It("fails when copying binary to jumpbox fails", func() {
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root",
"oms download package -f installer.tar.gz -H abc1234567890 codesphere-lts-v1.77.2").Return(nil)
nodeClient.EXPECT().CopyFile(mock.MatchedBy(jumpboxMatcher), mock.Anything, "/tmp/oms-new").Return(fmt.Errorf("copy failed"))

err := bs.InstallCodesphere()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed to update OMS binary on jumpbox for codesphere-lts-v1.77.2"))
})
})

Context("with local package", func() {
BeforeEach(func() {
csEnv.InstallLocal = "fake-installer-lite.tar.gz"
Expand Down Expand Up @@ -1288,8 +1337,23 @@ var _ = Describe("GCP Bootstrapper", func() {
})
})

It("retries with a clean slate when download fails, succeeds on retry", func() {
downloadCmd := "oms download package -f installer.tar.gz -H abc1234567890 v1.2.3"
cleanAndRetryCmd := "rm -f v1.2.3-abc1234567890-installer.tar.gz && " + downloadCmd
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root", downloadCmd).Return(fmt.Errorf("md5 mismatch")).Once()
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root", cleanAndRetryCmd).Return(nil).Once()
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root",
"oms install codesphere -c /etc/codesphere/config.yaml -k /etc/codesphere/secrets/age_key.txt -p v1.2.3-abc1234567890-installer.tar.gz").Return(nil).Once()

err := bs.InstallCodesphere()
Expect(err).NotTo(HaveOccurred())
})

It("fails when download package fails", func() {
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root", "oms download package -f installer.tar.gz -H abc1234567890 v1.2.3").Return(fmt.Errorf("download error"))
downloadCmd := "oms download package -f installer.tar.gz -H abc1234567890 v1.2.3"
cleanAndRetryCmd := "rm -f v1.2.3-abc1234567890-installer.tar.gz && " + downloadCmd
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root", downloadCmd).Return(fmt.Errorf("download error")).Once()
nodeClient.EXPECT().RunCommand(mock.MatchedBy(jumpboxMatcher), "root", cleanAndRetryCmd).Return(fmt.Errorf("download error on retry")).Once()

err := bs.InstallCodesphere()
Expect(err).To(HaveOccurred())
Expand Down
Loading
Loading