mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-04-15 07:16:34 +00:00
does not auto update for package managers (#1850)
This commit is contained in:
@@ -53,7 +53,7 @@ that a cluster meets the requirements to run an application.`,
|
||||
_ = updater.CheckAndUpdate(cmd.Context(), updater.Options{
|
||||
BinaryName: "preflight",
|
||||
CurrentPath: exe,
|
||||
Printf: func(f string, a ...interface{}) { klog.V(1).Infof(f, a...) },
|
||||
Printf: func(f string, a ...interface{}) { fmt.Fprintf(os.Stderr, f, a...) },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ If no arguments are provided, specs are automatically loaded from the cluster by
|
||||
_ = updater.CheckAndUpdate(cmd.Context(), updater.Options{
|
||||
BinaryName: "support-bundle",
|
||||
CurrentPath: exe,
|
||||
Printf: func(f string, a ...interface{}) { klog.V(1).Infof(f, a...) },
|
||||
Printf: func(f string, a ...interface{}) { fmt.Fprintf(os.Stderr, f, a...) },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
74
pkg/updater/pkgmgr/homebrew.go
Normal file
74
pkg/updater/pkgmgr/homebrew.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package pkgmgr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// HomebrewPackageManager detects if a binary was installed via Homebrew
|
||||
type HomebrewPackageManager struct{
|
||||
formula string
|
||||
}
|
||||
|
||||
var _ PackageManager = (*HomebrewPackageManager)(nil)
|
||||
|
||||
type homebrewInfoOutput struct {
|
||||
Installed []struct {
|
||||
Version string `json:"version"`
|
||||
InstalledOn bool `json:"installed_on_request"`
|
||||
LinkedKeg string `json:"linked_keg"`
|
||||
} `json:"installed"`
|
||||
}
|
||||
|
||||
// NewHomebrewPackageManager creates a new Homebrew package manager detector
|
||||
func NewHomebrewPackageManager(formula string) PackageManager {
|
||||
return &HomebrewPackageManager{
|
||||
formula: formula,
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the human-readable name of the package manager
|
||||
func (h *HomebrewPackageManager) Name() string {
|
||||
return "Homebrew"
|
||||
}
|
||||
|
||||
// IsInstalled checks if the formula is installed via Homebrew
|
||||
func (h *HomebrewPackageManager) IsInstalled() (bool, error) {
|
||||
// First check if brew command exists
|
||||
brewPath, err := exec.LookPath("brew")
|
||||
if err != nil {
|
||||
// No brew command found, definitely not installed via brew
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Check if the formula is installed
|
||||
out, err := exec.Command(brewPath, "info", h.formula, "--json").Output()
|
||||
if err != nil {
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
if exitError.ExitCode() == 1 {
|
||||
// brew info with an invalid (not installed) package name returns an error
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
var info []homebrewInfoOutput
|
||||
if err := json.Unmarshal(out, &info); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if len(info) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Check if the formula has any installed versions
|
||||
return len(info[0].Installed) > 0, nil
|
||||
}
|
||||
|
||||
// UpgradeCommand returns the command to upgrade the package
|
||||
func (h *HomebrewPackageManager) UpgradeCommand() string {
|
||||
return fmt.Sprintf("brew upgrade %s", h.formula)
|
||||
}
|
||||
|
||||
69
pkg/updater/pkgmgr/krew.go
Normal file
69
pkg/updater/pkgmgr/krew.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package pkgmgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// KrewPackageManager detects if a binary was installed via kubectl krew
|
||||
type KrewPackageManager struct{
|
||||
pluginName string
|
||||
}
|
||||
|
||||
var _ PackageManager = (*KrewPackageManager)(nil)
|
||||
|
||||
// NewKrewPackageManager creates a new Krew package manager detector
|
||||
func NewKrewPackageManager(pluginName string) PackageManager {
|
||||
return &KrewPackageManager{
|
||||
pluginName: pluginName,
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the human-readable name of the package manager
|
||||
func (k *KrewPackageManager) Name() string {
|
||||
return "kubectl krew"
|
||||
}
|
||||
|
||||
// IsInstalled checks if the plugin is installed via krew
|
||||
func (k *KrewPackageManager) IsInstalled() (bool, error) {
|
||||
// First check if kubectl krew command exists
|
||||
_, err := exec.LookPath("kubectl")
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Check if krew plugin is available
|
||||
out, err := exec.Command("kubectl", "krew", "version").Output()
|
||||
if err != nil {
|
||||
// krew not installed
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if !strings.Contains(string(out), "krew") {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Check if the plugin is installed by listing installed plugins
|
||||
listOut, err := exec.Command("kubectl", "krew", "list").Output()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check if our plugin is in the installed list
|
||||
installedPlugins := strings.Split(string(listOut), "\n")
|
||||
for _, line := range installedPlugins {
|
||||
// Lines are in format: "PLUGIN VERSION"
|
||||
if strings.HasPrefix(strings.TrimSpace(line), k.pluginName+" ") || strings.TrimSpace(line) == k.pluginName {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// UpgradeCommand returns the command to upgrade the plugin
|
||||
func (k *KrewPackageManager) UpgradeCommand() string {
|
||||
return fmt.Sprintf("kubectl krew upgrade %s", k.pluginName)
|
||||
}
|
||||
|
||||
11
pkg/updater/pkgmgr/pkgmgr.go
Normal file
11
pkg/updater/pkgmgr/pkgmgr.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package pkgmgr
|
||||
|
||||
// PackageManager represents an external package manager that can manage the binary
|
||||
type PackageManager interface {
|
||||
// IsInstalled returns true if the package/formula is installed via this package manager
|
||||
IsInstalled() (bool, error)
|
||||
// UpgradeCommand returns the command the user should run to upgrade
|
||||
UpgradeCommand() string
|
||||
// Name returns the human-readable name of the package manager
|
||||
Name() string
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"time"
|
||||
|
||||
hv "github.com/hashicorp/go-version"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/updater/pkgmgr"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/version"
|
||||
)
|
||||
|
||||
@@ -47,6 +48,8 @@ func (o *Options) client() *http.Client {
|
||||
|
||||
// CheckAndUpdate checks GitHub releases for a newer version and, if newer, downloads
|
||||
// the corresponding tar.gz asset, extracts the binary, and atomically replaces CurrentPath.
|
||||
// If the binary was installed via a package manager (brew, krew), it will display the
|
||||
// appropriate upgrade command instead of performing the update.
|
||||
func CheckAndUpdate(ctx context.Context, o Options) error {
|
||||
if o.Skip {
|
||||
return nil
|
||||
@@ -80,6 +83,15 @@ func CheckAndUpdate(ctx context.Context, o Options) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if installed via package manager - only show message if newer version exists
|
||||
if pkgMgr := detectPackageManager(o.BinaryName); pkgMgr != nil {
|
||||
if o.Printf != nil {
|
||||
o.Printf("A newer version (%s) is available. Please run: %s\n",
|
||||
latest, pkgMgr.UpgradeCommand())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if o.Printf != nil {
|
||||
o.Printf("Updating %s from %s to %s...\n", o.BinaryName, current, latest)
|
||||
}
|
||||
@@ -259,3 +271,62 @@ func sanityCheckBinary(path string) error {
|
||||
_ = hex.EncodeToString(h.Sum(nil))
|
||||
return nil
|
||||
}
|
||||
|
||||
// detectPackageManager checks if the binary was installed via a known package manager
|
||||
func detectPackageManager(binaryName string) pkgmgr.PackageManager {
|
||||
// Map binary names to their package manager formula/plugin names
|
||||
formulaName := getHomebrewFormulaName(binaryName)
|
||||
pluginName := getKrewPluginName(binaryName)
|
||||
|
||||
// List of package managers to check
|
||||
packageManagers := []pkgmgr.PackageManager{
|
||||
pkgmgr.NewHomebrewPackageManager(formulaName),
|
||||
pkgmgr.NewKrewPackageManager(pluginName),
|
||||
}
|
||||
|
||||
for _, pm := range packageManagers {
|
||||
installed, err := pm.IsInstalled()
|
||||
if err != nil {
|
||||
// Continue checking other package managers if one fails
|
||||
continue
|
||||
}
|
||||
if installed {
|
||||
return pm
|
||||
}
|
||||
}
|
||||
|
||||
// No package manager detected
|
||||
return nil
|
||||
}
|
||||
|
||||
// getHomebrewFormulaName maps binary names to Homebrew formula names
|
||||
func getHomebrewFormulaName(binaryName string) string {
|
||||
formulaMap := map[string]string{
|
||||
"preflight": "preflight",
|
||||
"support-bundle": "support-bundle",
|
||||
"troubleshoot": "troubleshoot",
|
||||
}
|
||||
|
||||
if formula, exists := formulaMap[binaryName]; exists {
|
||||
return formula
|
||||
}
|
||||
|
||||
// Default to the binary name if not in the map
|
||||
return binaryName
|
||||
}
|
||||
|
||||
// getKrewPluginName maps binary names to krew plugin names
|
||||
func getKrewPluginName(binaryName string) string {
|
||||
pluginMap := map[string]string{
|
||||
"preflight": "preflight",
|
||||
"support-bundle": "support-bundle",
|
||||
"troubleshoot": "troubleshoot",
|
||||
}
|
||||
|
||||
if plugin, exists := pluginMap[binaryName]; exists {
|
||||
return plugin
|
||||
}
|
||||
|
||||
// Default to the binary name
|
||||
return binaryName
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user