github.com/canthefason/helm@v2.2.1-0.20170221172616-16b043b8d505+incompatible/cmd/helm/init.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors All rights reserved. 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 18 19 import ( 20 "errors" 21 "fmt" 22 "io" 23 "os" 24 25 "github.com/spf13/cobra" 26 kerrors "k8s.io/kubernetes/pkg/api/errors" 27 "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" 28 29 "k8s.io/helm/cmd/helm/helmpath" 30 "k8s.io/helm/cmd/helm/installer" 31 "k8s.io/helm/pkg/repo" 32 ) 33 34 const initDesc = ` 35 This command installs Tiller (the helm server side component) onto your 36 Kubernetes Cluster and sets up local configuration in $HELM_HOME (default ~/.helm/) 37 38 As with the rest of the Helm commands, 'helm init' discovers Kubernetes clusters 39 by reading $KUBECONFIG (default '~/.kube/config') and using the default context. 40 41 To set up just a local environment, use '--client-only'. That will configure 42 $HELM_HOME, but not attempt to connect to a remote cluster and install the Tiller 43 deployment. 44 45 When installing Tiller, 'helm init' will attempt to install the latest released 46 version. You can specify an alternative image with '--tiller-image'. For those 47 frequently working on the latest code, the flag '--canary-image' will install 48 the latest pre-release version of Tiller (e.g. the HEAD commit in the GitHub 49 repository on the master branch). 50 51 To dump a manifest containing the Tiller deployment YAML, combine the 52 '--dry-run' and '--debug' flags. 53 ` 54 55 const ( 56 stableRepository = "stable" 57 localRepository = "local" 58 stableRepositoryURL = "https://kubernetes-charts.storage.googleapis.com" 59 // This is the IPv4 loopback, not localhost, because we have to force IPv4 60 // for Dockerized Helm: https://github.com/kubernetes/helm/issues/1410 61 localRepositoryURL = "http://127.0.0.1:8879/charts" 62 ) 63 64 type initCmd struct { 65 image string 66 clientOnly bool 67 canary bool 68 upgrade bool 69 namespace string 70 dryRun bool 71 out io.Writer 72 home helmpath.Home 73 kubeClient internalclientset.Interface 74 } 75 76 func newInitCmd(out io.Writer) *cobra.Command { 77 i := &initCmd{ 78 out: out, 79 } 80 81 cmd := &cobra.Command{ 82 Use: "init", 83 Short: "initialize Helm on both client and server", 84 Long: initDesc, 85 RunE: func(cmd *cobra.Command, args []string) error { 86 if len(args) != 0 { 87 return errors.New("This command does not accept arguments") 88 } 89 i.namespace = tillerNamespace 90 i.home = helmpath.Home(homePath()) 91 return i.run() 92 }, 93 } 94 95 f := cmd.Flags() 96 f.StringVarP(&i.image, "tiller-image", "i", "", "override tiller image") 97 f.BoolVar(&i.canary, "canary-image", false, "use the canary tiller image") 98 f.BoolVar(&i.upgrade, "upgrade", false, "upgrade if tiller is already installed") 99 f.BoolVarP(&i.clientOnly, "client-only", "c", false, "if set does not install tiller") 100 f.BoolVar(&i.dryRun, "dry-run", false, "do not install local or remote") 101 102 return cmd 103 } 104 105 // runInit initializes local config and installs tiller to Kubernetes Cluster 106 func (i *initCmd) run() error { 107 if flagDebug { 108 dm, err := installer.DeploymentManifest(i.namespace, i.image, i.canary) 109 if err != nil { 110 return err 111 } 112 fm := fmt.Sprintf("apiVersion: extensions/v1beta1\nkind: Deployment\n%s", dm) 113 fmt.Fprintln(i.out, fm) 114 115 sm, err := installer.ServiceManifest(i.namespace) 116 if err != nil { 117 return err 118 } 119 fm = fmt.Sprintf("apiVersion: v1\nkind: Service\n%s", sm) 120 fmt.Fprintln(i.out, fm) 121 } 122 123 if i.dryRun { 124 return nil 125 } 126 127 if err := ensureDirectories(i.home, i.out); err != nil { 128 return err 129 } 130 if err := ensureDefaultRepos(i.home, i.out); err != nil { 131 return err 132 } 133 if err := ensureRepoFileFormat(i.home.RepositoryFile(), i.out); err != nil { 134 return err 135 } 136 fmt.Fprintf(i.out, "$HELM_HOME has been configured at %s.\n", helmHome) 137 138 if !i.clientOnly { 139 if i.kubeClient == nil { 140 _, c, err := getKubeClient(kubeContext) 141 if err != nil { 142 return fmt.Errorf("could not get kubernetes client: %s", err) 143 } 144 i.kubeClient = c 145 } 146 if err := installer.Install(i.kubeClient, i.namespace, i.image, i.canary, flagDebug); err != nil { 147 if !kerrors.IsAlreadyExists(err) { 148 return fmt.Errorf("error installing: %s", err) 149 } 150 if i.upgrade { 151 if err := installer.Upgrade(i.kubeClient, i.namespace, i.image, i.canary); err != nil { 152 return fmt.Errorf("error when upgrading: %s", err) 153 } 154 fmt.Fprintln(i.out, "\nTiller (the helm server side component) has been upgraded to the current version.") 155 } else { 156 fmt.Fprintln(i.out, "Warning: Tiller is already installed in the cluster.\n"+ 157 "(Use --client-only to suppress this message, or --upgrade to upgrade Tiller to the current version.)") 158 } 159 } else { 160 fmt.Fprintln(i.out, "\nTiller (the helm server side component) has been installed into your Kubernetes Cluster.") 161 } 162 } else { 163 fmt.Fprintln(i.out, "Not installing tiller due to 'client-only' flag having been set") 164 } 165 166 fmt.Fprintln(i.out, "Happy Helming!") 167 return nil 168 } 169 170 // ensureDirectories checks to see if $HELM_HOME exists 171 // 172 // If $HELM_HOME does not exist, this function will create it. 173 func ensureDirectories(home helmpath.Home, out io.Writer) error { 174 configDirectories := []string{ 175 home.String(), 176 home.Repository(), 177 home.Cache(), 178 home.LocalRepository(), 179 home.Plugins(), 180 home.Starters(), 181 } 182 for _, p := range configDirectories { 183 if fi, err := os.Stat(p); err != nil { 184 fmt.Fprintf(out, "Creating %s \n", p) 185 if err := os.MkdirAll(p, 0755); err != nil { 186 return fmt.Errorf("Could not create %s: %s", p, err) 187 } 188 } else if !fi.IsDir() { 189 return fmt.Errorf("%s must be a directory", p) 190 } 191 } 192 193 return nil 194 } 195 196 func ensureDefaultRepos(home helmpath.Home, out io.Writer) error { 197 repoFile := home.RepositoryFile() 198 if fi, err := os.Stat(repoFile); err != nil { 199 fmt.Fprintf(out, "Creating %s \n", repoFile) 200 f := repo.NewRepoFile() 201 sr, err := initStableRepo(home.CacheIndex(stableRepository)) 202 if err != nil { 203 return err 204 } 205 lr, err := initLocalRepo(home.LocalRepository(localRepoIndexFilePath), home.CacheIndex("local")) 206 if err != nil { 207 return err 208 } 209 f.Add(sr) 210 f.Add(lr) 211 if err := f.WriteFile(repoFile, 0644); err != nil { 212 return err 213 } 214 } else if fi.IsDir() { 215 return fmt.Errorf("%s must be a file, not a directory", repoFile) 216 } 217 return nil 218 } 219 220 func initStableRepo(cacheFile string) (*repo.Entry, error) { 221 c := repo.Entry{ 222 Name: stableRepository, 223 URL: stableRepositoryURL, 224 Cache: cacheFile, 225 } 226 r, err := repo.NewChartRepository(&c) 227 if err != nil { 228 return nil, err 229 } 230 231 // In this case, the cacheFile is always absolute. So passing empty string 232 // is safe. 233 if err := r.DownloadIndexFile(""); err != nil { 234 return nil, fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", stableRepositoryURL, err.Error()) 235 } 236 237 return &c, nil 238 } 239 240 func initLocalRepo(indexFile, cacheFile string) (*repo.Entry, error) { 241 if fi, err := os.Stat(indexFile); err != nil { 242 i := repo.NewIndexFile() 243 if err := i.WriteFile(indexFile, 0644); err != nil { 244 return nil, err 245 } 246 247 //TODO: take this out and replace with helm update functionality 248 os.Symlink(indexFile, cacheFile) 249 } else if fi.IsDir() { 250 return nil, fmt.Errorf("%s must be a file, not a directory", indexFile) 251 } 252 253 return &repo.Entry{ 254 Name: localRepository, 255 URL: localRepositoryURL, 256 Cache: cacheFile, 257 }, nil 258 } 259 260 func ensureRepoFileFormat(file string, out io.Writer) error { 261 r, err := repo.LoadRepositoriesFile(file) 262 if err == repo.ErrRepoOutOfDate { 263 fmt.Fprintln(out, "Updating repository file format...") 264 if err := r.WriteFile(file, 0644); err != nil { 265 return err 266 } 267 } 268 269 return nil 270 }