Skip to content

Commit

Permalink
create plugin.go
Browse files Browse the repository at this point in the history
Signed-off-by: nitishfy <[email protected]>
  • Loading branch information
nitishfy committed Sep 26, 2024
1 parent 3b7b475 commit 0fb4013
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 93 deletions.
95 changes: 4 additions & 91 deletions cmd/argocd/commands/root.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
package commands

import (
pluginError "errors"
"fmt"
"github.com/spf13/cobra"
"k8s.io/client-go/tools/clientcmd"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"syscall"

"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/admin"
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize"
Expand All @@ -24,88 +19,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/localconfig"
)

type ArgoCDCLIOptions struct {
PluginHandler PluginHandler
Arguments []string
}

// PluginHandler parses command line arguments
// and performs executable filename lookups to search
// for valid plugin files, and execute found plugins.
type PluginHandler interface {
// LookForPlugin will iterate over a list of given prefixes
// in order to recognize valid plugin filenames.
// The first filepath to match a prefix is returned.
LookForPlugin(filename string) (string, bool)
// ExecutePlugin receives an executable's filepath, a slice
// of arguments, and a slice of environment variables
// to relay to the executable.
ExecutePlugin(executablePath string, cmdArgs, environment []string) error
}

// DefaultPluginHandler implements the PluginHandler interface
type DefaultPluginHandler struct {
ValidPrefixes []string
}

func NewDefaultPluginHandler(validPrefixes []string) *DefaultPluginHandler {
return &DefaultPluginHandler{
ValidPrefixes: validPrefixes,
}
}

// LookForPlugin implements PluginHandler
func (h *DefaultPluginHandler) LookForPlugin(filename string) (string, bool) {
for _, prefix := range h.ValidPrefixes {
path, err := exec.LookPath(fmt.Sprintf("%s-%s", prefix, filename))
if shouldSkipOnLookPathErr(err) || len(path) == 0 {
continue
}
return path, true
}
return "", false
}

// ExecutePlugin implements PluginHandler
func (h *DefaultPluginHandler) ExecutePlugin(executablePath string, cmdArgs, environment []string) error {
// Windows does not support exec syscall.
if runtime.GOOS == "windows" {
cmd := Command(executablePath, cmdArgs...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
cmd.Env = environment
err := cmd.Run()
if err == nil {
os.Exit(0)
}
return err
}

return syscall.Exec(executablePath, append([]string{executablePath}, cmdArgs...), environment)
}

func Command(name string, arg ...string) *exec.Cmd {
cmd := &exec.Cmd{
Path: name,
Args: append([]string{name}, arg...),
}
if filepath.Base(name) == name {
lp, err := exec.LookPath(name)
if lp != "" && !shouldSkipOnLookPathErr(err) {
// Update cmd.Path even if err is non-nil.
// If err is ErrDot (especially on Windows), lp may include a resolved
// extension (like .exe or .bat) that should be preserved.
cmd.Path = lp
}
}
return cmd
}

func shouldSkipOnLookPathErr(err error) bool {
return err != nil && !pluginError.Is(err, exec.ErrDot)
}

func init() {
cobra.OnInitialize(initConfig)
}
Expand All @@ -116,13 +29,13 @@ func initConfig() {
}

func NewDefaultArgoCDCommand() *cobra.Command {
return NewDefaultArgoCDCommandWithArgs(ArgoCDCLIOptions{
PluginHandler: NewDefaultPluginHandler([]string{"argocd"}),
return NewDefaultArgoCDCommandWithArgs(cmdutil.ArgoCDCLIOptions{
PluginHandler: cmdutil.NewDefaultPluginHandler([]string{"argocd"}),
Arguments: os.Args,
})
}

func NewDefaultArgoCDCommandWithArgs(o ArgoCDCLIOptions) *cobra.Command {
func NewDefaultArgoCDCommandWithArgs(o cmdutil.ArgoCDCLIOptions) *cobra.Command {
cmd := NewCommand()

if o.PluginHandler == nil {
Expand Down Expand Up @@ -156,7 +69,7 @@ func NewDefaultArgoCDCommandWithArgs(o ArgoCDCLIOptions) *cobra.Command {
return cmd
}

func HandlePluginCommand(pluginHandler PluginHandler, cmdArgs []string, minArgs int) error {
func HandlePluginCommand(pluginHandler cmdutil.PluginHandler, cmdArgs []string, minArgs int) error {
var remainingArgs []string // this will contain all "non-flag" arguments
for _, arg := range cmdArgs {
// if you encounter a flag, break the loop
Expand Down
6 changes: 4 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"github.com/argoproj/argo-cd/v2/cmd/util"
"os"
"path/filepath"

Expand All @@ -24,8 +25,9 @@ const (

func main() {
var command *cobra.Command
o := cli.ArgoCDCLIOptions{
PluginHandler: cli.NewDefaultPluginHandler([]string{"argocd"}),

o := util.ArgoCDCLIOptions{
PluginHandler: util.NewDefaultPluginHandler([]string{"argocd"}),
Arguments: os.Args,
}

Expand Down
94 changes: 94 additions & 0 deletions cmd/util/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package util

import (
pluginError "errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"syscall"
)

type ArgoCDCLIOptions struct {
PluginHandler PluginHandler
Arguments []string
}

// PluginHandler parses command line arguments
// and performs executable filename lookups to search
// for valid plugin files, and execute found plugins.
type PluginHandler interface {
// LookForPlugin will iterate over a list of given prefixes
// in order to recognize valid plugin filenames.
// The first filepath to match a prefix is returned.
LookForPlugin(filename string) (string, bool)
// ExecutePlugin receives an executable's filepath, a slice
// of arguments, and a slice of environment variables
// to relay to the executable.
ExecutePlugin(executablePath string, cmdArgs, environment []string) error
}

// DefaultPluginHandler implements the PluginHandler interface
type DefaultPluginHandler struct {
ValidPrefixes []string
}

func NewDefaultPluginHandler(validPrefixes []string) *DefaultPluginHandler {
return &DefaultPluginHandler{
ValidPrefixes: validPrefixes,
}
}

// LookForPlugin implements PluginHandler
func (h *DefaultPluginHandler) LookForPlugin(filename string) (string, bool) {
for _, prefix := range h.ValidPrefixes {
path, err := exec.LookPath(fmt.Sprintf("%s-%s", prefix, filename))
if shouldSkipOnLookPathErr(err) || len(path) == 0 {
continue
}
return path, true
}
return "", false
}

// ExecutePlugin implements PluginHandler
func (h *DefaultPluginHandler) ExecutePlugin(executablePath string, cmdArgs, environment []string) error {
// Windows does not support exec syscall.
if runtime.GOOS == "windows" {
cmd := Command(executablePath, cmdArgs...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
cmd.Env = environment
err := cmd.Run()
if err == nil {
os.Exit(0)
}
return err
}

return syscall.Exec(executablePath, append([]string{executablePath}, cmdArgs...), environment)
}

func Command(name string, arg ...string) *exec.Cmd {
cmd := &exec.Cmd{
Path: name,
Args: append([]string{name}, arg...),
}
if filepath.Base(name) == name {
lp, err := exec.LookPath(name)
if lp != "" && !shouldSkipOnLookPathErr(err) {
// Update cmd.Path even if err is non-nil.
// If err is ErrDot (especially on Windows), lp may include a resolved
// extension (like .exe or .bat) that should be preserved.
cmd.Path = lp
}
}
return cmd
}

// shouldSkipOnLookPathErr checks if the error is nil and it is of type ErrDot
func shouldSkipOnLookPathErr(err error) bool {
return err != nil && !pluginError.Is(err, exec.ErrDot)
}

0 comments on commit 0fb4013

Please sign in to comment.