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
6 changes: 3 additions & 3 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,9 @@ func NewCmdRoot(streams genericclioptions.IOStreams) *cobra.Command {

// Checks if the version check should be run
func shouldRunVersionCheck(skipVersionCheckFlag bool, commandName string) bool {

// If either are true, then the version check should NOT run, hence negation
return !(skipVersionCheckFlag || canCommandSkipVersionCheck(commandName))
return !skipVersionCheckFlag &&
!canCommandSkipVersionCheck(commandName) &&
!utils.IsManagedInstall()
}

func canCommandSkipVersionCheck(commandName string) bool {
Expand Down
11 changes: 11 additions & 0 deletions cmd/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ var upgradeCmd = &cobra.Command{
}

func upgrade(cmd *cobra.Command, args []string) error {
if utils.IsManagedInstall() {
instruction, err := utils.UpgradeInstruction()
if err != nil {
return err
}
fmt.Fprintf(cmd.ErrOrStderr(),
"osdctl was installed via %s; self-upgrade is disabled.\nPlease upgrade using: %s\n",
utils.InstallMethod, instruction)
return nil
Comment thread
gvnnn marked this conversation as resolved.
}

// rootName ensures that the upgrade will fail if we ever decide to rename osdctl
// between releases :-)
rootName := cmd.Root().Name()
Expand Down
54 changes: 54 additions & 0 deletions cmd/upgrade_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package cmd

import (
"bytes"
"strings"
"testing"

"github.com/openshift/osdctl/pkg/utils"
)

func TestUpgradeRefusesWhenManaged(t *testing.T) {
tests := []struct {
name string
installMethod string
wantSubstring string
wantErr bool
}{
{"copr", "copr", "dnf upgrade osdctl", false},
{"homebrew", "homebrew", "brew upgrade osdctl", false},
{"unknown", "unknown", "unknown install method", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
original := utils.InstallMethod
defer func() { utils.InstallMethod = original }()
utils.InstallMethod = tt.installMethod

var buf bytes.Buffer
upgradeCmd.SetErr(&buf)
defer upgradeCmd.SetErr(nil)

err := upgrade(upgradeCmd, nil)
if tt.wantErr {
if err == nil {
t.Fatal("expected error, got nil")
}
if !strings.Contains(err.Error(), tt.wantSubstring) {
t.Errorf("error should contain %q, got: %s", tt.wantSubstring, err.Error())
}
return
}
if err != nil {
t.Fatalf("expected nil error, got: %v", err)
}
output := buf.String()
if !strings.Contains(output, tt.installMethod) {
t.Errorf("output should mention %q, got: %s", tt.installMethod, output)
}
if !strings.Contains(output, tt.wantSubstring) {
t.Errorf("output should contain %q, got: %s", tt.wantSubstring, output)
}
})
}
}
14 changes: 8 additions & 6 deletions cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import (
// versionResponse is necessary for the JSON version response. It uses the three
// variables that get set during the build.
type versionResponse struct {
Commit string `json:"commit"`
Version string `json:"version"`
Latest string `json:"latest"`
Commit string `json:"commit"`
Version string `json:"version"`
Latest string `json:"latest"`
InstallMethod string `json:"install_method,omitempty"`
}

// versionCmd is the subcommand "osdctl version" for cobra.
Expand All @@ -41,9 +42,10 @@ func version(cmd *cobra.Command, args []string) error {

latest, _ := utils.GetLatestVersion() // let's ignore this error, just in case we have no internet access
ver, err := json.MarshalIndent(&versionResponse{
Commit: gitCommit,
Version: utils.Version,
Latest: strings.TrimPrefix(latest, "v"),
Commit: gitCommit,
Version: utils.Version,
Latest: strings.TrimPrefix(latest, "v"),
InstallMethod: utils.InstallMethod,
}, "", " ")
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion hack/copr.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Source: %{gosource}
%if %{without bootstrap}
%define gomodulesmode GO111MODULE=on
%build
export GO_LDFLAGS='-X "github.com/openshift/osdctl/pkg/utils.Version="@version@"'
export GO_LDFLAGS='-X "github.com/openshift/osdctl/pkg/utils.Version=@version@" -X "github.com/openshift/osdctl/pkg/utils.InstallMethod=copr"'
%gobuild -o %{gobuilddir}/bin/osdctl %{goipath}
%endif

Expand Down
2 changes: 1 addition & 1 deletion hack/osdctl.spec
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Source: %{gosource}
%if %{without bootstrap}
%define gomodulesmode GO111MODULE=on
%build
export GO_LDFLAGS='-X "github.com/openshift/osdctl/pkg/utils.Version="@version@"'
export GO_LDFLAGS='-X "github.com/openshift/osdctl/pkg/utils.Version=@version@" -X "github.com/openshift/osdctl/pkg/utils.InstallMethod=copr"'
%gobuild -o %{gobuilddir}/bin/osdctl %{goipath}
%endif

Expand Down
28 changes: 28 additions & 0 deletions pkg/utils/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package utils

import (
"encoding/json"
"fmt"
"io"
"net/http"
"time"
Expand All @@ -22,8 +23,35 @@ var (
// Will be set during build process via GoReleaser
// See also: https://pkg.go.dev/cmd/link
Version string

// InstallMethod is set at build time via -X ldflags when osdctl is
// built by a package manager. Empty string (default) means the binary
// was built from source or via GoReleaser (GitHub releases).
// Known values: "copr", "homebrew".
InstallMethod string
)

// IsManagedInstall reports whether osdctl was installed via a package
// manager (e.g. COPR/RPM, Homebrew) rather than from a GitHub release.
func IsManagedInstall() bool {
return InstallMethod != ""
}

// UpgradeInstruction returns a human-readable upgrade command for the
// current install method.
func UpgradeInstruction() (string, error) {
switch InstallMethod {
case "":
return "", nil
case "copr":
return "dnf upgrade osdctl", nil
case "homebrew":
return "brew upgrade osdctl", nil
default:
return "", fmt.Errorf("unknown install method: %q", InstallMethod)
}
}

// githubResponse is a necessary struct for the JSON unmarshalling that is happening
// in the getLatestVersion().
type gitHubResponse struct {
Expand Down
54 changes: 54 additions & 0 deletions pkg/utils/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package utils

import "testing"

func TestIsManagedInstall(t *testing.T) {
tests := []struct {
name string
installMethod string
want bool
}{
{"empty (GitHub release)", "", false},
{"copr", "copr", true},
{"homebrew", "homebrew", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
original := InstallMethod
defer func() { InstallMethod = original }()
InstallMethod = tt.installMethod
if got := IsManagedInstall(); got != tt.want {
t.Errorf("IsManagedInstall() = %v, want %v", got, tt.want)
}
})
}
}

func TestUpgradeInstruction(t *testing.T) {
tests := []struct {
name string
installMethod string
want string
wantErr bool
}{
{"copr", "copr", "dnf upgrade osdctl", false},
{"homebrew", "homebrew", "brew upgrade osdctl", false},
{"empty", "", "", false},
{"unknown", "unknown", "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
original := InstallMethod
defer func() { InstallMethod = original }()
InstallMethod = tt.installMethod
got, err := UpgradeInstruction()
if (err != nil) != tt.wantErr {
t.Errorf("UpgradeInstruction() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("UpgradeInstruction() = %q, want %q", got, tt.want)
}
})
}
}