github.com/tonto/cli@v0.0.0-20180104210444-aec958fa47db/build_server.go (about) 1 // This command builds a custom fn server with extensions compiled into it. 2 // 3 // NOTES: 4 // * We could just add extensions in the imports, but then there's no way to order them or potentially add extra config (although config should almost always be via env vars) 5 6 package main 7 8 import ( 9 "errors" 10 "fmt" 11 "html/template" 12 "io/ioutil" 13 "os" 14 15 "github.com/urfave/cli" 16 yaml "gopkg.in/yaml.v2" 17 ) 18 19 func buildServer() cli.Command { 20 cmd := buildServerCmd{} 21 flags := append([]cli.Flag{}, cmd.flags()...) 22 return cli.Command{ 23 Name: "build-server", 24 Usage: "build custom fn server", 25 Flags: flags, 26 Action: cmd.buildServer, 27 } 28 } 29 30 type buildServerCmd struct { 31 verbose bool 32 noCache bool 33 } 34 35 func (b *buildServerCmd) flags() []cli.Flag { 36 return []cli.Flag{ 37 cli.BoolFlag{ 38 Name: "v", 39 Usage: "verbose mode", 40 Destination: &b.verbose, 41 }, 42 cli.BoolFlag{ 43 Name: "no-cache", 44 Usage: "Don't use docker cache", 45 Destination: &b.noCache, 46 }, 47 cli.StringFlag{ 48 Name: "tag,t", 49 Usage: "image name and optional tag", 50 }, 51 } 52 } 53 54 // steps: 55 // • Yaml file with extensions listed 56 // • NOTE: All extensions should use env vars for config 57 // • Generating main.go with extensions 58 // * Generate a Dockerfile that gets all the extensions (using dep) 59 // • then generate a main.go with extensions 60 // • compile, throw in another container like main dockerfile 61 func (b *buildServerCmd) buildServer(c *cli.Context) error { 62 63 if c.String("tag") == "" { 64 return errors.New("docker tag required") 65 } 66 67 // path, err := os.Getwd() 68 // if err != nil { 69 // return err 70 // } 71 fpath := "ext.yaml" 72 bb, err := ioutil.ReadFile(fpath) 73 if err != nil { 74 return fmt.Errorf("could not open %s for parsing. Error: %v", fpath, err) 75 } 76 ef := &extFile{} 77 err = yaml.Unmarshal(bb, ef) 78 if err != nil { 79 return err 80 } 81 82 err = os.MkdirAll("tmp", 0777) 83 if err != nil { 84 return err 85 } 86 err = os.Chdir("tmp") 87 if err != nil { 88 return err 89 } 90 err = generateMain(ef) 91 if err != nil { 92 return err 93 } 94 err = generateDockerfile() 95 if err != nil { 96 return err 97 } 98 dir, err := os.Getwd() 99 if err != nil { 100 return err 101 } 102 err = runBuild(c, dir, c.String("tag"), "Dockerfile", b.noCache) 103 if err != nil { 104 return err 105 } 106 fmt.Printf("Custom Fn server built successfully.\n") 107 return nil 108 } 109 110 func generateMain(ef *extFile) error { 111 tmpl, err := template.New("main").Parse(mainTmpl) 112 if err != nil { 113 return err 114 } 115 f, err := os.Create("main.go") 116 if err != nil { 117 return err 118 } 119 defer f.Close() 120 err = tmpl.Execute(f, ef) 121 if err != nil { 122 return err 123 } 124 return nil 125 } 126 127 func generateDockerfile() error { 128 if err := ioutil.WriteFile("Dockerfile", []byte(dockerFileTmpl), os.FileMode(0644)); err != nil { 129 return err 130 } 131 return nil 132 } 133 134 type extFile struct { 135 Extensions []*extInfo `yaml:"extensions"` 136 } 137 138 type extInfo struct { 139 Name string `yaml:"name"` 140 // will have version and other things down the road 141 } 142 143 var mainTmpl = `package main 144 145 import ( 146 "context" 147 148 "github.com/fnproject/fn/api/server" 149 150 {{- range .Extensions }} 151 _ "{{ .Name }}" 152 {{- end}} 153 ) 154 155 func main() { 156 ctx := context.Background() 157 funcServer := server.NewFromEnv(ctx) 158 {{- range .Extensions }} 159 funcServer.AddExtensionByName("{{ .Name }}") 160 {{- end}} 161 funcServer.Start(ctx) 162 } 163 ` 164 165 // NOTE: Getting build errors with dep, probably because our vendor dir is wack. Might work again once we switch to dep. 166 // vendor/github.com/fnproject/fn/api/agent/drivers/docker/registry.go:93: too many arguments in call to client.NewRepository 167 // have ("context".Context, reference.Named, string, http.RoundTripper) want (reference.Named, string, http.RoundTripper) 168 // go build github.com/x/y/vendor/github.com/rdallman/migrate/database/mysql: no buildable Go source files in /go/src/github.com/x/y/vendor/github.com/rdallman/migrate/database/mysql 169 // # github.com/x/y/vendor/github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/scribe 170 // vendor/github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/scribe/scribe.go:210: undefined: thrift.TClient 171 var dockerFileTmpl = `# build stage 172 FROM golang:1.9-alpine AS build-env 173 RUN apk --no-cache add build-base git bzr mercurial gcc 174 # RUN go get -u github.com/golang/dep/cmd/dep 175 ENV D=/go/src/github.com/x/y 176 ADD main.go $D/ 177 RUN cd $D && go get 178 # RUN cd $D && dep init && dep ensure 179 RUN cd $D && go build -o fnserver && cp fnserver /tmp/ 180 181 # final stage 182 FROM fnproject/dind 183 RUN apk add --no-cache ca-certificates 184 WORKDIR /app 185 COPY --from=build-env /tmp/fnserver /app/fnserver 186 CMD ["./fnserver"] 187 `