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