Skip to content

Commit

Permalink
Fetch helm and use custom home-dir
Browse files Browse the repository at this point in the history
Fetches helm for the user, so they don't have to know about it.

Uses a custom home-dir not to interfere with existing helm
usage.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <[email protected]>
  • Loading branch information
alexellis committed Oct 11, 2019
1 parent 8aede68 commit 0f092b4
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 19 deletions.
9 changes: 9 additions & 0 deletions pkg/cmd/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,12 @@ func Test_RewriteKubeconfig(t *testing.T) {
t.Errorf("Unexpected error, got: %q, want: %q.", len(match), len(expectedContextsToReplace))
}
}

func Test_getHelmURL(t *testing.T) {
got := getHelmURL("amd64", "darwin", "v2.14.3")
want := "https://get.helm.sh/helm-v2.14.3-darwin-amd64.tar.gz"

if want != got {
t.Errorf("want %s, got %s", want, got)
}
}
37 changes: 32 additions & 5 deletions pkg/cmd/kubernetes_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ func fetchChart(path, chart string) error {
}

task := execute.ExecTask{
Command: fmt.Sprintf("helm fetch %s --untar --untardir %s", chart, path),
Command: fmt.Sprintf("%s fetch %s --untar --untardir %s", localBinary("helm"), chart, path),
Env: os.Environ(),
}
res, err := task.Execute()

Expand Down Expand Up @@ -48,8 +49,9 @@ func templateChart(basePath, chart, namespace, outputPath, values string) error

chartRoot := path.Join(basePath, chart)
task := execute.ExecTask{
Command: fmt.Sprintf("helm template %s --output-dir %s --values %s --namespace %s",
chart, outputPath, path.Join(chartRoot, values), namespace),
Command: fmt.Sprintf("%s template %s --output-dir %s --values %s --namespace %s",
localBinary("helm"), chart, outputPath, path.Join(chartRoot, values), namespace),
Env: os.Environ(),
Cwd: basePath,
}

Expand All @@ -65,9 +67,15 @@ func templateChart(basePath, chart, namespace, outputPath, values string) error
return nil
}

func localBinary(name string) string {
home := os.Getenv("HOME")
return path.Join(path.Join(home, ".k3sup/.bin/"), name)
}

func addHelmRepo(name, url string) error {
task := execute.ExecTask{
Command: fmt.Sprintf("helm repo add %s %s", name, url),
Command: fmt.Sprintf("%s repo add %s %s", localBinary("helm"), name, url),
Env: os.Environ(),
}
res, err := task.Execute()

Expand All @@ -83,7 +91,26 @@ func addHelmRepo(name, url string) error {

func updateHelmRepos() error {
task := execute.ExecTask{
Command: fmt.Sprintf("helm repo update"),
Command: fmt.Sprintf("%s repo update", localBinary("helm")),
Env: os.Environ(),
}
res, err := task.Execute()

if err != nil {
return err
}

if res.ExitCode != 0 {
return fmt.Errorf("exit code %d", res.ExitCode)
}
return nil
}

func helmInit() error {
task := execute.ExecTask{
Command: fmt.Sprintf("%s", localBinary("helm")),
Env: os.Environ(),
Args: []string{"init", "--client-only"},
}
res, err := task.Execute()

Expand Down
87 changes: 78 additions & 9 deletions pkg/cmd/openfaas_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package cmd

import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"path"
"strings"
Expand All @@ -29,6 +32,18 @@ func makeInstallOpenFaaS() *cobra.Command {
openfaas.Flags().StringP("namespace", "n", "openfaas", "Namespace for core services")

openfaas.RunE = func(command *cobra.Command, args []string) error {
kubeConfigPath := path.Join(os.Getenv("HOME"), ".kube/config")

if val, ok := os.LookupEnv("KUBECONFIG"); ok {
kubeConfigPath = val
}

if command.Flags().Changed("kubeconfig") {
kubeConfigPath, _ = command.Flags().GetString("kubeconfig")
}

fmt.Printf("Using context: %s\n", kubeConfigPath)

arch := getArchitecture()
fmt.Printf("Node architecture: %s\n", arch)

Expand All @@ -49,18 +64,17 @@ func makeInstallOpenFaaS() *cobra.Command {

log.Printf("User dir established as: %s\n", userPath)

kubeConfigPath := path.Join(os.Getenv("HOME"), ".kube/config")
os.Setenv("HELM_HOME", path.Join(userPath, ".helm"))

if val, ok := os.LookupEnv("KUBECONFIG"); ok {
kubeConfigPath = val
}
if _, statErr := os.Stat(path.Join(path.Join(userPath, ".bin"), "helm")); statErr != nil {
downloadHelm(userPath, clientArch, clientOS)

if command.Flags().Changed("kubeconfig") {
kubeConfigPath, _ = command.Flags().GetString("kubeconfig")
err = helmInit()
if err != nil {
return err
}
}

fmt.Printf("Using context: %s\n", kubeConfigPath)

// lb, _ := command.Flags().GetBool("loadbalancer")

namespace, _ := command.Flags().GetString("namespace")
Expand Down Expand Up @@ -109,7 +123,29 @@ func makeInstallOpenFaaS() *cobra.Command {

err = kubectl("apply", "-R", "-f", outputPath)

return err
if err != nil {
return err
}

fmt.Println(`=======================================================================
= OpenFaaS has been installed. =
=======================================================================
If basic auth is enabled, you can now log into your gateway.
# Get the faas-cli
curl -SLsf https://cli.openfaas.com | sudo sh
# Forward the gateway to your machine
kubectl rollout status -n openfaas deploy/gateway
kubectl port-forward -n openfaas svc/gateway &
# Get your password and log in
PASSWORD=$(kubectl get secret -n openfaas basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode; echo)
echo -n $PASSWORD | faas-cli login --username admin --password-stdin
Thank you for using k3sup!`)

return nil
}

return openfaas
Expand All @@ -134,3 +170,36 @@ func getClientArch() (string, string) {

return arch, os
}

func getHelmURL(arch, os, version string) string {
archSuffix := "amd64"
osSuffix := strings.ToLower(os)

if strings.HasPrefix(arch, "armv7") {
archSuffix = "arm"
} else if strings.HasPrefix(arch, "aarch64") {
archSuffix = "arm64"
}

return fmt.Sprintf("https://get.helm.sh/helm-%s-%s-%s.tar.gz", version, osSuffix, archSuffix)
}

func downloadHelm(userPath, clientArch, clientOS string) error {
helmURL := getHelmURL(clientArch, clientOS, "v2.14.3")
fmt.Println(helmURL)
parsedURL, _ := url.Parse(helmURL)

res, err := http.DefaultClient.Get(parsedURL.String())
if err != nil {
return err
}

defer res.Body.Close()
r := ioutil.NopCloser(res.Body)
untarErr := Untar(r, path.Join(userPath, ".bin"))
if untarErr != nil {
return untarErr
}

return nil
}
132 changes: 132 additions & 0 deletions pkg/cmd/untar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Edited on 2019-10-11 to remove support for nested folders when un-taring
// so that all files are placed in the same target directory

package cmd

import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"log"
"os"
"path"
"path/filepath"
"strings"
"time"
)

// TODO(bradfitz): this was copied from x/build/cmd/buildlet/buildlet.go
// but there were some buildlet-specific bits in there, so the code is
// forked for now. Unfork and add some opts arguments here, so the
// buildlet can use this code somehow.

// Untar reads the gzip-compressed tar file from r and writes it into dir.
func Untar(r io.Reader, dir string) error {
return untar(r, dir)
}

func untar(r io.Reader, dir string) (err error) {
t0 := time.Now()
nFiles := 0
madeDir := map[string]bool{}
defer func() {
td := time.Since(t0)
if err == nil {
log.Printf("extracted tarball into %s: %d files, %d dirs (%v)", dir, nFiles, len(madeDir), td)
} else {
log.Printf("error extracting tarball into %s after %d files, %d dirs, %v: %v", dir, nFiles, len(madeDir), td, err)
}
}()
zr, err := gzip.NewReader(r)
if err != nil {
return fmt.Errorf("requires gzip-compressed body: %v", err)
}
tr := tar.NewReader(zr)
loggedChtimesError := false
for {
f, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
log.Printf("tar reading error: %v", err)
return fmt.Errorf("tar error: %v", err)
}
if !validRelPath(f.Name) {
return fmt.Errorf("tar contained invalid name error %q", f.Name)
}
baseFile := filepath.Base(f.Name)
abs := path.Join(dir, baseFile)
fmt.Println(abs, f.Name)

fi := f.FileInfo()
mode := fi.Mode()
switch {
case mode.IsDir():

break

case mode.IsRegular():

wf, err := os.OpenFile(abs, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode.Perm())
if err != nil {
return err
}
n, err := io.Copy(wf, tr)
if closeErr := wf.Close(); closeErr != nil && err == nil {
err = closeErr
}
if err != nil {
return fmt.Errorf("error writing to %s: %v", abs, err)
}
if n != f.Size {
return fmt.Errorf("only wrote %d bytes to %s; expected %d", n, abs, f.Size)
}
modTime := f.ModTime
if modTime.After(t0) {
// Clamp modtimes at system time. See
// golang.org/issue/19062 when clock on
// buildlet was behind the gitmirror server
// doing the git-archive.
modTime = t0
}
if !modTime.IsZero() {
if err := os.Chtimes(abs, modTime, modTime); err != nil && !loggedChtimesError {
// benign error. Gerrit doesn't even set the
// modtime in these, and we don't end up relying
// on it anywhere (the gomote push command relies
// on digests only), so this is a little pointless
// for now.
log.Printf("error changing modtime: %v (further Chtimes errors suppressed)", err)
loggedChtimesError = true // once is enough
}
}
nFiles++
default:
}
}
return nil
}

func validRelativeDir(dir string) bool {
if strings.Contains(dir, `\`) || path.IsAbs(dir) {
return false
}
dir = path.Clean(dir)
if strings.HasPrefix(dir, "../") || strings.HasSuffix(dir, "/..") || dir == ".." {
return false
}
return true
}

func validRelPath(p string) bool {
if p == "" || strings.Contains(p, `\`) || strings.HasPrefix(p, "/") || strings.Contains(p, "../") {
return false
}
return true
}
18 changes: 13 additions & 5 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,31 @@ package config
import (
"fmt"
"os"
"path"
)

// K3sVersion default version
const K3sVersion = "v0.8.1"

func InitUserDir() (string, error) {
home := os.Getenv("HOME")
fullPath := fmt.Sprintf("%s/k3sup/.bin/", home)
root := fmt.Sprintf("%s/.k3sup/", home)

if len(home) == 0 {
return fullPath, fmt.Errorf("env-var HOME, not set")
return home, fmt.Errorf("env-var HOME, not set")
}

err := os.MkdirAll(fullPath, 0700)
binPath := path.Join(root, "/.bin/")
err := os.MkdirAll(binPath, 0700)
if err != nil {
return fullPath, err
return binPath, err
}

return fullPath, nil
helmPath := path.Join(root, "/.helm/")
helmErr := os.MkdirAll(helmPath, 0700)
if helmErr != nil {
return helmPath, helmErr
}

return root, nil
}

0 comments on commit 0f092b4

Please sign in to comment.