github.com/sdbaiguanghe/helm@v2.16.7+incompatible/cmd/helm/helm.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main // import "k8s.io/helm/cmd/helm" 18 19 import ( 20 "fmt" 21 "io/ioutil" 22 "log" 23 "os" 24 "strings" 25 26 "github.com/spf13/cobra" 27 "google.golang.org/grpc/grpclog" 28 "google.golang.org/grpc/status" 29 "k8s.io/client-go/kubernetes" 30 "k8s.io/client-go/rest" 31 32 // Import to initialize client auth plugins. 33 _ "k8s.io/client-go/plugin/pkg/client/auth" 34 35 "k8s.io/helm/pkg/helm" 36 helm_env "k8s.io/helm/pkg/helm/environment" 37 "k8s.io/helm/pkg/helm/portforwarder" 38 "k8s.io/helm/pkg/kube" 39 "k8s.io/helm/pkg/tlsutil" 40 ) 41 42 const ( 43 bashCompletionFunc = ` 44 __helm_override_flag_list=(--kubeconfig --kube-context --host --tiller-namespace --home) 45 __helm_override_flags() 46 { 47 local ${__helm_override_flag_list[*]##*-} two_word_of of var 48 for w in "${words[@]}"; do 49 if [ -n "${two_word_of}" ]; then 50 eval "${two_word_of##*-}=\"${two_word_of}=\${w}\"" 51 two_word_of= 52 continue 53 fi 54 for of in "${__helm_override_flag_list[@]}"; do 55 case "${w}" in 56 ${of}=*) 57 eval "${of##*-}=\"${w}\"" 58 ;; 59 ${of}) 60 two_word_of="${of}" 61 ;; 62 esac 63 done 64 done 65 for var in "${__helm_override_flag_list[@]##*-}"; do 66 if eval "test -n \"\$${var}\""; then 67 eval "echo \${${var}}" 68 fi 69 done 70 } 71 72 __helm_binary_name() 73 { 74 local helm_binary 75 helm_binary="${words[0]}" 76 __helm_debug "${FUNCNAME[0]}: helm_binary is ${helm_binary}" 77 echo ${helm_binary} 78 } 79 80 __helm_list_releases() 81 { 82 __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" 83 local out filter 84 # Use ^ to map from the start of the release name 85 filter="^${words[c]}" 86 # Use eval in case helm_binary_name or __helm_override_flags contains a variable (e.g., $HOME/bin/h2) 87 if out=$(eval $(__helm_binary_name) list $(__helm_override_flags) -a -q -m 1000 ${filter} 2>/dev/null); then 88 COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) 89 fi 90 } 91 92 __helm_list_repos() 93 { 94 __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" 95 local out oflags 96 oflags=$(__helm_override_flags) 97 __helm_debug "${FUNCNAME[0]}: __helm_override_flags are ${oflags}" 98 # Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h2) 99 if out=$(eval $(__helm_binary_name) repo list ${oflags} 2>/dev/null | tail +2 | cut -f1); then 100 COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) 101 fi 102 } 103 104 __helm_list_plugins() 105 { 106 __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" 107 local out oflags 108 oflags=$(__helm_override_flags) 109 __helm_debug "${FUNCNAME[0]}: __helm_override_flags are ${oflags}" 110 # Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h2) 111 if out=$(eval $(__helm_binary_name) plugin list ${oflags} 2>/dev/null | tail +2 | cut -f1); then 112 COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) 113 fi 114 } 115 116 __helm_custom_func() 117 { 118 __helm_debug "${FUNCNAME[0]}: c is $c words[@] is ${words[@]}" 119 case ${last_command} in 120 helm_delete | helm_history | helm_status | helm_test |\ 121 helm_upgrade | helm_rollback | helm_get_*) 122 __helm_list_releases 123 return 124 ;; 125 helm_repo_remove | helm_repo_update) 126 __helm_list_repos 127 return 128 ;; 129 helm_plugin_remove | helm_plugin_update) 130 __helm_list_plugins 131 return 132 ;; 133 *) 134 ;; 135 esac 136 } 137 ` 138 ) 139 140 var ( 141 tillerTunnel *kube.Tunnel 142 settings helm_env.EnvSettings 143 ) 144 145 var globalUsage = `The Kubernetes package manager 146 147 To begin working with Helm, run the 'helm init' command: 148 149 $ helm init 150 151 This will install Tiller to your running Kubernetes cluster. 152 It will also set up any necessary local configuration. 153 154 Common actions from this point include: 155 156 - helm search: Search for charts 157 - helm fetch: Download a chart to your local directory to view 158 - helm install: Upload the chart to Kubernetes 159 - helm list: List releases of charts 160 161 Environment: 162 163 - $HELM_HOME: Set an alternative location for Helm files. By default, these are stored in ~/.helm 164 - $HELM_HOST: Set an alternative Tiller host. The format is host:port 165 - $HELM_NO_PLUGINS: Disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. 166 - $TILLER_NAMESPACE: Set an alternative Tiller namespace (default "kube-system") 167 - $KUBECONFIG: Set an alternative Kubernetes configuration file (default "~/.kube/config") 168 - $HELM_TLS_CA_CERT: Path to TLS CA certificate used to verify the Helm client and Tiller server certificates (default "$HELM_HOME/ca.pem") 169 - $HELM_TLS_CERT: Path to TLS client certificate file for authenticating to Tiller (default "$HELM_HOME/cert.pem") 170 - $HELM_TLS_KEY: Path to TLS client key file for authenticating to Tiller (default "$HELM_HOME/key.pem") 171 - $HELM_TLS_ENABLE: Enable TLS connection between Helm and Tiller (default "false") 172 - $HELM_TLS_VERIFY: Enable TLS connection between Helm and Tiller and verify Tiller server certificate (default "false") 173 - $HELM_TLS_HOSTNAME: The hostname or IP address used to verify the Tiller server certificate (default "127.0.0.1") 174 - $HELM_KEY_PASSPHRASE: Set HELM_KEY_PASSPHRASE to the passphrase of your PGP private key. If set, you will not be prompted for the passphrase while signing helm charts 175 176 ` 177 178 func newRootCmd(args []string) *cobra.Command { 179 cmd := &cobra.Command{ 180 Use: "helm", 181 Short: "The Helm package manager for Kubernetes.", 182 Long: globalUsage, 183 SilenceUsage: true, 184 PersistentPreRun: func(*cobra.Command, []string) { 185 if settings.TLSCaCertFile == helm_env.DefaultTLSCaCert || settings.TLSCaCertFile == "" { 186 settings.TLSCaCertFile = settings.Home.TLSCaCert() 187 } else { 188 settings.TLSCaCertFile = os.ExpandEnv(settings.TLSCaCertFile) 189 } 190 if settings.TLSCertFile == helm_env.DefaultTLSCert || settings.TLSCertFile == "" { 191 settings.TLSCertFile = settings.Home.TLSCert() 192 } else { 193 settings.TLSCertFile = os.ExpandEnv(settings.TLSCertFile) 194 } 195 if settings.TLSKeyFile == helm_env.DefaultTLSKeyFile || settings.TLSKeyFile == "" { 196 settings.TLSKeyFile = settings.Home.TLSKey() 197 } else { 198 settings.TLSKeyFile = os.ExpandEnv(settings.TLSKeyFile) 199 } 200 }, 201 PersistentPostRun: func(*cobra.Command, []string) { 202 teardown() 203 }, 204 BashCompletionFunction: bashCompletionFunc, 205 } 206 flags := cmd.PersistentFlags() 207 208 settings.AddFlags(flags) 209 210 out := cmd.OutOrStdout() 211 212 cmd.AddCommand( 213 // chart commands 214 newCreateCmd(out), 215 newDependencyCmd(out), 216 newFetchCmd(out), 217 newInspectCmd(out), 218 newLintCmd(out), 219 newPackageCmd(out), 220 newRepoCmd(out), 221 newSearchCmd(out), 222 newServeCmd(out), 223 newVerifyCmd(out), 224 225 // release commands 226 newDeleteCmd(nil, out), 227 newGetCmd(nil, out), 228 newHistoryCmd(nil, out), 229 newInstallCmd(nil, out), 230 newListCmd(nil, out), 231 newRollbackCmd(nil, out), 232 newStatusCmd(nil, out), 233 newUpgradeCmd(nil, out), 234 235 newReleaseTestCmd(nil, out), 236 newResetCmd(nil, out), 237 newVersionCmd(nil, out), 238 239 newCompletionCmd(out), 240 newHomeCmd(out), 241 newInitCmd(out), 242 newPluginCmd(out), 243 newTemplateCmd(out), 244 245 // Hidden documentation generator command: 'helm docs' 246 newDocsCmd(out), 247 248 // Deprecated 249 markDeprecated(newRepoUpdateCmd(out), "Use 'helm repo update'\n"), 250 ) 251 252 flags.Parse(args) 253 254 // set defaults from environment 255 settings.Init(flags) 256 257 // Find and add plugins 258 loadPlugins(cmd, out) 259 260 return cmd 261 } 262 263 func init() { 264 // Tell gRPC not to log to console. 265 grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags)) 266 } 267 268 func main() { 269 cmd := newRootCmd(os.Args[1:]) 270 if err := cmd.Execute(); err != nil { 271 switch e := err.(type) { 272 case pluginError: 273 os.Exit(e.code) 274 default: 275 os.Exit(1) 276 } 277 } 278 } 279 280 func markDeprecated(cmd *cobra.Command, notice string) *cobra.Command { 281 cmd.Deprecated = notice 282 return cmd 283 } 284 285 func setupConnection() error { 286 if settings.TillerHost == "" { 287 config, client, err := getKubeClient(settings.KubeContext, settings.KubeConfig) 288 if err != nil { 289 return err 290 } 291 292 tillerTunnel, err = portforwarder.New(settings.TillerNamespace, client, config) 293 if err != nil { 294 return err 295 } 296 297 settings.TillerHost = fmt.Sprintf("127.0.0.1:%d", tillerTunnel.Local) 298 debug("Created tunnel using local port: '%d'\n", tillerTunnel.Local) 299 } 300 301 // Set up the gRPC config. 302 debug("SERVER: %q\n", settings.TillerHost) 303 304 // Plugin support. 305 return nil 306 } 307 308 func teardown() { 309 if tillerTunnel != nil { 310 tillerTunnel.Close() 311 } 312 } 313 314 func checkArgsLength(argsReceived int, requiredArgs ...string) error { 315 expectedNum := len(requiredArgs) 316 if argsReceived != expectedNum { 317 arg := "arguments" 318 if expectedNum == 1 { 319 arg = "argument" 320 } 321 return fmt.Errorf("This command needs %v %s: %s", expectedNum, arg, strings.Join(requiredArgs, ", ")) 322 } 323 return nil 324 } 325 326 // prettyError unwraps or rewrites certain errors to make them more user-friendly. 327 func prettyError(err error) error { 328 // Add this check can prevent the object creation if err is nil. 329 if err == nil { 330 return nil 331 } 332 // If it's grpc's error, make it more user-friendly. 333 if s, ok := status.FromError(err); ok { 334 return fmt.Errorf(s.Message()) 335 } 336 // Else return the original error. 337 return err 338 } 339 340 // configForContext creates a Kubernetes REST client configuration for a given kubeconfig context. 341 func configForContext(context string, kubeconfig string) (*rest.Config, error) { 342 config, err := kube.GetConfig(context, kubeconfig).ClientConfig() 343 if err != nil { 344 return nil, fmt.Errorf("could not get Kubernetes config for context %q: %s", context, err) 345 } 346 return config, nil 347 } 348 349 // getKubeClient creates a Kubernetes config and client for a given kubeconfig context. 350 func getKubeClient(context string, kubeconfig string) (*rest.Config, kubernetes.Interface, error) { 351 config, err := configForContext(context, kubeconfig) 352 if err != nil { 353 return nil, nil, err 354 } 355 client, err := kubernetes.NewForConfig(config) 356 if err != nil { 357 return nil, nil, fmt.Errorf("could not get Kubernetes client: %s", err) 358 } 359 return config, client, nil 360 } 361 362 // ensureHelmClient returns a new helm client impl. if h is not nil. 363 func ensureHelmClient(h helm.Interface) helm.Interface { 364 if h != nil { 365 return h 366 } 367 return newClient() 368 } 369 370 func newClient() helm.Interface { 371 options := []helm.Option{helm.Host(settings.TillerHost), helm.ConnectTimeout(settings.TillerConnectionTimeout)} 372 373 if settings.TLSVerify || settings.TLSEnable { 374 debug("Host=%q, Key=%q, Cert=%q, CA=%q\n", settings.TLSServerName, settings.TLSKeyFile, settings.TLSCertFile, settings.TLSCaCertFile) 375 tlsopts := tlsutil.Options{ 376 ServerName: settings.TLSServerName, 377 KeyFile: settings.TLSKeyFile, 378 CertFile: settings.TLSCertFile, 379 InsecureSkipVerify: true, 380 } 381 if settings.TLSVerify { 382 tlsopts.CaCertFile = settings.TLSCaCertFile 383 tlsopts.InsecureSkipVerify = false 384 } 385 tlscfg, err := tlsutil.ClientConfig(tlsopts) 386 if err != nil { 387 fmt.Fprintln(os.Stderr, err) 388 os.Exit(2) 389 } 390 options = append(options, helm.WithTLS(tlscfg)) 391 } 392 return helm.NewClient(options...) 393 }