diff --git a/docker-compose.yml b/docker-compose.yml index b1a78766..10931bb8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,7 +22,7 @@ services: <<: *base environment: LETS_CONFIG_DIR: .. - command: gotestsum --format testname -- ./... -coverprofile=coverage.out + entrypoint: gotestsum --format testname -- ./... -coverprofile=coverage.out test-bats: <<: *base diff --git a/lets.yaml b/lets.yaml index e1908d8f..80694abc 100644 --- a/lets.yaml +++ b/lets.yaml @@ -28,7 +28,9 @@ commands: git push --tags test-unit: - description: Run unit tests + description: | + Run unit tests + (-run TestCase to run one test) depends: [build-lets-image] cmd: - docker-compose diff --git a/upgrade/registry/registry.go b/upgrade/registry/registry.go index 3c81da42..73dd46e0 100644 --- a/upgrade/registry/registry.go +++ b/upgrade/registry/registry.go @@ -101,7 +101,7 @@ func (reg *GithubRegistry) DownloadReleaseBinary( } dstDir := fmt.Sprintf("%s.dir", dstPath) - // cleanup if something abd happens during download/extract/rename flow + // cleanup if something bad happens during download/extract/rename flow defer os.RemoveAll(dstDir) err = os.RemoveAll(dstDir) diff --git a/upgrade/upgrade.go b/upgrade/upgrade.go index 9b2a977a..309ba208 100644 --- a/upgrade/upgrade.go +++ b/upgrade/upgrade.go @@ -119,11 +119,31 @@ func replaceBinaries(downloadPath string, executablePath string, backupPath stri defer os.RemoveAll(downloadPath) defer os.RemoveAll(backupPath) - err := os.Rename(downloadPath, executablePath) + newBinary, err := os.Open(downloadPath) + defer newBinary.Close() + if err != nil { - // restore original file from backup - err := os.Rename(backupPath, executablePath) + return fmt.Errorf("failed to open new lets binary: %w", err) + } + currentBinary, err := os.OpenFile(executablePath, os.O_WRONLY, 0755) + defer currentBinary.Close() + + if err != nil { + return fmt.Errorf("failed to open current lets binary: %w", err) + } + + _, err = io.Copy(currentBinary, newBinary) + if err != nil { + backupBinary, err := os.Open(backupPath) + defer backupBinary.Close() + + if err != nil { + return fmt.Errorf("failed to open backup lets binary: %w", err) + } + + // restore original file from backup + _, err = io.Copy(currentBinary, backupBinary) return fmt.Errorf("failed to update lets binary: %w", err) } diff --git a/upgrade/upgrade_test.go b/upgrade/upgrade_test.go index 5147bc96..5f1eb12d 100644 --- a/upgrade/upgrade_test.go +++ b/upgrade/upgrade_test.go @@ -42,6 +42,28 @@ func (m MockRegistry) GetDownloadURL(repoURI string, packageName string, version return "" } +type BadRegistry struct { + latestVersion string +} + +func (b BadRegistry) GetLatestRelease() (string, error) { + return b.latestVersion, nil + +} + +func (b BadRegistry) DownloadReleaseBinary(packageName string, version string, dstPath string) error { + // do not create file at all + return nil +} + +func (b BadRegistry) GetPackageName(os string, arch string) (string, error) { + return "lets_test_package", nil +} + +func (b BadRegistry) GetDownloadURL(repoURI string, packageName string, version string) string { + return "" +} + func createTempBinary(content string) (*os.File, error) { binary, err := os.CreateTemp("", "lets.*.current") if err != nil { @@ -106,6 +128,33 @@ func TestSelfUpgrade(t *testing.T) { } }) + // TODO use https://github.com/spf13/afero#using-afero-for-testing + // TODO or use https://godocs.io/testing/fstest + // TODO or https://github.com/blang/vfs + // info https://stackoverflow.com/questions/16742331/how-to-mock-abstract-filesystem-in-go + //t.Run("should restore original binary if upgrade failed", func(t *testing.T) { + // currentVersion := "v0.0.1" + // latestVersion := "v0.0.2" + // + // upgrader, err := newMockUpgrader(&BadRegistry{latestVersion: latestVersion}, currentVersion) + // if err != nil { + // t.Errorf("failed to create upgrader: %s", err) + // } + // + // if !testVersion(upgrader.binaryPath, currentVersion) { + // t.Errorf("expected version %s", currentVersion) + // } + // + // err = upgrader.Upgrade() + // if err == nil { + // t.Errorf("expected some error if upgrade failed") + // } + // + // if !testVersion(upgrader.binaryPath, currentVersion) { + // t.Errorf("expected to preserv current binary version %s", currentVersion) + // } + //}) + t.Run("should not self-upgrade same version", func(t *testing.T) { currentVersion := "v0.0.1" latestVersion := "v0.0.1"