Files
fx-serverless/fx.go
2020-02-21 17:28:07 +08:00

256 lines
5.6 KiB
Go

package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"regexp"
"github.com/apex/log"
"github.com/google/uuid"
aurora "github.com/logrusorgru/aurora"
"github.com/metrue/fx/context"
"github.com/metrue/fx/handlers"
"github.com/metrue/fx/middlewares"
"github.com/urfave/cli"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
)
const version = "0.9.1"
func init() {
go checkForUpdate()
}
func handle(fns ...func(ctx context.Contexter) error) func(ctx *cli.Context) error {
return func(c *cli.Context) error {
ctx := context.FromCliContext(c)
for _, fn := range fns {
if err := fn(ctx); err != nil {
panic(err)
}
}
return nil
}
}
func checkForUpdate() {
const releaseURL = "https://api.github.com/repos/metrue/fx/releases/latest"
resp, err := http.Get(releaseURL)
if err != nil {
log.Debugf("Failed to fetch Github release page, error %v", err)
return
}
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
var releaseJSON struct {
Tag string `json:"tag_name"`
URL string `json:"html_url"`
}
if err := decoder.Decode(&releaseJSON); err != nil {
log.Debugf("Failed to decode Github release page JSON, error %v", err)
return
}
if matched, err := regexp.MatchString(`^(\d+\.)(\d+\.)(\d+)$`, releaseJSON.Tag); err != nil || !matched {
log.Debugf("Unofficial release %s?", releaseJSON.Tag)
return
}
log.Debugf("Latest release tag is %s", releaseJSON.Tag)
if releaseJSON.Tag != version {
fmt.Fprintf(os.Stderr, "\nfx %s is available (you're using %s), get the latest release from: %s\n",
releaseJSON.Tag, version, releaseJSON.URL)
}
}
func main() {
app := cli.NewApp()
app.Name = "fx"
app.Usage = "makes function as a service"
app.Version = version
defer func() {
if r := recover(); r != nil {
fmt.Println(aurora.Red("*****************"))
fmt.Println(r)
fmt.Println(aurora.Red("*****************"))
}
}()
app.Commands = []cli.Command{
{
Name: "infra",
Usage: "manage infrastructure",
Subcommands: []cli.Command{
{
Name: "create",
Usage: "create a infra for fx service",
Flags: []cli.Flag{
cli.StringFlag{
Name: "type, t",
Usage: "infracture type, 'docker', 'k8s' and 'k3s' support",
},
cli.StringFlag{
Name: "name, n",
Usage: "name to identify the infrastructure",
},
cli.StringFlag{
Name: "host",
Usage: "user and ip of your host, eg. 'root@182.12.1.12'",
},
cli.StringFlag{
Name: "master",
Usage: "serve as master node in K3S cluster, eg. 'root@182.12.1.12'",
},
cli.StringFlag{
Name: "agents",
Usage: "serve as agent node in K3S cluster, eg. 'root@187.1. 2. 3,root@123.3.2.1'",
},
},
Action: handle(
middlewares.LoadConfig,
handlers.Setup,
),
},
{
Name: "list",
Usage: "list all infrastructures",
Action: handle(
middlewares.LoadConfig,
handlers.ListInfra,
),
},
{
Name: "use",
Usage: "set current context to target cloud with given name",
Action: handle(
middlewares.LoadConfig,
handlers.UseInfra,
),
},
},
},
{
Name: "up",
Usage: "deploy a function",
ArgsUsage: "[func.go func.js func.py func.rb ...]",
Flags: []cli.Flag{
cli.StringFlag{
Name: "name, n",
Value: uuid.New().String(),
Usage: "service name",
},
cli.IntFlag{
Name: "port, p",
Usage: "port number",
},
cli.BoolFlag{
Name: "healthcheck, hc",
Usage: "do a health check after service up",
},
cli.BoolFlag{
Name: "force, f",
Usage: "force deploy a function or functions",
},
},
Action: handle(
middlewares.LoadConfig,
middlewares.Provision,
middlewares.Parse("up"),
middlewares.Binding,
middlewares.Build,
handlers.Up,
),
},
{
Name: "down",
Usage: "destroy a service",
ArgsUsage: "[service 1, service 2, ....]",
Action: handle(
middlewares.Parse("down"),
middlewares.LoadConfig,
middlewares.Provision,
handlers.Down,
),
},
{
Name: "list",
Aliases: []string{"ls"},
Usage: "list deployed services",
Flags: []cli.Flag{
cli.StringFlag{
Name: "format, f",
Value: "table",
Usage: "output format, 'table' and 'JSON' supported",
},
},
Action: handle(
middlewares.Parse("list"),
middlewares.LoadConfig,
middlewares.Provision,
handlers.List,
),
},
{
Name: "call",
Usage: "run a function instantly",
Flags: []cli.Flag{
cli.StringFlag{
Name: "host, H",
Usage: "fx server host, default is localhost",
},
},
Action: handle(handlers.Call),
},
{
Name: "image",
Usage: "manage image of service",
Subcommands: []cli.Command{
{
Name: "build",
Usage: "build a image",
Flags: []cli.Flag{
cli.StringFlag{
Name: "tag, t",
Usage: "image tag",
},
},
Action: handle(
middlewares.LoadConfig,
middlewares.Provision,
middlewares.Parse("image_build"),
handlers.BuildImage,
),
},
{
Name: "export",
Usage: "export the Docker project of service",
Flags: []cli.Flag{
cli.StringFlag{
Name: "output, o",
Usage: "output directory",
},
},
Action: handle(
middlewares.LoadConfig,
middlewares.Provision,
middlewares.Parse("image_export"),
handlers.ExportImage,
),
},
},
},
{
Name: "doctor",
Usage: "health check for fx",
Action: handle(handlers.Doctor),
},
}
if err := app.Run(os.Args); err != nil {
os.Exit(1)
}
}