github.com/replicatedhq/ship@v0.55.0/pkg/helm/init.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 /* 18 This file was edited by Replicated in 2018 to remove some functionality and to expose `helm init` as a function. 19 Among other things, clientOnly has been set as the default (and only) option, and the cli interface code has been removed. 20 */ 21 22 package helm 23 24 import ( 25 "bufio" 26 "bytes" 27 "encoding/json" 28 "fmt" 29 "io" 30 "os" 31 "path/filepath" 32 33 "github.com/pkg/errors" 34 "k8s.io/apimachinery/pkg/util/yaml" 35 "k8s.io/helm/cmd/helm/installer" 36 "k8s.io/helm/pkg/getter" 37 "k8s.io/helm/pkg/helm/environment" 38 "k8s.io/helm/pkg/helm/helmpath" 39 "k8s.io/helm/pkg/repo" 40 ) 41 42 // const initDesc = ` 43 // This command installs Tiller (the Helm server-side component) onto your 44 // Kubernetes Cluster and sets up local configuration in $HELM_HOME (default ~/.helm/). 45 // 46 // As with the rest of the Helm commands, 'helm init' discovers Kubernetes clusters 47 // by reading $KUBECONFIG (default '~/.kube/config') and using the default context. 48 // 49 // To set up just a local environment, use '--client-only'. That will configure 50 // $HELM_HOME, but not attempt to connect to a Kubernetes cluster and install the Tiller 51 // deployment. 52 // 53 // When installing Tiller, 'helm init' will attempt to install the latest released 54 // version. You can specify an alternative image with '--tiller-image'. For those 55 // frequently working on the latest code, the flag '--canary-image' will install 56 // the latest pre-release version of Tiller (e.g. the HEAD commit in the GitHub 57 // repository on the master branch). 58 // 59 // To dump a manifest containing the Tiller deployment YAML, combine the 60 // '--dry-run' and '--debug' flags. 61 // ` 62 63 const ( 64 stableRepository = "stable" 65 localRepository = "local" 66 localRepositoryIndexFile = "index.yaml" 67 ) 68 69 var ( 70 stableRepositoryURL = "https://charts.helm.sh/stable" 71 // This is the IPv4 loopback, not localhost, because we have to force IPv4 72 // for Dockerized Helm: https://github.com/kubernetes/helm/issues/1410 73 localRepositoryURL = "http://127.0.0.1:8879/charts" 74 ) 75 76 type initCmd struct { 77 skipRefresh bool // do not refresh (download) the local repository cache 78 out io.Writer 79 home helmpath.Home 80 opts installer.Options 81 } 82 83 func Init(home string) (string, error) { 84 var buf bytes.Buffer 85 bufWriter := bufio.NewWriter(&buf) 86 87 toInit := initCmd{ 88 out: bufWriter, 89 } 90 91 var path string 92 var err error 93 if home != "" { 94 path, err = filepath.Abs(home) 95 } else { 96 path, err = helmHome() 97 } 98 if err != nil { 99 return "", errors.Wrap(err, "unable to find home directory") 100 } 101 toInit.home = helmpath.Home(path) 102 103 err = toInit.run() 104 return buf.String(), err 105 } 106 107 // run initializes local config and installs Tiller to Kubernetes cluster. 108 func (i *initCmd) run() error { 109 110 writeYAMLManifests := func(manifests []string) error { 111 w := i.out 112 for _, manifest := range manifests { 113 if _, err := fmt.Fprintln(w, "---"); err != nil { 114 return err 115 } 116 117 if _, err := fmt.Fprintln(w, manifest); err != nil { 118 return err 119 } 120 } 121 122 // YAML ending document boundary marker 123 _, err := fmt.Fprintln(w, "...") 124 return err 125 } 126 if len(i.opts.Output) > 0 { 127 var manifests []string 128 var err error 129 if manifests, err = installer.TillerManifests(&i.opts); err != nil { 130 return err 131 } 132 switch i.opts.Output.String() { 133 case "json": 134 for _, manifest := range manifests { 135 var out bytes.Buffer 136 jsonb, err := yaml.ToJSON([]byte(manifest)) 137 if err != nil { 138 return err 139 } 140 buf := bytes.NewBuffer(jsonb) 141 if err := json.Indent(&out, buf.Bytes(), "", " "); err != nil { 142 return err 143 } 144 if _, err = i.out.Write(out.Bytes()); err != nil { 145 return err 146 } 147 fmt.Fprint(i.out, "\n") 148 } 149 return nil 150 case "yaml": 151 return writeYAMLManifests(manifests) 152 default: 153 return fmt.Errorf("unknown output format: %q", i.opts.Output) 154 } 155 } 156 if settings.Debug { 157 var manifests []string 158 var err error 159 160 // write Tiller manifests 161 if manifests, err = installer.TillerManifests(&i.opts); err != nil { 162 return err 163 } 164 165 if err = writeYAMLManifests(manifests); err != nil { 166 return err 167 } 168 } 169 170 if err := ensureDirectories(i.home, i.out); err != nil { 171 return err 172 } 173 if err := ensureDefaultRepos(i.home, i.out, i.skipRefresh); err != nil { 174 return err 175 } 176 if err := ensureRepoFileFormat(i.home.RepositoryFile(), i.out); err != nil { 177 return err 178 } 179 fmt.Fprintf(i.out, "$HELM_HOME has been configured at %s.\n", i.home) 180 181 fmt.Fprintln(i.out, "Happy Helming!") 182 return nil 183 } 184 185 // ensureDirectories checks to see if $HELM_HOME exists. 186 // 187 // If $HELM_HOME does not exist, this function will create it. 188 func ensureDirectories(home helmpath.Home, out io.Writer) error { 189 configDirectories := []string{ 190 home.String(), 191 home.Repository(), 192 home.Cache(), 193 home.LocalRepository(), 194 home.Plugins(), 195 home.Starters(), 196 home.Archive(), 197 } 198 for _, p := range configDirectories { 199 if fi, err := os.Stat(p); err != nil { 200 fmt.Fprintf(out, "Creating %s \n", p) 201 if err := os.MkdirAll(p, 0755); err != nil { 202 return fmt.Errorf("Could not create %s: %s", p, err) 203 } 204 } else if !fi.IsDir() { 205 return fmt.Errorf("%s must be a directory", p) 206 } 207 } 208 209 return nil 210 } 211 212 func ensureDefaultRepos(home helmpath.Home, out io.Writer, skipRefresh bool) error { 213 repoFile := home.RepositoryFile() 214 if fi, err := os.Stat(repoFile); err != nil { 215 fmt.Fprintf(out, "Creating %s \n", repoFile) 216 f := repo.NewRepoFile() 217 sr, err := initStableRepo(home.CacheIndex(stableRepository), out, skipRefresh, home) 218 if err != nil { 219 return err 220 } 221 lr, err := initLocalRepo(home.LocalRepository(localRepositoryIndexFile), home.CacheIndex("local"), out, home) 222 if err != nil { 223 return err 224 } 225 f.Add(sr) 226 f.Add(lr) 227 if err := f.WriteFile(repoFile, 0644); err != nil { 228 return err 229 } 230 } else if fi.IsDir() { 231 return fmt.Errorf("%s must be a file, not a directory", repoFile) 232 } 233 return nil 234 } 235 236 func initStableRepo(cacheFile string, out io.Writer, skipRefresh bool, home helmpath.Home) (*repo.Entry, error) { 237 fmt.Fprintf(out, "Adding %s repo with URL: %s \n", stableRepository, stableRepositoryURL) 238 c := repo.Entry{ 239 Name: stableRepository, 240 URL: stableRepositoryURL, 241 Cache: cacheFile, 242 } 243 r, err := repo.NewChartRepository(&c, getter.All(environment.EnvSettings{Home: home})) 244 if err != nil { 245 return nil, err 246 } 247 248 if skipRefresh { 249 return &c, nil 250 } 251 252 // In this case, the cacheFile is always absolute. So passing empty string 253 // is safe. 254 if err := r.DownloadIndexFile(""); err != nil { 255 return nil, fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", stableRepositoryURL, err.Error()) 256 } 257 258 return &c, nil 259 } 260 261 func initLocalRepo(indexFile, cacheFile string, out io.Writer, home helmpath.Home) (*repo.Entry, error) { 262 if fi, err := os.Stat(indexFile); err != nil { 263 fmt.Fprintf(out, "Adding %s repo with URL: %s \n", localRepository, localRepositoryURL) 264 i := repo.NewIndexFile() 265 if err := i.WriteFile(indexFile, 0644); err != nil { 266 return nil, err 267 } 268 269 //TODO: take this out and replace with helm update functionality 270 if err := createLink(indexFile, cacheFile, home); err != nil { 271 return nil, err 272 } 273 } else if fi.IsDir() { 274 return nil, fmt.Errorf("%s must be a file, not a directory", indexFile) 275 } 276 277 return &repo.Entry{ 278 Name: localRepository, 279 URL: localRepositoryURL, 280 Cache: cacheFile, 281 }, nil 282 } 283 284 func ensureRepoFileFormat(file string, out io.Writer) error { 285 r, err := repo.LoadRepositoriesFile(file) 286 if err == repo.ErrRepoOutOfDate { 287 fmt.Fprintln(out, "Updating repository file format...") 288 if err := r.WriteFile(file, 0644); err != nil { 289 return err 290 } 291 } 292 293 return nil 294 }