diff --git a/cmd/plugins/plugin.go b/cmd/plugins/plugin.go index 4cb5da1b..14e28eb2 100644 --- a/cmd/plugins/plugin.go +++ b/cmd/plugins/plugin.go @@ -28,7 +28,6 @@ import ( dkplog "github.com/deckhouse/deckhouse/pkg/log" - "github.com/deckhouse/deckhouse-cli/cmd/plugins/flags" "github.com/deckhouse/deckhouse-cli/pkg/registry/service" ) @@ -41,7 +40,7 @@ const ( func NewPluginCommand(commandName string, description string, aliases []string, logger *dkplog.Logger) *cobra.Command { pc := NewPluginsCommand(logger.Named("plugins-command")) - pluginContractFilePath := path.Join(flags.DeckhousePluginsDir, "cache", "contracts", "system.json") + pluginContractFilePath := path.Join(pc.pluginDirectory, "cache", "contracts", "system.json") pluginContract, err := service.GetPluginContractFromFile(pluginContractFilePath) if err != nil { logger.Debug("failed to get plugin contract from cache", slog.String("error", err.Error())) @@ -53,18 +52,18 @@ func NewPluginCommand(commandName string, description string, aliases []string, // to check we can create directories here // we try to create root plugins folder - err = os.MkdirAll(flags.DeckhousePluginsDir+"/plugins", 0755) + err = os.MkdirAll(pc.pluginDirectory+"/plugins", 0755) // if permission failed if errors.Is(err, os.ErrPermission) { - pc.logger.Warn("use homedir instead of default d8 plugins path in '/opt/deckhouse/lib/deckhouse-cli'", slog.String("new_path", flags.DeckhousePluginsDir), dkplog.Err(err)) + pc.logger.Warn("use homedir instead of default d8 plugins path in '/opt/deckhouse/lib/deckhouse-cli'", slog.String("new_path", pc.pluginDirectory), dkplog.Err(err)) - flags.DeckhousePluginsDir, err = os.UserHomeDir() + pc.pluginDirectory, err = os.UserHomeDir() if err != nil { logger.Warn("failed to receive home dir to create plugins dir", slog.String("error", err.Error())) return nil } - flags.DeckhousePluginsDir = path.Join(flags.DeckhousePluginsDir, ".deckhouse-cli") + pc.pluginDirectory = path.Join(pc.pluginDirectory, ".deckhouse-cli") } if err != nil { @@ -83,11 +82,12 @@ func NewPluginCommand(commandName string, description string, aliases []string, pc.InitPluginServices() }, Run: func(cmd *cobra.Command, args []string) { - installed, err := checkInstalled(commandName) + installed, err := pc.checkInstalled(commandName) if err != nil { fmt.Println("Error checking installed:", err) return } + if !installed { fmt.Println("Not installed, installing...") err = pc.InstallPlugin(cmd.Context(), commandName, "", -1) @@ -98,7 +98,7 @@ func NewPluginCommand(commandName string, description string, aliases []string, fmt.Println("Installed successfully") } - pluginPath := path.Join(flags.DeckhousePluginsDir, "plugins", commandName) + pluginPath := path.Join(pc.pluginDirectory, "plugins", commandName) pluginBinaryPath := path.Join(pluginPath, "current") absPath, err := filepath.Abs(pluginBinaryPath) if err != nil { @@ -106,7 +106,7 @@ func NewPluginCommand(commandName string, description string, aliases []string, return } - logger.Info("Executing plugin", slog.Any("args", args)) + logger.Debug("Executing plugin", slog.Any("args", args)) command := exec.CommandContext(cmd.Context(), absPath, args...) command.Stdout = os.Stdout @@ -122,8 +122,8 @@ func NewPluginCommand(commandName string, description string, aliases []string, return systemCmd } -func checkInstalled(commandName string) (bool, error) { - installedFile := path.Join(flags.DeckhousePluginsDir, "plugins", commandName, "current") +func (pc *PluginsCommand) checkInstalled(commandName string) (bool, error) { + installedFile := path.Join(pc.pluginDirectory, "plugins", commandName, "current") absPath, err := filepath.Abs(installedFile) if err != nil { return false, fmt.Errorf("failed to compute absolute path: %w", err) diff --git a/cmd/plugins/plugins.go b/cmd/plugins/plugins.go index a4b75e46..2f44966e 100644 --- a/cmd/plugins/plugins.go +++ b/cmd/plugins/plugins.go @@ -19,6 +19,7 @@ package plugins import ( "context" "encoding/json" + "errors" "fmt" "log/slog" "os" @@ -42,6 +43,7 @@ import ( type PluginsCommand struct { service *service.PluginService pluginRegistryClient registry.Client + pluginDirectory string logger *dkplog.Logger } @@ -62,7 +64,8 @@ type pluginsListData struct { func NewPluginsCommand(logger *dkplog.Logger) *PluginsCommand { return &PluginsCommand{ - logger: logger, + pluginDirectory: flags.DeckhousePluginsDir, + logger: logger, } } @@ -76,6 +79,20 @@ func NewCommand(logger *dkplog.Logger) *cobra.Command { PersistentPreRun: func(_ *cobra.Command, _ []string) { // init plugin services for subcommands after flags are parsed pc.InitPluginServices() + + err := os.MkdirAll(flags.DeckhousePluginsDir+"/plugins", 0755) + // if permission failed + if errors.Is(err, os.ErrPermission) { + pc.logger.Warn("use homedir instead of default d8 plugins path in '/opt/deckhouse/lib/deckhouse-cli'", slog.String("new_path", flags.DeckhousePluginsDir), dkplog.Err(err)) + + newPluginDirectory, err := os.UserHomeDir() + if err != nil { + logger.Warn("failed to receive home dir to create plugins dir", slog.String("error", err.Error())) + return + } + + pc.pluginDirectory = path.Join(newPluginDirectory, ".deckhouse-cli") + } }, } @@ -150,7 +167,7 @@ func (pc *PluginsCommand) preparePluginsListData(ctx context.Context, showInstal // fetchInstalledPlugins retrieves installed plugins from filesystem func (pc *PluginsCommand) fetchInstalledPlugins() ([]pluginDisplayInfo, error) { - plugins, err := os.ReadDir(path.Join(flags.DeckhousePluginsDir, "plugins")) + plugins, err := os.ReadDir(path.Join(pc.pluginDirectory, "plugins")) if err != nil { return nil, fmt.Errorf("failed to read plugins directory: %w", err) } @@ -158,7 +175,7 @@ func (pc *PluginsCommand) fetchInstalledPlugins() ([]pluginDisplayInfo, error) { res := make([]pluginDisplayInfo, 0, len(plugins)) for _, plugin := range plugins { - pluginBinaryPath := path.Join(flags.DeckhousePluginsDir, "plugins", plugin.Name(), "current") + pluginBinaryPath := path.Join(pc.pluginDirectory, "plugins", plugin.Name(), "current") cmd := exec.Command(pluginBinaryPath, "--version") output, err := cmd.Output() @@ -202,7 +219,7 @@ func (pc *PluginsCommand) fetchInstalledPlugins() ([]pluginDisplayInfo, error) { } func (pc *PluginsCommand) getInstalledPluginContract(pluginName string) (*internal.Plugin, error) { - contractFile := path.Join(flags.DeckhousePluginsDir, "cache", "contracts", pluginName+".json") + contractFile := path.Join(pc.pluginDirectory, "cache", "contracts", pluginName+".json") file, err := os.Open(contractFile) if err != nil { @@ -520,7 +537,7 @@ func (pc *PluginsCommand) InstallPlugin(ctx context.Context, pluginName, version func (pc *PluginsCommand) installPlugin(ctx context.Context, pluginName string, version *semver.Version, useMajor int) error { // create plugin directory if it doesn't exist // example path: /opt/deckhouse/lib/deckhouse-cli/plugins/example-plugin - pluginDir := path.Join(flags.DeckhousePluginsDir, "plugins", pluginName) + pluginDir := path.Join(pc.pluginDirectory, "plugins", pluginName) err := os.MkdirAll(pluginDir, 0755) if err != nil { return fmt.Errorf("failed to create plugin directory: %w", err) @@ -615,7 +632,7 @@ func (pc *PluginsCommand) installPlugin(ctx context.Context, pluginName string, // cache contract // example path: /opt/deckhouse/lib/deckhouse-cli/cache/contracts - contractDir := path.Join(flags.DeckhousePluginsDir, "cache", "contracts") + contractDir := path.Join(pc.pluginDirectory, "cache", "contracts") err = os.MkdirAll(contractDir, 0755) if err != nil { return fmt.Errorf("failed to create contract directory: %w", err) @@ -707,7 +724,7 @@ func (pc *PluginsCommand) pluginsUpdateAllCommand() *cobra.Command { fmt.Println("Updating all installed plugins...") - plugins, err := os.ReadDir(path.Join(flags.DeckhousePluginsDir, "plugins")) + plugins, err := os.ReadDir(path.Join(pc.pluginDirectory, "plugins")) if err != nil { return fmt.Errorf("failed to read plugins directory: %w", err) } @@ -739,7 +756,7 @@ func (pc *PluginsCommand) pluginsRemoveCommand() *cobra.Command { pluginName := args[0] fmt.Printf("Removing plugin: %s\n", pluginName) - pluginDir := path.Join(flags.DeckhousePluginsDir, "plugins", pluginName) + pluginDir := path.Join(pc.pluginDirectory, "plugins", pluginName) fmt.Printf("Removing plugin from: %s\n", pluginDir) err := os.RemoveAll(pluginDir) @@ -749,7 +766,7 @@ func (pc *PluginsCommand) pluginsRemoveCommand() *cobra.Command { fmt.Println("Cleaning up plugin files...") - os.Remove(path.Join(flags.DeckhousePluginsDir, "cache", "contracts", pluginName+".json")) + os.Remove(path.Join(pc.pluginDirectory, "cache", "contracts", pluginName+".json")) fmt.Printf("✓ Plugin '%s' successfully removed!\n", pluginName) @@ -771,7 +788,7 @@ func (pc *PluginsCommand) pluginsRemoveAllCommand() *cobra.Command { RunE: func(_ *cobra.Command, _ []string) error { fmt.Println("Removing all installed plugins...") - plugins, err := os.ReadDir(path.Join(flags.DeckhousePluginsDir, "plugins")) + plugins, err := os.ReadDir(path.Join(pc.pluginDirectory, "plugins")) if err != nil { return fmt.Errorf("failed to read plugins directory: %w", err) } @@ -779,7 +796,7 @@ func (pc *PluginsCommand) pluginsRemoveAllCommand() *cobra.Command { fmt.Println("Found", len(plugins), "plugins to remove:") for _, plugin := range plugins { - pluginDir := path.Join(flags.DeckhousePluginsDir, "plugins", plugin.Name()) + pluginDir := path.Join(pc.pluginDirectory, "plugins", plugin.Name()) fmt.Printf("Removing plugin from: %s\n", pluginDir) err := os.RemoveAll(pluginDir) @@ -789,7 +806,7 @@ func (pc *PluginsCommand) pluginsRemoveAllCommand() *cobra.Command { fmt.Printf("Cleaning up plugin files for '%s'...\n", plugin.Name()) - os.Remove(path.Join(flags.DeckhousePluginsDir, "cache", "contracts", plugin.Name()+".json")) + os.Remove(path.Join(pc.pluginDirectory, "cache", "contracts", plugin.Name()+".json")) fmt.Printf("✓ Plugin '%s' successfully removed!\n", plugin.Name()) } diff --git a/pkg/registry/service/plugin_service.go b/pkg/registry/service/plugin_service.go index 8a88bce8..8f1523f7 100644 --- a/pkg/registry/service/plugin_service.go +++ b/pkg/registry/service/plugin_service.go @@ -81,15 +81,13 @@ func (s *PluginService) GetPluginContract(ctx context.Context, pluginName, tag s return nil, fmt.Errorf("no manifests found in index manifest") } - // hardcoded first + // hardcoded first manifest (all contracts must be the same for all manifests) digest := indexManifest.GetManifests()[0].GetDigest() if digest.String() == "" { return nil, fmt.Errorf("no digest found in manifest") } - digestClient := pluginClient.WithSegment("meta") - - digestManifestResult, err = digestClient.GetManifest(ctx, "@"+digest.String()) + digestManifestResult, err = pluginClient.GetManifest(ctx, "@"+digest.String()) if err != nil { return nil, fmt.Errorf("failed to get manifest: %w", err) }