Skip to content

Commit

Permalink
Merge pull request #8 from skilld-labs/1-add_keyring
Browse files Browse the repository at this point in the history
#1: add keyring support
  • Loading branch information
davidferlay authored Apr 9, 2024
2 parents 296b977 + a07f322 commit 2b637e6
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 68 deletions.
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ module github.com/skilld-labs/plasmactl-update
go 1.21.6

require (
github.com/google/renameio v1.0.1
github.com/launchrctl/keyring v0.1.1
github.com/launchrctl/launchr v0.6.0
github.com/launchrctl/keyring v0.2.0
github.com/launchrctl/launchr v0.7.0
github.com/spf13/cobra v1.8.0
)

Expand Down
10 changes: 4 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU=
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
Expand All @@ -57,10 +55,10 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/launchrctl/keyring v0.1.1 h1:arwOg7ultBcE/ZEeERNFfDrL1q615xDG6v2CC2EfcKg=
github.com/launchrctl/keyring v0.1.1/go.mod h1:ECTYIH2h8CX+9pC93oVtmTMsqgeWd+MOruq+Kk7Y0io=
github.com/launchrctl/launchr v0.6.0 h1:hOyftbkd/Sv7Q+O902WXaCprNx8pMAyZ1juw8r+s94c=
github.com/launchrctl/launchr v0.6.0/go.mod h1:mz7JSPdg/PqRX3iUZdln8n0kmq/B7vhC8sPeT9z5LHs=
github.com/launchrctl/keyring v0.2.0 h1:rwIfYryajarEH1uoM2OU6OfAhjABNg09UQWH4GQ6T8s=
github.com/launchrctl/keyring v0.2.0/go.mod h1:/jdzj6fbw/idyd0AsragVSri4I0BUhT5596mU5rQ8OI=
github.com/launchrctl/launchr v0.7.0 h1:lHnPebOXJAMZK1FpRnShgw6BNwxbhF3HrUAQKpHBF5o=
github.com/launchrctl/launchr v0.7.0/go.mod h1:et+ykNbE3m7mMPydWKDV/6slFus1CD1vOaGH+j5uJ3M=
github.com/moby/moby v25.0.4+incompatible h1:vea1J80wDM5x5geaZSaywFkfFxLABJIQ3mmR4ewZGbU=
github.com/moby/moby v25.0.4+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
Expand Down
38 changes: 22 additions & 16 deletions plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,52 @@ package plasmactlupdate

import (
"fmt"
"github.com/launchrctl/keyring"
"github.com/launchrctl/launchr"
"github.com/launchrctl/launchr/pkg/cli"
"github.com/launchrctl/launchr/pkg/log"
"github.com/spf13/cobra"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"

"github.com/launchrctl/keyring"
"github.com/launchrctl/launchr"
"github.com/launchrctl/launchr/pkg/cli"
"github.com/launchrctl/launchr/pkg/log"
"github.com/spf13/cobra"
)

func init() {
launchr.RegisterPlugin(&Plugin{})
}

// Plugin is launchr plugin providing update action.
type Plugin struct{}
type Plugin struct {
k keyring.Keyring
}

// PluginInfo implements launchr.Plugin interface.
func (p *Plugin) PluginInfo() launchr.PluginInfo {
return launchr.PluginInfo{}
return launchr.PluginInfo{
Weight: 20,
}
}

// OnAppInit implements launchr.Plugin interface.
func (p *Plugin) OnAppInit(_ launchr.App) error {
func (p *Plugin) OnAppInit(app launchr.App) error {
app.GetService(&p.k)
return nil
}

// CobraAddCommands implements launchr.CobraPlugin interface to provide bump functionality.
func (p *Plugin) CobraAddCommands(rootCmd *cobra.Command) error {
var creds keyring.CredentialsItem
var ci keyring.CredentialsItem

var updCmd = &cobra.Command{
Use: "update",
Short: "Command to fetch and install latest version of plasmactl",
RunE: func(cmd *cobra.Command, args []string) error {
// Don't show usage help on a runtime error.
cmd.SilenceUsage = true
u, err := CreateUpdate(creds)
u, err := createUpdateAction(p.k, ci)
if err != nil {
return err
}
Expand All @@ -52,16 +58,16 @@ func (p *Plugin) CobraAddCommands(rootCmd *cobra.Command) error {
}

// Credentials flags
creds.URL = BaseUrl
updCmd.Flags().StringVarP(&creds.Username, "username", "u", "", "Username")
updCmd.Flags().StringVarP(&creds.Password, "password", "p", "", "Password")
ci.URL = baseURL
updCmd.Flags().StringVarP(&ci.Username, "username", "u", "", "Username")
updCmd.Flags().StringVarP(&ci.Password, "password", "p", "", "Password")
rootCmd.AddCommand(updCmd)

return nil
}

// runUpdate command entrypoint.
func runUpdate(u *Update) error {
func runUpdate(u *updateAction) error {
// Wrapper to conclude errors.
if err := runCommands(u); err != nil {
u.exitWithError()
Expand All @@ -72,7 +78,7 @@ func runUpdate(u *Update) error {
}

// runCommands run commands one by one.
func runCommands(u *Update) error {
func runCommands(u *updateAction) error {
cli.Println("Starting plasmactl installation...")

currOS, arch, err := u.initVars()
Expand All @@ -98,7 +104,7 @@ func runCommands(u *Update) error {
}

// Format the URL with the determined 'os', 'arch' and 'extension' values.
u.c.URL = fmt.Sprintf(binPathMask, BaseUrl, sr, currOS, arch, u.ext)
u.c.URL = fmt.Sprintf(binPathMask, baseURL, sr, currOS, arch, u.ext)
cli.Println("Downloading file: %s", u.c.URL)

// Download file to the temp folder.
Expand Down
106 changes: 63 additions & 43 deletions update.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ package plasmactlupdate
import (
"errors"
"fmt"
"github.com/launchrctl/keyring"
"github.com/launchrctl/launchr/pkg/cli"
"github.com/launchrctl/launchr/pkg/log"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"

"github.com/launchrctl/keyring"
"github.com/launchrctl/launchr/pkg/cli"
"github.com/launchrctl/launchr/pkg/log"
)

// Update stored update definition.
type Update struct {
type updateAction struct {
k keyring.Keyring
c keyring.CredentialsItem
ext string
fName string
Expand All @@ -25,29 +26,30 @@ type Update struct {
fDir string
}

// CreateUpdate instance.
func CreateUpdate(cr keyring.CredentialsItem) (*Update, error) {
return &Update{cr, "", "", "", "", ""}, nil
// createUpdateAction instance.
func createUpdateAction(kr keyring.Keyring, cr keyring.CredentialsItem) (*updateAction, error) {
return &updateAction{k: kr, c: cr}, nil
}

// Errors.
var (
errUnsupportedOS = errors.New("unsupported operating system")
errUnsupportedArch = errors.New("unsupported architecture")
errInvalidCreds = errors.New("failed to validate credentials")
errUnsupportedOS = errors.New("unsupported operating system")
errUnsupportedArch = errors.New("unsupported architecture")
errInvalidCreds = errors.New("failed to validate credentials")
errMalformedKeyring = errors.New("the keyring is malformed or wrong passphrase provided")
)

// Define the URL pattern for the file.
const (
BaseUrl = "https://repositories.skilld.cloud/repository/pla-plasmactl-raw"
baseURL = "https://repositories.skilld.cloud/repository/pla-plasmactl-raw"
releasePath = "stable_release"
binPathMask = "%s/%s/plasmactl_%s_%s%s"
)

// initVars initialize plugin variables.
func (u *Update) initVars() (string, string, error) {
func (u *updateAction) initVars() (string, string, error) {
// Get username and password.
if err := u.getCreds(); err != nil {
if err := u.getCredentials(); err != nil {
return "", "", err
}

Expand All @@ -57,7 +59,7 @@ func (u *Update) initVars() (string, string, error) {
return "", "", err
}

// Set Update vars.
// Set updateAction vars.
u.fName = fmt.Sprintf("plasmactl%s", u.ext)
u.fTmpPath = filepath.Join(os.TempDir(), u.fName)

Expand All @@ -67,7 +69,7 @@ func (u *Update) initVars() (string, string, error) {
return "", "", err
}

u.c.URL = fmt.Sprintf("%s/%s", BaseUrl, releasePath)
u.c.URL = fmt.Sprintf("%s/%s", baseURL, releasePath)

log.Debug("OS = %s\n", currOS)
log.Debug("Arch = %s\n", arch)
Expand All @@ -77,25 +79,44 @@ func (u *Update) initVars() (string, string, error) {
return currOS, arch, nil
}

// getCreds stores username and password credentials.
func (u *Update) getCreds() error {
if u.c.Username != "" && u.c.Password != "" {
return nil
}
// getCredentials stores username and password credentials.
func (u *updateAction) getCredentials() error {
repoURL := fmt.Sprintf("%s/%s", baseURL, releasePath)
log.Debug("Source url of release: %s\n", repoURL)

if u.c.URL != "" {
cli.Println("Enter credentials for %s", u.c.URL)
}
// Get credentials and save in keyring.
ci, err := u.k.GetForURL(repoURL)
if err != nil {
if errors.Is(err, keyring.ErrEmptyPass) {
return err
} else if !errors.Is(err, keyring.ErrNotFound) {
log.Debug("%s", err)
return errMalformedKeyring
}

if err := keyring.RequestCredentialsFromTty(&u.c); err != nil {
return err
ci.URL = repoURL
ci.Username = u.c.Username
ci.Password = u.c.Password
if ci.Username == "" || ci.Password == "" {
cli.Println("Enter credentials for %s", ci.URL)
if err = keyring.RequestCredentialsFromTty(&ci); err != nil {
return err
}
}

if err = u.k.AddItem(ci); err != nil {
return err
}

err = u.k.Save()
}

return nil
u.c = ci
return err
}

// getOS check operating system supports and set extension package file.
func (u *Update) getOS() (os string, err error) {
func (u *updateAction) getOS() (os string, err error) {
os = runtime.GOOS

if os == "windows" {
Expand All @@ -113,14 +134,14 @@ func getArch() (arch string, err error) {

if arch == "amd64" || arch == "386" || arch == "arm64" {
return arch, nil
} else {
cli.Println("Unsupported architecture: %s", arch)
return "", errUnsupportedArch
}

cli.Println("Unsupported architecture: %s", arch)
return "", errUnsupportedArch
}

// validateCredentials make request to validate credentials and return HTTP status code.
func (u *Update) validateCredentials() error {
func (u *updateAction) validateCredentials() error {
r, err := u.sendRequest()
if err != nil {
return err
Expand Down Expand Up @@ -151,7 +172,7 @@ func (u *Update) validateCredentials() error {
}

// sendRequest send HTTP request, make authorization and return response.
func (u *Update) sendRequest() (*http.Response, error) {
func (u *updateAction) sendRequest() (*http.Response, error) {
client := &http.Client{}
req, err := http.NewRequest("GET", u.c.URL, nil)
if err != nil {
Expand All @@ -168,7 +189,7 @@ func (u *Update) sendRequest() (*http.Response, error) {
}

// getStableRelease send request and get stable release version.
func (u *Update) getStableRelease() (string, error) {
func (u *updateAction) getStableRelease() (string, error) {
resp, err := u.sendRequest()

if err != nil {
Expand All @@ -189,7 +210,7 @@ func (u *Update) getStableRelease() (string, error) {
}

// downloadFile Download the file using with Basic Auth header.
func (u *Update) downloadFile() error {
func (u *updateAction) downloadFile() error {
resp, err := u.sendRequest()
if err != nil {
return err
Expand Down Expand Up @@ -221,7 +242,7 @@ func (u *Update) downloadFile() error {
}

// setBinPath get bin folder path.
func (u *Update) setBinPath(envPath, homeDir string) {
func (u *updateAction) setBinPath(envPath, homeDir string) {
if strings.Contains(envPath, homeDir+"/.global/bin") {
u.fDir = filepath.Join(homeDir, ".global/bin")
} else if strings.Contains(envPath, homeDir+"/.local/bin") {
Expand All @@ -234,7 +255,7 @@ func (u *Update) setBinPath(envPath, homeDir string) {
}

// installFile copy file to the bin folder and remove temp file.
func (u *Update) installFile(dirPath string) error {
func (u *updateAction) installFile(dirPath string) error {
cli.Println("Installing %s binary under %s", u.fName, dirPath)

info, err := os.Stat(u.fDir)
Expand All @@ -248,23 +269,22 @@ func (u *Update) installFile(dirPath string) error {
if err != nil {
return err
}
defer src.Close()

// Set temp permissions for the folder with plasmactl.
if err = u.setFolderPermissions("777", u.fDir); err != nil {
return err
}

fTmpName := u.fPath + ".tmp"
dst, err := os.Create(fTmpName)
dst, err := os.Create(filepath.Clean(fTmpName))
if err != nil {
src.Close()
u.setFolderPermissions(pathPerm, u.fDir)
return err
}
defer dst.Close()

_, err = io.Copy(dst, src)
src.Close()
dst.Close()
if err != nil {
u.setFolderPermissions(pathPerm, u.fDir)
return err
Expand All @@ -289,7 +309,7 @@ func (u *Update) installFile(dirPath string) error {
return nil
}

func (u *Update) setFolderPermissions(pathPerm, fPath string) error {
func (u *updateAction) setFolderPermissions(pathPerm, fPath string) error {
cmd := exec.Command("sudo", "chmod", pathPerm, fPath)
if err := cmd.Run(); err != nil {
log.Debug("Error to set back %s folder permissions", fPath)
Expand All @@ -299,7 +319,7 @@ func (u *Update) setFolderPermissions(pathPerm, fPath string) error {
}

// exitWithError exit with error and remove temp file.
func (u *Update) exitWithError() {
func (u *updateAction) exitWithError() {
if _, err := os.Stat(u.fTmpPath); err == nil {
if err = os.Remove(u.fTmpPath); err != nil {
log.Err("Error file %s deleting: %s", u.fTmpPath, err)
Expand Down

0 comments on commit 2b637e6

Please sign in to comment.