github.com/iron-io/functions@v0.0.0-20180820112432-d59d7d1c40b2/fn/common/common.go (about)

     1  package common
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"log"
    10  	"net/url"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"strings"
    15  	"text/template"
    16  
    17  	"github.com/iron-io/functions/fn/langs"
    18  	functions "github.com/iron-io/functions_go"
    19  	"github.com/spf13/viper"
    20  )
    21  
    22  var (
    23  	API_VERSION     string
    24  	SSL_SKIP_VERIFY bool
    25  	JWT_AUTH_KEY    string
    26  	API_URL         string
    27  	SCHEME          string
    28  	INITIAL_VERSION string
    29  	HOST            string
    30  	BASE_PATH       string
    31  )
    32  
    33  func SetEnv() {
    34  	viper.AutomaticEnv()
    35  	API_VERSION = "/v1"
    36  	SSL_SKIP_VERIFY = (os.Getenv("SSL_SKIP_VERIFY") == "true")
    37  	JWT_AUTH_KEY = viper.GetString("jwt_auth_key")
    38  	SCHEME = "http"
    39  	INITIAL_VERSION = "0.0.1"
    40  	viper.SetDefault("API_URL", "http://localhost:8080")
    41  	API_URL = viper.GetString("API_URL")
    42  	BASE_PATH = GetBasePath(API_VERSION)
    43  }
    44  
    45  func GetBasePath(version string) string {
    46  	u, err := url.Parse(API_URL)
    47  	if err != nil {
    48  		log.Fatalln("Couldn't parse API URL:", err)
    49  	}
    50  	HOST = u.Host
    51  	SCHEME = u.Scheme
    52  	u.Path = version
    53  	return u.String()
    54  }
    55  
    56  func ResetBasePath(c *functions.Configuration) error {
    57  	c.BasePath = BASE_PATH
    58  	return nil
    59  }
    60  
    61  func Verbwriter(verbose bool) io.Writer {
    62  	verbwriter := ioutil.Discard
    63  	if verbose {
    64  		verbwriter = os.Stderr
    65  	}
    66  	return verbwriter
    67  }
    68  
    69  func Buildfunc(verbwriter io.Writer, fn string) (*Funcfile, error) {
    70  	funcfile, err := ParseFuncfile(fn)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	if funcfile.Version == "" {
    76  		err = funcfile.Bumpversion()
    77  		if err != nil {
    78  			return nil, err
    79  		}
    80  		if err := StoreFuncfile(fn, funcfile); err != nil {
    81  			return nil, err
    82  		}
    83  		funcfile, err = ParseFuncfile(fn)
    84  		if err != nil {
    85  			return nil, err
    86  		}
    87  	}
    88  
    89  	if err := localbuild(verbwriter, fn, funcfile.Build); err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	if err := dockerbuild(verbwriter, fn, funcfile); err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	return funcfile, nil
    98  }
    99  
   100  func localbuild(verbwriter io.Writer, path string, steps []string) error {
   101  	for _, cmd := range steps {
   102  		exe := exec.Command("/bin/sh", "-c", cmd)
   103  		exe.Dir = filepath.Dir(path)
   104  		exe.Stderr = verbwriter
   105  		exe.Stdout = verbwriter
   106  		if err := exe.Run(); err != nil {
   107  			return fmt.Errorf("error running command %v (%v)", cmd, err)
   108  		}
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  func dockerbuild(verbwriter io.Writer, path string, ff *Funcfile) error {
   115  	dir := filepath.Dir(path)
   116  
   117  	var helper langs.LangHelper
   118  	dockerfile := filepath.Join(dir, "Dockerfile")
   119  	if !Exists(dockerfile) {
   120  		err := writeTmpDockerfile(dir, ff)
   121  		defer os.Remove(filepath.Join(dir, "Dockerfile"))
   122  		if err != nil {
   123  			return err
   124  		}
   125  		helper = langs.GetLangHelper(*ff.Runtime)
   126  		if helper == nil {
   127  			return fmt.Errorf("Cannot build, no language helper found for %v", *ff.Runtime)
   128  		}
   129  		if helper.HasPreBuild() {
   130  			err := helper.PreBuild()
   131  			if err != nil {
   132  				return err
   133  			}
   134  		}
   135  	}
   136  
   137  	fmt.Printf("Building image %v\n", ff.FullName())
   138  	cmd := exec.Command("docker", "build", "-t", ff.FullName(), ".")
   139  	cmd.Dir = dir
   140  	cmd.Stderr = os.Stderr
   141  	cmd.Stdout = os.Stdout
   142  	if err := cmd.Run(); err != nil {
   143  		return fmt.Errorf("error running docker build: %v", err)
   144  	}
   145  	if helper != nil {
   146  		err := helper.AfterBuild()
   147  		if err != nil {
   148  			return err
   149  		}
   150  	}
   151  	return nil
   152  }
   153  
   154  func Exists(name string) bool {
   155  	if _, err := os.Stat(name); err != nil {
   156  		if os.IsNotExist(err) {
   157  			return false
   158  		}
   159  	}
   160  	return true
   161  }
   162  
   163  var AcceptableFnRuntimes = map[string]string{
   164  	"elixir":           "iron/elixir",
   165  	"erlang":           "iron/erlang",
   166  	"gcc":              "iron/gcc",
   167  	"go":               "iron/go",
   168  	"java":             "iron/java",
   169  	"leiningen":        "iron/leiningen",
   170  	"mono":             "iron/mono",
   171  	"node":             "iron/node",
   172  	"perl":             "iron/perl",
   173  	"php":              "iron/php",
   174  	"python":           "iron/python:2",
   175  	"ruby":             "iron/ruby",
   176  	"scala":            "iron/scala",
   177  	"rust":             "corey/rust-alpine",
   178  	"dotnet":           "microsoft/dotnet:runtime",
   179  	"lambda-nodejs4.3": "iron/functions-lambda:nodejs4.3",
   180  }
   181  
   182  const tplDockerfile = `FROM {{ .BaseImage }}
   183  WORKDIR /function
   184  ADD . /function/
   185  {{ if ne .Entrypoint "" }} ENTRYPOINT [{{ .Entrypoint }}] {{ end }}
   186  {{ if ne .Cmd "" }} CMD [{{ .Cmd }}] {{ end }}
   187  `
   188  
   189  func writeTmpDockerfile(dir string, ff *Funcfile) error {
   190  	if ff.Entrypoint == "" && ff.Cmd == "" {
   191  		return errors.New("entrypoint and cmd are missing, you must provide one or the other")
   192  	}
   193  
   194  	runtime, tag := ff.RuntimeTag()
   195  	rt, ok := AcceptableFnRuntimes[runtime]
   196  	if !ok {
   197  		return fmt.Errorf("cannot use runtime %s", runtime)
   198  	}
   199  
   200  	if tag != "" {
   201  		rt = fmt.Sprintf("%s:%s", rt, tag)
   202  	}
   203  
   204  	fd, err := os.Create(filepath.Join(dir, "Dockerfile"))
   205  	if err != nil {
   206  		return err
   207  	}
   208  	defer fd.Close()
   209  
   210  	// convert entrypoint string to slice
   211  	bufferEp := stringToSlice(ff.Entrypoint)
   212  	bufferCmd := stringToSlice(ff.Cmd)
   213  
   214  	t := template.Must(template.New("Dockerfile").Parse(tplDockerfile))
   215  	err = t.Execute(fd, struct {
   216  		BaseImage, Entrypoint, Cmd string
   217  	}{rt, bufferEp.String(), bufferCmd.String()})
   218  
   219  	return err
   220  }
   221  
   222  func stringToSlice(in string) bytes.Buffer {
   223  	epvals := strings.Fields(in)
   224  	var buffer bytes.Buffer
   225  	for i, s := range epvals {
   226  		if i > 0 {
   227  			buffer.WriteString(", ")
   228  		}
   229  		buffer.WriteString("\"")
   230  		buffer.WriteString(s)
   231  		buffer.WriteString("\"")
   232  	}
   233  	return buffer
   234  }
   235  
   236  func ExtractEnvConfig(configs []string) map[string]string {
   237  	c := make(map[string]string)
   238  	for _, v := range configs {
   239  		kv := strings.SplitN(v, "=", 2)
   240  		if len(kv) == 2 {
   241  			c[kv[0]] = os.ExpandEnv(kv[1])
   242  		}
   243  	}
   244  	return c
   245  }
   246  
   247  func Dockerpush(ff *Funcfile) error {
   248  	latestTag := ff.Name + ":latest"
   249  	cmd := exec.Command("docker", "tag", ff.FullName(), latestTag)
   250  	cmd.Stderr = os.Stderr
   251  	cmd.Stdout = os.Stdout
   252  	if err := cmd.Run(); err != nil {
   253  		return fmt.Errorf("error tagging latest: %v", err)
   254  	}
   255  	cmd = exec.Command("docker", "push", ff.Name)
   256  	if err := cmd.Run(); err != nil {
   257  		return fmt.Errorf("error running docker push: %v", err)
   258  	}
   259  	return nil
   260  }
   261  
   262  func AppNamePath(img string) (string, string) {
   263  	sep := strings.Index(img, "/")
   264  	if sep < 0 {
   265  		return "", ""
   266  	}
   267  	tag := strings.Index(img[sep:], ":")
   268  	if tag < 0 {
   269  		tag = len(img[sep:])
   270  	}
   271  	return img[:sep], img[sep : sep+tag]
   272  }