github.com/asifdxtreme/cli@v6.1.3-0.20150123051144-9ead8700b4ae+incompatible/cf/commands/plugin/install_plugin.go (about) 1 package plugin 2 3 import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "path/filepath" 8 9 "github.com/cloudfoundry/cli/cf/command" 10 "github.com/cloudfoundry/cli/cf/command_metadata" 11 "github.com/cloudfoundry/cli/cf/configuration/plugin_config" 12 . "github.com/cloudfoundry/cli/cf/i18n" 13 "github.com/cloudfoundry/cli/cf/requirements" 14 "github.com/cloudfoundry/cli/cf/terminal" 15 "github.com/cloudfoundry/cli/fileutils" 16 "github.com/cloudfoundry/cli/plugin" 17 "github.com/cloudfoundry/cli/plugin/rpc" 18 "github.com/codegangsta/cli" 19 ) 20 21 type PluginInstall struct { 22 ui terminal.UI 23 config plugin_config.PluginConfiguration 24 coreCmds map[string]command.Command 25 } 26 27 func NewPluginInstall(ui terminal.UI, config plugin_config.PluginConfiguration, coreCmds map[string]command.Command) *PluginInstall { 28 return &PluginInstall{ 29 ui: ui, 30 config: config, 31 coreCmds: coreCmds, 32 } 33 } 34 35 func (cmd *PluginInstall) Metadata() command_metadata.CommandMetadata { 36 return command_metadata.CommandMetadata{ 37 Name: "install-plugin", 38 Description: T("Install the plugin defined in command argument"), 39 Usage: T(`CF_NAME install-plugin URL or LOCAL-PATH/TO/PLUGIN 40 41 EXAMPLE: 42 cf install-plugin https://github.com/cf-experimental/plugin-foobar 43 cf install-plugin ~/Downloads/plugin-foobar 44 `), 45 } 46 } 47 48 func (cmd *PluginInstall) GetRequirements(_ requirements.Factory, c *cli.Context) (req []requirements.Requirement, err error) { 49 if len(c.Args()) != 1 { 50 cmd.ui.FailWithUsage(c) 51 } 52 53 return 54 } 55 56 func (cmd *PluginInstall) Run(c *cli.Context) { 57 var downloader fileutils.Downloader 58 59 pluginSourceFilepath := c.Args()[0] 60 61 if filepath.Dir(pluginSourceFilepath) == "." { 62 pluginSourceFilepath = "./" + filepath.Clean(pluginSourceFilepath) 63 } 64 65 cmd.ui.Say(fmt.Sprintf(T("Installing plugin {{.PluginPath}}...", map[string]interface{}{"PluginPath": pluginSourceFilepath}))) 66 67 if !cmd.ensureCandidatePluginBinaryExistsAtGivenPath(pluginSourceFilepath) { 68 cmd.ui.Say("") 69 cmd.ui.Say(T("File not found locally, attempting to download binary file from internet ...")) 70 pluginSourceFilepath = cmd.tryDownloadPluginBinaryfromGivenPath(pluginSourceFilepath, downloader) 71 } 72 73 _, pluginExecutableName := filepath.Split(pluginSourceFilepath) 74 75 pluginDestinationFilepath := filepath.Join(cmd.config.GetPluginPath(), pluginExecutableName) 76 77 cmd.ensurePluginBinaryWithSameFileNameDoesNotAlreadyExist(pluginDestinationFilepath, pluginExecutableName) 78 79 pluginMetadata := cmd.runBinaryAndObtainPluginMetadata(pluginSourceFilepath) 80 81 cmd.ensurePluginIsSafeForInstallation(pluginMetadata, pluginDestinationFilepath, pluginSourceFilepath) 82 83 cmd.installPlugin(pluginMetadata, pluginDestinationFilepath, pluginSourceFilepath) 84 85 if downloader != nil { 86 err := downloader.RemoveFile() 87 if err != nil { 88 cmd.ui.Say(T("Problem removing downloaded binary in temp directory: ") + err.Error()) 89 } 90 } 91 92 cmd.ui.Ok() 93 cmd.ui.Say(fmt.Sprintf(T("Plugin {{.PluginName}} v{{.Version}} successfully installed.", map[string]interface{}{"PluginName": pluginMetadata.Name, "Version": fmt.Sprintf("%d.%d.%d", pluginMetadata.Version.Major, pluginMetadata.Version.Minor, pluginMetadata.Version.Build)}))) 94 } 95 96 func (cmd *PluginInstall) ensurePluginBinaryWithSameFileNameDoesNotAlreadyExist(pluginDestinationFilepath, pluginExecutableName string) { 97 _, err := os.Stat(pluginDestinationFilepath) 98 if err == nil || os.IsExist(err) { 99 cmd.ui.Failed(fmt.Sprintf(T("The file {{.PluginExecutableName}} already exists under the plugin directory.\n", 100 map[string]interface{}{ 101 "PluginExecutableName": pluginExecutableName, 102 }))) 103 } else if !os.IsNotExist(err) { 104 cmd.ui.Failed(fmt.Sprintf(T("Unexpected error has occurred:\n{{.Error}}", map[string]interface{}{"Error": err.Error()}))) 105 } 106 } 107 108 func (cmd *PluginInstall) ensurePluginIsSafeForInstallation(pluginMetadata *plugin.PluginMetadata, pluginDestinationFilepath string, pluginSourceFilepath string) { 109 plugins := cmd.config.Plugins() 110 if pluginMetadata.Name == "" { 111 cmd.ui.Failed(fmt.Sprintf(T("Unable to obtain plugin name for executable {{.Executable}}", map[string]interface{}{"Executable": pluginSourceFilepath}))) 112 } 113 114 if _, ok := plugins[pluginMetadata.Name]; ok { 115 cmd.ui.Failed(fmt.Sprintf(T("Plugin name {{.PluginName}} is already taken", map[string]interface{}{"PluginName": pluginMetadata.Name}))) 116 } 117 118 if pluginMetadata.Commands == nil { 119 cmd.ui.Failed(fmt.Sprintf(T("Error getting command list from plugin {{.FilePath}}", map[string]interface{}{"FilePath": pluginSourceFilepath}))) 120 } 121 122 shortNames := cmd.getShortNames() 123 124 for _, pluginCmd := range pluginMetadata.Commands { 125 //check for command conflicting core commands/alias 126 if _, exists := cmd.coreCmds[pluginCmd.Name]; exists || shortNames[pluginCmd.Name] || pluginCmd.Name == "help" { 127 cmd.ui.Failed(fmt.Sprintf(T("Command `{{.Command}}` in the plugin being installed is a native CF command/alias. Rename the `{{.Command}}` command in the plugin being installed in order to enable its installation and use.", 128 map[string]interface{}{"Command": pluginCmd.Name}))) 129 } 130 131 //check for alias conflicting core command/alias 132 if _, exists := cmd.coreCmds[pluginCmd.Alias]; exists || shortNames[pluginCmd.Alias] || pluginCmd.Alias == "help" { 133 cmd.ui.Failed(fmt.Sprintf(T("Alias `{{.Command}}` in the plugin being installed is a native CF command/alias. Rename the `{{.Command}}` command in the plugin being installed in order to enable its installation and use.", 134 map[string]interface{}{"Command": pluginCmd.Alias}))) 135 } 136 137 for installedPluginName, installedPlugin := range plugins { 138 for _, installedPluginCmd := range installedPlugin.Commands { 139 140 //check for command conflicting other plugin commands/alias 141 if installedPluginCmd.Name == pluginCmd.Name || installedPluginCmd.Alias == pluginCmd.Name { 142 cmd.ui.Failed(fmt.Sprintf(T("Command `{{.Command}}` is a command/alias in plugin '{{.PluginName}}'. You could try uninstalling plugin '{{.PluginName}}' and then install this plugin in order to invoke the `{{.Command}}` command. However, you should first fully understand the impact of uninstalling the existing '{{.PluginName}}' plugin.", 143 map[string]interface{}{"Command": pluginCmd.Name, "PluginName": installedPluginName}))) 144 } 145 146 //check for alias conflicting other plugin commands/alias 147 if pluginCmd.Alias != "" && (installedPluginCmd.Name == pluginCmd.Alias || installedPluginCmd.Alias == pluginCmd.Alias) { 148 cmd.ui.Failed(fmt.Sprintf(T("Alias `{{.Command}}` is a command/alias in plugin '{{.PluginName}}'. You could try uninstalling plugin '{{.PluginName}}' and then install this plugin in order to invoke the `{{.Command}}` command. However, you should first fully understand the impact of uninstalling the existing '{{.PluginName}}' plugin.", 149 map[string]interface{}{"Command": pluginCmd.Alias, "PluginName": installedPluginName}))) 150 } 151 } 152 } 153 } 154 155 } 156 157 func (cmd *PluginInstall) installPlugin(pluginMetadata *plugin.PluginMetadata, pluginDestinationFilepath, pluginSourceFilepath string) { 158 err := fileutils.CopyFile(pluginDestinationFilepath, pluginSourceFilepath) 159 if err != nil { 160 cmd.ui.Failed(fmt.Sprintf(T("Could not copy plugin binary: \n{{.Error}}", map[string]interface{}{"Error": err.Error()}))) 161 } 162 163 configMetadata := plugin_config.PluginMetadata{ 164 Location: pluginDestinationFilepath, 165 Version: pluginMetadata.Version, 166 Commands: pluginMetadata.Commands, 167 } 168 169 cmd.config.SetPlugin(pluginMetadata.Name, configMetadata) 170 } 171 172 func (cmd *PluginInstall) runBinaryAndObtainPluginMetadata(pluginSourceFilepath string) *plugin.PluginMetadata { 173 rpcService, err := rpc.NewRpcService(nil, nil, nil) 174 if err != nil { 175 cmd.ui.Failed(err.Error()) 176 } 177 178 err = rpcService.Start() 179 if err != nil { 180 cmd.ui.Failed(err.Error()) 181 } 182 defer rpcService.Stop() 183 184 cmd.runPluginBinary(pluginSourceFilepath, rpcService.Port()) 185 186 return rpcService.RpcCmd.PluginMetadata 187 } 188 189 func (cmd *PluginInstall) ensureCandidatePluginBinaryExistsAtGivenPath(pluginSourceFilepath string) bool { 190 _, err := os.Stat(pluginSourceFilepath) 191 if err != nil && os.IsNotExist(err) { 192 return false 193 } 194 return true 195 } 196 197 func (cmd *PluginInstall) tryDownloadPluginBinaryfromGivenPath(pluginSourceFilepath string, downloader fileutils.Downloader) string { 198 199 savePath := os.TempDir() 200 downloader = fileutils.NewDownloader(savePath) 201 size, filename, err := downloader.DownloadFile(pluginSourceFilepath) 202 203 if err != nil { 204 cmd.ui.Failed(fmt.Sprintf(T("Download attempt failed: {{.Error}}\n\nUnable to install, plugin is not available from local/internet.", map[string]interface{}{"Error": err.Error()}))) 205 } 206 207 cmd.ui.Say(fmt.Sprintf("%d "+T("bytes downloaded")+"...", size)) 208 209 executablePath := filepath.Join(savePath, filename) 210 os.Chmod(executablePath, 0700) 211 212 return executablePath 213 } 214 215 func (cmd *PluginInstall) getShortNames() map[string]bool { 216 shortNames := make(map[string]bool) 217 for _, singleCmd := range cmd.coreCmds { 218 metaData := singleCmd.Metadata() 219 if metaData.ShortName != "" { 220 shortNames[metaData.ShortName] = true 221 } 222 } 223 return shortNames 224 } 225 226 func (cmd *PluginInstall) runPluginBinary(location string, servicePort string) { 227 pluginInvocation := exec.Command(location, servicePort, "SendMetadata") 228 229 err := pluginInvocation.Run() 230 if err != nil { 231 cmd.ui.Failed(err.Error()) 232 } 233 }