Skip to content

Commit

Permalink
[release-v1.15] Add plugin install command (#524)
Browse files Browse the repository at this point in the history
* Add plugin install command

* Split into functions and cleanup code

* Fix formatting of desc

* Add plugin remove and update flag

* Update pkg/kn/commands/plugin/install.go

Co-authored-by: Roland Huß <[email protected]>

* Update pkg/kn/commands/plugin/remove.go

Co-authored-by: Roland Huß <[email protected]>

* Update pkg/kn/commands/plugin/remove.go

Co-authored-by: Roland Huß <[email protected]>

* Update pkg/kn/commands/plugin/remove.go

Co-authored-by: Roland Huß <[email protected]>

* Update pkg/kn/commands/plugin/remove.go

Co-authored-by: Roland Huß <[email protected]>

* Apply suggestions from code review

Co-authored-by: Roland Huß <[email protected]>

* Remove misleading alias cmd

---------

Co-authored-by: David Simansky <[email protected]>
Co-authored-by: Roland Huß <[email protected]>
  • Loading branch information
3 people authored Nov 13, 2024
1 parent 0b23c43 commit 6ec062a
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 1 deletion.
145 changes: 145 additions & 0 deletions pkg/kn/commands/plugin/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright © 2024 The Knative Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package plugin

import (
"errors"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"

"github.com/spf13/cobra"

"knative.dev/client/pkg/kn/commands"
"knative.dev/client/pkg/kn/config"
)

var updateFlag bool

// NewPluginInstallCommand creates a new `kn plugin install` command
func NewPluginInstallCommand(p *commands.KnParams) *cobra.Command {

var localPath string
cmd := &cobra.Command{
Use: "install",
Short: "Install a plugin from URL or local file",
Long: `Install a plugin by downloading it from a URL or the local filesystem.
Pick up a plugin from a URL or a local path and copy it to ${KN_CONFIG_DIR}/kn/plugins.
Current: ` + config.GlobalConfig.PluginsDir(),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
if localPath == "" {
return errors.New("'kn plugin install' requires the URL as argument or local file specified via '-f'")
}
println("Copying plugin from " + localPath)
// Local plugin file to copy
return fetchLocal(localPath)
}
println("Downloading from " + args[0])
// Download from provided external URL and copy to plugins dir
return fetchFromURL(args[0])
},
}
cmd.Flags().StringVarP(&localPath, "file", "f", "", "Path to install plugin from local filesystem")
cmd.Flags().BoolVarP(&updateFlag, "update", "u", false, "Update installed plugin")
return cmd
}

func parseFilePath(src, separator string) string {
segments := strings.Split(src, separator)
return filepath.Join(config.GlobalConfig.PluginsDir(), segments[len(segments)-1])
}

func fetchLocal(localPath string) error {
localFile, err := os.Open(localPath)
if err != nil {
return err
}
fileName := parseFilePath(localPath, string(os.PathSeparator))

if err := updatePluginCheck(fileName); err != nil {
return err
}

dest, err := os.Create(fileName)
if err != nil {
return err
}
return installPlugin(fileName, dest, localFile)
}

func fetchFromURL(externalUrl string) error {
// Build fileName from fullPath
fileURL, err := url.Parse(externalUrl)
if err != nil {
return err
}
path := fileURL.Path
// Assuming URL, hence forward-slash separator
fileName := parseFilePath(path, "/")

client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}

// Download file
resp, err := client.Get(fileURL.String())
if err != nil {
println(err)
}
defer resp.Body.Close()

if err := updatePluginCheck(fileName); err != nil {
return err
}

file, err := os.Create(fileName)
if err != nil {
return err
}
defer file.Close()

return installPlugin(fileName, file, resp.Body)
}

func updatePluginCheck(path string) error {
_, err := os.Stat(path)
if !os.IsNotExist(err) {
if !updateFlag {
return errors.New("plugin already exists, please use `--update` flag to replace it at '" + path + "'")
}
}
return nil
}

func installPlugin(fileName string, dst io.Writer, src io.Reader) error {
_, err := io.Copy(dst, src)
if err != nil {
return err
}
err = os.Chmod(fileName, os.FileMode(0755))
if err != nil {
return err
}
println("Plugin installed to '" + fileName + "'")
return nil
}
3 changes: 2 additions & 1 deletion pkg/kn/commands/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ Plugins provide extended functionality that is not part of the core kn command-l
Please refer to the documentation and examples for more information about how to write your own plugins.`,
}

pluginCmd.AddCommand(NewPluginInstallCommand(p))
pluginCmd.AddCommand(NewPluginRemoveCommand(p))
pluginCmd.AddCommand(NewPluginListCommand(p))

return pluginCmd
}
52 changes: 52 additions & 0 deletions pkg/kn/commands/plugin/remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright © 2024 The Knative Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package plugin

import (
"errors"
"os"
"path/filepath"

"github.com/spf13/cobra"

"knative.dev/client/pkg/kn/commands"
"knative.dev/client/pkg/kn/config"
)

// NewPluginRemoveCommand creates a new `kn plugin remove` command
func NewPluginRemoveCommand(p *commands.KnParams) *cobra.Command {
cmd := &cobra.Command{
Use: "remove",
Short: "remove a plugin",
Long: `Remove a plugin.
Plugin is removed from ${KN_CONFIG_DIR}/kn/plugins.
Current: ` + config.GlobalConfig.PluginsDir(),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("'kn plugin remove' requires plugin name")
}
name := args[0]
filename := filepath.Join(config.GlobalConfig.PluginsDir(), name)
if _, err := os.Stat(filename); os.IsNotExist(err) {
return errors.New("plugin '" + name + "' does not exist")
}
println("Removed plugin '" + filename + "'")
return os.Remove(filename)
},
}
return cmd
}

0 comments on commit 6ec062a

Please sign in to comment.