client/pkg/kn/commands/plugin/path_verifier.go

103 lines
2.8 KiB
Go

// Copyright © 2018 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 (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/spf13/cobra"
)
// PathVerifier receives a path and determines if it is valid or not
type PathVerifier interface {
// Verify determines if a given path is valid
Verify(path string) []error
}
// CommandOverrideVerifier verifies that existing kn commands are not overriden
type CommandOverrideVerifier struct {
Root *cobra.Command
SeenPlugins map[string]string
}
// Verify implements PathVerifier and determines if a given path
// is valid depending on whether or not it overwrites an existing
// kn command path, or a previously seen plugin.
func (v *CommandOverrideVerifier) Verify(path string) []error {
if v.Root == nil {
return []error{fmt.Errorf("unable to verify path with nil root")}
}
// extract the plugin binary name
segs := strings.Split(path, string(os.PathSeparator))
binName := segs[len(segs)-1]
cmdPath := strings.Split(binName, "-")
if len(cmdPath) > 1 {
// the first argument is always "kn" for a plugin binary
cmdPath = cmdPath[1:]
}
errors := []error{}
isExec, err := isExecutable(path)
if err == nil && !isExec {
errors = append(errors, fmt.Errorf("warning: %s identified as a kn plugin, but it is not executable", path))
} else if err != nil {
errors = append(errors, fmt.Errorf("error: unable to identify %s as an executable file: %v", path, err))
}
if existingPath, ok := v.SeenPlugins[binName]; ok {
errors = append(errors, fmt.Errorf("warning: %s is overshadowed by a similarly named plugin: %s", path, existingPath))
} else {
v.SeenPlugins[binName] = path
}
cmd, _, err := v.Root.Find(cmdPath)
if err == nil {
errors = append(errors, fmt.Errorf("warning: %s overwrites existing command: %q", binName, cmd.CommandPath()))
}
return errors
}
// Private functions
func isExecutable(fullPath string) (bool, error) {
info, err := os.Stat(fullPath)
if err != nil {
return false, err
}
if runtime.GOOS == "windows" {
fileExt := strings.ToLower(filepath.Ext(fullPath))
switch fileExt {
case ".bat", ".cmd", ".com", ".exe", ".ps1":
return true, nil
}
return false, nil
}
if m := info.Mode(); !m.IsDir() && m&0111 != 0 {
return true, nil
}
return false, nil
}