-
Notifications
You must be signed in to change notification settings - Fork 292
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4005 from adamkpickering/3953-refactor-rdctl-getR…
…DPath Refactor `rdctl` code that gets path to main Rancher Desktop executable
- Loading branch information
Showing
8 changed files
with
201 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,91 +1,14 @@ | ||
package utils | ||
|
||
import ( | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/rancher-sandbox/rancher-desktop/src/go/rdctl/pkg/directories" | ||
) | ||
|
||
// Get the parent (or grandparent, or great-grandparent...) directory of fullPath. | ||
// numberTimes is the number of steps to ascend in the directory hierarchy. | ||
func MoveToParent(fullPath string, numberTimes int) string { | ||
// Get the steps-th parent directory of fullPath. | ||
func getParentDir(fullPath string, steps int) string { | ||
fullPath = filepath.Clean(fullPath) | ||
for ; numberTimes > 0; numberTimes-- { | ||
for ; steps > 0; steps-- { | ||
fullPath = filepath.Dir(fullPath) | ||
} | ||
return fullPath | ||
} | ||
|
||
/** | ||
* Verify the path exists. For Linux pass in mode bits to guarantee the file is executable (for at least one | ||
* category of user). Note that on macOS the candidate is a directory, so never pass in mode bits. | ||
* And mode bits don't make sense on Windows. | ||
*/ | ||
func CheckExistence(candidatePath string, modeBits fs.FileMode) string { | ||
stat, err := os.Stat(candidatePath) | ||
if err != nil { | ||
return "" | ||
} | ||
if modeBits != 0 && (!stat.Mode().IsRegular() || stat.Mode().Perm()&modeBits == 0) { | ||
// The modeBits check is only for executability -- we only care if at least one of the three | ||
// `x` mode bits is on. So this check isn't used for a general permission-mode-bit check. | ||
return "" | ||
} | ||
return candidatePath | ||
} | ||
|
||
// Returns the absolute path to the Rancher Desktop executable. | ||
// Returns an empty string if the executable was not found. | ||
func GetWindowsRDPath(rdctlPath string) string { | ||
if rdctlPath != "" { | ||
normalParentPath := MoveToParent(rdctlPath, 5) | ||
candidatePath := CheckExistence(filepath.Join(normalParentPath, "Rancher Desktop.exe"), 0) | ||
if candidatePath != "" { | ||
return candidatePath | ||
} | ||
} | ||
homedir, err := os.UserHomeDir() | ||
if err != nil { | ||
homedir = "" | ||
} | ||
dataPaths := []string{} | ||
// %LOCALAPPDATA% | ||
dir, err := directories.GetLocalAppDataDirectory() | ||
if err == nil { | ||
dataPaths = append(dataPaths, dir) | ||
} | ||
// %APPDATA% | ||
dir, err = directories.GetRoamingAppDataDirectory() | ||
if err == nil { | ||
dataPaths = append(dataPaths, dir) | ||
} | ||
// Add these two paths if the above two fail to find where the program was installed | ||
dataPaths = append( | ||
dataPaths, | ||
filepath.Join(homedir, "AppData", "Local"), | ||
filepath.Join(homedir, "AppData", "Roaming"), | ||
) | ||
for _, dataDir := range dataPaths { | ||
candidatePath := CheckExistence(filepath.Join(dataDir, "Programs", "Rancher Desktop", "Rancher Desktop.exe"), 0) | ||
if candidatePath != "" { | ||
return candidatePath | ||
} | ||
} | ||
return "" | ||
} | ||
|
||
func GetDarwinRDPath(rdctlPath string) string { | ||
if rdctlPath != "" { | ||
// we're at .../Applications/R D.app (could have a different name)/Contents/Resources/resources/darwin/bin | ||
// and want to move to the "R D.app" part | ||
RDAppParentPath := MoveToParent(rdctlPath, 6) | ||
if CheckExistence(filepath.Join(RDAppParentPath, "Contents", "MacOS", "Rancher Desktop"), 0o111) != "" { | ||
return RDAppParentPath | ||
} | ||
} | ||
// This fallback is mostly for running `npm run dev` and using the installed app because there is no app | ||
// that rdctl would launch directly in dev mode. | ||
return CheckExistence(filepath.Join("/Applications", "Rancher Desktop.app"), 0) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package utils | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
// Returns the absolute path to the Rancher Desktop executable. | ||
// Returns an empty string if the executable was not found. | ||
func GetRDPath() (string, error) { | ||
rdctlSymlinkPath, err := os.Executable() | ||
if err != nil { | ||
return "", fmt.Errorf("failed to get path to rdctl: %w", err) | ||
} | ||
rdctlPath, err := filepath.EvalSymlinks(rdctlSymlinkPath) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to resolve %q: %w", rdctlSymlinkPath, err) | ||
} | ||
|
||
// we're at .../Applications/R D.app (could have a different name)/Contents/Resources/resources/darwin/bin/rdctl | ||
// and want to move to the "R D.app" part | ||
RDAppParentPath := getParentDir(rdctlPath, 6) | ||
executablePath := filepath.Join(RDAppParentPath, "Contents", "MacOS", "Rancher Desktop") | ||
usable, err := checkUsableApplication(executablePath, true) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to check usability of %q: %w", executablePath, err) | ||
} | ||
if usable { | ||
return RDAppParentPath, nil | ||
} | ||
|
||
// This fallback is mostly for running `npm run dev` and using the installed app because there is no app | ||
// that rdctl would launch directly in dev mode. | ||
candidatePath := filepath.Join("/Applications", "Rancher Desktop.app") | ||
usable, err = checkUsableApplication(candidatePath, false) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to check usability of %q: %w", candidatePath, err) | ||
} | ||
if usable { | ||
return RDAppParentPath, nil | ||
} | ||
|
||
return "", errors.New("search locations exhausted") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package utils | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
// Returns the absolute path to the Rancher Desktop executable, | ||
// or an error if it was unable to find Rancher Desktop. | ||
func GetRDPath() (string, error) { | ||
rdctlSymlinkPath, err := os.Executable() | ||
if err != nil { | ||
return "", fmt.Errorf("failed to get path to rdctl: %w", err) | ||
} | ||
rdctlPath, err := filepath.EvalSymlinks(rdctlSymlinkPath) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to resolve %q: %w", rdctlSymlinkPath, err) | ||
} | ||
// rdctl should be at <installDir>/resources/resources/linux/bin/rdctl. | ||
// rancher-desktop should be 5 directories up from that, at <installDir>/rancher-desktop. | ||
normalParentPath := getParentDir(rdctlPath, 5) | ||
candidatePaths := []string{ | ||
filepath.Join(normalParentPath, "rancher-desktop"), | ||
"/opt/rancher-desktop/rancher-desktop", | ||
} | ||
for _, candidatePath := range candidatePaths { | ||
usable, err := checkUsableApplication(candidatePath, true) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to check usability of %q: %w", candidatePath, err) | ||
} | ||
if usable { | ||
return candidatePath, nil | ||
} | ||
} | ||
return "", errors.New("search locations exhausted") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
//go:build linux || darwin | ||
|
||
package utils | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"golang.org/x/sys/unix" | ||
"io/fs" | ||
"os" | ||
) | ||
|
||
// Verify that the candidatePath is usable as a Rancher Desktop "executable". This means: | ||
// - check that candidatePath exists | ||
// - if checkExecutability is true, check that candidatePath is a regular file, | ||
// and that it is executable | ||
// | ||
// Note that candidatePath may not always be a file; in macOS, it may be a | ||
// .app directory. | ||
func checkUsableApplication(candidatePath string, checkExecutability bool) (bool, error) { | ||
statResult, err := os.Stat(candidatePath) | ||
if errors.Is(err, fs.ErrNotExist) { | ||
return false, nil | ||
} | ||
if err != nil { | ||
return false, fmt.Errorf("failed to get info on %q: %w", candidatePath, err) | ||
} | ||
|
||
if !checkExecutability { | ||
return true, nil | ||
} | ||
|
||
if !statResult.Mode().IsRegular() { | ||
return false, nil | ||
} | ||
|
||
err = unix.Access(candidatePath, unix.X_OK) | ||
return err == nil, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package utils | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/rancher-sandbox/rancher-desktop/src/go/rdctl/pkg/directories" | ||
) | ||
|
||
// Returns the absolute path to the Rancher Desktop executable. | ||
// Returns an empty string if the executable was not found. | ||
func GetRDPath() (string, error) { | ||
rdctlSymlinkPath, err := os.Executable() | ||
if err != nil { | ||
return "", fmt.Errorf("failed to get path to rdctl: %w", err) | ||
} | ||
rdctlPath, err := filepath.EvalSymlinks(rdctlSymlinkPath) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to resolve %q: %w", rdctlSymlinkPath, err) | ||
} | ||
// rdctl should be at <installDir>/resources/resources/win32/bin/rdctl.exe. | ||
// rancher-desktop should be 5 directories up from that, at <installDir>/Rancher Desktop.exe. | ||
normalParentPath := getParentDir(rdctlPath, 5) | ||
candidatePath := filepath.Join(normalParentPath, "Rancher Desktop.exe") | ||
_, err = os.Stat(candidatePath) | ||
if err != nil && !errors.Is(err, fs.ErrNotExist) { | ||
return "", fmt.Errorf("failed to check existence of %q: %w", candidatePath, err) | ||
} | ||
if err == nil { | ||
return candidatePath, nil | ||
} | ||
|
||
homedir, err := os.UserHomeDir() | ||
if err != nil { | ||
homedir = "" | ||
} | ||
dataPaths := []string{} | ||
// %LOCALAPPDATA% | ||
dir, err := directories.GetLocalAppDataDirectory() | ||
if err == nil { | ||
dataPaths = append(dataPaths, dir) | ||
} | ||
// %APPDATA% | ||
dir, err = directories.GetRoamingAppDataDirectory() | ||
if err == nil { | ||
dataPaths = append(dataPaths, dir) | ||
} | ||
dataPaths = append( | ||
dataPaths, | ||
filepath.Join(homedir, "AppData", "Local"), | ||
filepath.Join(homedir, "AppData", "Roaming"), | ||
) | ||
for _, dataDir := range dataPaths { | ||
candidatePath := filepath.Join(dataDir, "Programs", "Rancher Desktop", "Rancher Desktop.exe") | ||
_, err := os.Stat(candidatePath) | ||
if err != nil && !errors.Is(err, fs.ErrNotExist) { | ||
return "", fmt.Errorf("failed to check existence of %q: %w", candidatePath, err) | ||
} | ||
if err == nil { | ||
return candidatePath, nil | ||
} | ||
} | ||
|
||
return "", errors.New("search locations exhausted") | ||
} |