github.com/devcamcar/cli@v0.0.0-20181107134215-706a05759d18/commands/build_server.go (about)

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