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 }