github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/actor/pluginaction/install.go (about) 1 package pluginaction 2 3 import ( 4 "io/ioutil" 5 "os" 6 "path/filepath" 7 "sort" 8 "strings" 9 10 "code.cloudfoundry.org/cli/actor/actionerror" 11 "code.cloudfoundry.org/cli/api/plugin" 12 "code.cloudfoundry.org/cli/util/configv3" 13 "code.cloudfoundry.org/cli/util/generic" 14 "code.cloudfoundry.org/gofileutils/fileutils" 15 ) 16 17 //go:generate counterfeiter . PluginMetadata 18 19 type PluginMetadata interface { 20 GetMetadata(pluginPath string) (configv3.Plugin, error) 21 } 22 23 //go:generate counterfeiter . CommandList 24 25 type CommandList interface { 26 HasCommand(string) bool 27 HasAlias(string) bool 28 } 29 30 // CreateExecutableCopy makes a temporary copy of a plugin binary and makes it 31 // executable. 32 // 33 // config.PluginHome() + /temp is used as the temp dir instead of the system 34 // temp for security reasons. 35 func (actor Actor) CreateExecutableCopy(path string, tempPluginDir string) (string, error) { 36 tempFile, err := makeTempFile(tempPluginDir) 37 if err != nil { 38 return "", err 39 } 40 41 // add '.exe' to the temp file if on Windows 42 executablePath := generic.ExecutableFilename(tempFile.Name()) 43 err = os.Rename(tempFile.Name(), executablePath) 44 if err != nil { 45 return "", err 46 } 47 48 err = fileutils.CopyPathToPath(path, executablePath) 49 if err != nil { 50 return "", err 51 } 52 53 err = os.Chmod(executablePath, 0700) 54 if err != nil { 55 return "", err 56 } 57 58 return executablePath, nil 59 } 60 61 // DownloadExecutableBinaryFromURL fetches a plugin binary from the specified 62 // URL, if it exists. 63 func (actor Actor) DownloadExecutableBinaryFromURL(pluginURL string, tempPluginDir string, proxyReader plugin.ProxyReader) (string, error) { 64 tempFile, err := makeTempFile(tempPluginDir) 65 if err != nil { 66 return "", err 67 } 68 69 err = actor.client.DownloadPlugin(pluginURL, tempFile.Name(), proxyReader) 70 if err != nil { 71 return "", err 72 } 73 74 return tempFile.Name(), nil 75 } 76 77 // FileExists returns true if the file exists. It returns false if the file 78 // doesn't exist or there is an error checking. 79 func (actor Actor) FileExists(path string) bool { 80 _, err := os.Stat(path) 81 return err == nil 82 } 83 84 func (actor Actor) GetAndValidatePlugin(pluginMetadata PluginMetadata, commandList CommandList, path string) (configv3.Plugin, error) { 85 plugin, err := pluginMetadata.GetMetadata(path) 86 if err != nil || plugin.Name == "" || len(plugin.Commands) == 0 { 87 return configv3.Plugin{}, actionerror.PluginInvalidError{Err: err} 88 } 89 90 installedPlugins := actor.config.Plugins() 91 92 conflictingNames := []string{} 93 conflictingAliases := []string{} 94 95 for _, command := range plugin.Commands { 96 if commandList.HasCommand(command.Name) || commandList.HasAlias(command.Name) { 97 conflictingNames = append(conflictingNames, command.Name) 98 } 99 100 if commandList.HasAlias(command.Alias) || commandList.HasCommand(command.Alias) { 101 conflictingAliases = append(conflictingAliases, command.Alias) 102 } 103 104 for _, installedPlugin := range installedPlugins { 105 // we do not error if a plugins commands conflict with previous 106 // versions of the same plugin 107 if plugin.Name == installedPlugin.Name { 108 continue 109 } 110 111 for _, installedCommand := range installedPlugin.Commands { 112 if command.Name == installedCommand.Name || command.Name == installedCommand.Alias { 113 conflictingNames = append(conflictingNames, command.Name) 114 } 115 116 if command.Alias != "" && 117 (command.Alias == installedCommand.Alias || command.Alias == installedCommand.Name) { 118 conflictingAliases = append(conflictingAliases, command.Alias) 119 } 120 } 121 } 122 } 123 124 if len(conflictingNames) > 0 || len(conflictingAliases) > 0 { 125 sort.Slice(conflictingNames, func(i, j int) bool { 126 return strings.ToLower(conflictingNames[i]) < strings.ToLower(conflictingNames[j]) 127 }) 128 129 sort.Slice(conflictingAliases, func(i, j int) bool { 130 return strings.ToLower(conflictingAliases[i]) < strings.ToLower(conflictingAliases[j]) 131 }) 132 133 return configv3.Plugin{}, actionerror.PluginCommandsConflictError{ 134 PluginName: plugin.Name, 135 PluginVersion: plugin.Version.String(), 136 CommandNames: conflictingNames, 137 CommandAliases: conflictingAliases, 138 } 139 } 140 141 return plugin, nil 142 } 143 144 func (actor Actor) InstallPluginFromPath(path string, plugin configv3.Plugin) error { 145 installPath := generic.ExecutableFilename(filepath.Join(actor.config.PluginHome(), plugin.Name)) 146 err := fileutils.CopyPathToPath(path, installPath) 147 if err != nil { 148 return err 149 } 150 // rwxr-xr-x so that multiple users can share the same $CF_PLUGIN_HOME 151 err = os.Chmod(installPath, 0755) 152 if err != nil { 153 return err 154 } 155 156 plugin.Location = installPath 157 158 actor.config.AddPlugin(plugin) 159 160 err = actor.config.WritePluginConfig() 161 if err != nil { 162 return err 163 } 164 165 return nil 166 } 167 168 func makeTempFile(tempDir string) (*os.File, error) { 169 tempFile, err := ioutil.TempFile(tempDir, "") 170 if err != nil { 171 return nil, err 172 } 173 174 err = tempFile.Close() 175 if err != nil { 176 return nil, err 177 } 178 179 return tempFile, nil 180 }