Compare commits

..

4 Commits

Author SHA1 Message Date
Minghe
23c4171ece update to up and down since we already merge them up (#335) 2019-10-18 09:56:22 +08:00
Minghe
d8a1868fce Merge "deploy" command to "up" command (#333) 2019-10-18 09:32:57 +08:00
Minghe Huang
d91a9959a8 bump version 2019-10-17 10:17:32 +08:00
Minghe
87e7c7d6ae fix the wrong binding when deploy function on Docker environment (#330) 2019-10-17 09:45:26 +08:00
10 changed files with 107 additions and 285 deletions

View File

@@ -70,8 +70,8 @@ jobs:
if [[ -z "$AKS_KUBECONFIG" ]];then
echo "skip deploy test since no valid KUBECONFIG"
else
DEBUG=true ./build/fx deploy -n hello -p 12345 examples/functions/JavaScript/func.js
./build/fx destroy hello
DEBUG=true ./build/fx up -n hello -p 12345 examples/functions/JavaScript/func.js
./build/fx down hello
rm ${KUBECONFIG}
fi

View File

@@ -66,8 +66,8 @@ jobs:
run: |
export KUBECONFIG=${HOME}/.kube/aks
echo ${AKS_KUBECONFIG} | base64 -d > $KUBECONFIG
DEBUG=true ./build/fx deploy -n hello -p 12345 examples/functions/JavaScript/func.js
./build/fx destroy hello
DEBUG=true ./build/fx up -n hello -p 12345 examples/functions/JavaScript/func.js
./build/fx down hello
rm ${KUBECONFIG}
Release:
runs-on: ${{ matrix.os }}

View File

@@ -70,14 +70,11 @@ func (d *Docker) BuildImage(ctx context.Context, workdir string, name string) er
return err
}
defer resp.Body.Close()
if os.Getenv("DEBUG") != "" {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
log.Info(string(body))
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
log.Info(string(body))
return nil
}

View File

@@ -8,16 +8,18 @@ import (
"time"
dockerTypes "github.com/docker/docker/api/types"
dockerHTTP "github.com/metrue/fx/container_runtimes/docker/http"
runtime "github.com/metrue/fx/container_runtimes/docker/sdk"
"github.com/metrue/fx/deploy"
"github.com/metrue/fx/packer"
"github.com/metrue/fx/types"
"github.com/metrue/fx/utils"
"github.com/pkg/errors"
)
// Docker manage container
type Docker struct {
client *runtime.Docker
localClient *runtime.Docker
}
// CreateClient create a docker instance
@@ -26,25 +28,50 @@ func CreateClient(ctx context.Context) (*Docker, error) {
if err != nil {
return nil, err
}
return &Docker{client: cli}, nil
return &Docker{localClient: cli}, nil
}
// Deploy create a Docker container from given image, and bind the constants.FxContainerExposePort to given port
func (d *Docker) Deploy(ctx context.Context, fn types.Func, name string, ports []types.PortBinding) error {
// if DOCKER_REMOTE_HOST and DOCKER_REMOTE_PORT given
// it means user is going to deploy service to remote host
host := os.Getenv("DOCKER_REMOTE_HOST")
port := os.Getenv("DOCKER_REMOTE_PORT")
if port != "" && host != "" {
httpClient, err := dockerHTTP.Create(host, port)
if err != nil {
return err
}
project, err := packer.Pack(name, fn)
if err != nil {
return errors.Wrapf(err, "could pack function %v (%s)", name, fn)
}
return httpClient.Up(dockerHTTP.UpOptions{
Body: []byte(fn.Source),
Lang: fn.Language,
Name: name,
Port: int(ports[0].ServiceBindingPort),
HealtCheck: false,
Project: project,
})
}
workdir := fmt.Sprintf("/tmp/fx-%d", time.Now().Unix())
defer os.RemoveAll(workdir)
if err := packer.PackIntoDir(fn, workdir); err != nil {
log.Fatalf("could not pack function %v: %v", fn, err)
return err
}
if err := d.client.BuildImage(ctx, workdir, name); err != nil {
if err := d.localClient.BuildImage(ctx, workdir, name); err != nil {
log.Fatalf("could not build image: %v", err)
return err
}
nameWithTag := name + ":latest"
if err := d.client.ImageTag(ctx, name, nameWithTag); err != nil {
log.Fatalf("could tag image: %v", err)
if err := d.localClient.ImageTag(ctx, name, nameWithTag); err != nil {
log.Fatalf("could not tag image: %v", err)
return err
}
@@ -54,12 +81,12 @@ func (d *Docker) Deploy(ctx context.Context, fn types.Func, name string, ports [
// But it takes some times waiting image ready after image built, we retry to make sure it ready here
var imgInfo dockerTypes.ImageInspect
if err := utils.RunWithRetry(func() error {
return d.client.InspectImage(ctx, name, &imgInfo)
return d.localClient.InspectImage(ctx, name, &imgInfo)
}, time.Second*1, 5); err != nil {
return err
}
return d.client.StartContainer(ctx, name, name, ports)
return d.localClient.StartContainer(ctx, name, name, ports)
}
// Update a container
@@ -69,7 +96,7 @@ func (d *Docker) Update(ctx context.Context, name string) error {
// Destroy stop and remove container
func (d *Docker) Destroy(ctx context.Context, name string) error {
return d.client.ContainerStop(ctx, name, nil)
return d.localClient.ContainerStop(ctx, name, nil)
}
// GetStatus get status of container

37
fx.go
View File

@@ -15,7 +15,7 @@ import (
"github.com/urfave/cli"
)
const version = "0.7.5"
const version = "0.8.0"
var cfg *config.Config
@@ -192,33 +192,6 @@ func main() {
return handlers.Up(cfg)(c)
},
},
{
Name: "deploy",
Usage: "deploy a function or a group of functions",
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: func(c *cli.Context) error {
return handlers.Deploy(cfg)(c)
},
},
{
Name: "down",
Usage: "destroy a service",
@@ -227,14 +200,6 @@ func main() {
return handlers.Down(cfg)(c)
},
},
{
Name: "destroy",
Usage: "destroy a service",
ArgsUsage: "[service 1, service 2, ....]",
Action: func(c *cli.Context) error {
return handlers.Destroy(cfg)(c)
},
},
{
Name: "list",
Aliases: []string{"ls"},

View File

@@ -1,102 +0,0 @@
package handlers
import (
"context"
"fmt"
"io/ioutil"
"os"
"github.com/apex/log"
"github.com/metrue/fx/config"
"github.com/metrue/fx/constants"
api "github.com/metrue/fx/container_runtimes/docker/http"
"github.com/metrue/fx/deploy"
dockerDeployer "github.com/metrue/fx/deploy/docker"
k8sDeployer "github.com/metrue/fx/deploy/kubernetes"
"github.com/metrue/fx/types"
"github.com/metrue/fx/utils"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
// Deploy deploy handle function
func Deploy(cfg config.Configer) HandleFunc {
return func(ctx *cli.Context) (err error) {
funcFile := ctx.Args().First()
name := ctx.String("name")
port := ctx.Int("port")
force := ctx.Bool("force")
defer func() {
if r := recover(); r != nil {
log.Fatalf("fatal error happened: %v", r)
}
if err != nil {
log.Fatalf("deploy function %s (%s) failed: %v", name, funcFile, err)
}
log.Infof("function %s (%s) deployed successfully", name, funcFile)
}()
if port < PortRange.min || port > PortRange.max {
return fmt.Errorf("invalid port number: %d, port number should in range of %d - %d", port, PortRange.min, PortRange.max)
}
hosts, err := cfg.ListActiveMachines()
if err != nil {
return errors.Wrap(err, "list active machines failed")
}
if len(hosts) == 0 {
log.Warnf("no active machines")
return nil
}
// try to stop service firt
if force {
for n, host := range hosts {
if err := api.MustCreate(host.Host, constants.AgentPort).
Stop(name); err != nil {
log.Infof("stop function %s on machine %s failed: %v", name, n, err)
} else {
log.Infof("stop function %s on machine %s: %v", name, n, constants.CheckedSymbol)
}
}
}
body, err := ioutil.ReadFile(funcFile)
if err != nil {
return errors.Wrap(err, "read source failed")
}
lang := utils.GetLangFromFileName(funcFile)
var deployer deploy.Deployer
if os.Getenv("KUBECONFIG") != "" {
deployer, err = k8sDeployer.Create()
if err != nil {
return err
}
} else {
bctx := context.Background()
deployer, err = dockerDeployer.CreateClient(bctx)
if err != nil {
return err
}
}
// TODO multiple ports support
return deployer.Deploy(
context.Background(),
types.Func{Language: lang, Source: string(body)},
name,
[]types.PortBinding{
types.PortBinding{
ServiceBindingPort: 80,
ContainerExposePort: constants.FxContainerExposePort,
},
types.PortBinding{
ServiceBindingPort: 443,
ContainerExposePort: constants.FxContainerExposePort,
},
},
)
}
}

View File

@@ -1,38 +0,0 @@
package handlers
import (
"context"
"os"
"github.com/metrue/fx/config"
"github.com/metrue/fx/deploy"
dockerDeployer "github.com/metrue/fx/deploy/docker"
k8sDeployer "github.com/metrue/fx/deploy/kubernetes"
"github.com/urfave/cli"
)
// Destroy command handle
func Destroy(cfg config.Configer) HandleFunc {
return func(ctx *cli.Context) (err error) {
services := ctx.Args()
c := context.Background()
var runner deploy.Deployer
if os.Getenv("KUBECONFIG") != "" {
runner, err = k8sDeployer.Create()
if err != nil {
return err
}
} else {
runner, err = dockerDeployer.CreateClient(c)
if err != nil {
return err
}
}
for _, svc := range services {
if err := runner.Destroy(c, svc); err != nil {
return err
}
}
return nil
}
}

View File

@@ -1,28 +1,37 @@
package handlers
import (
"github.com/apex/log"
"context"
"os"
"github.com/metrue/fx/config"
"github.com/metrue/fx/constants"
api "github.com/metrue/fx/container_runtimes/docker/http"
"github.com/pkg/errors"
"github.com/metrue/fx/deploy"
dockerDeployer "github.com/metrue/fx/deploy/docker"
k8sDeployer "github.com/metrue/fx/deploy/kubernetes"
"github.com/urfave/cli"
)
// Down command handle
func Down(cfg config.Configer) HandleFunc {
return func(ctx *cli.Context) error {
containerID := ctx.Args()
hosts, err := cfg.ListActiveMachines()
if err != nil {
return errors.Wrapf(err, "list active machines failed: %v", err)
}
for name, host := range hosts {
if err := api.MustCreate(host.Host, constants.AgentPort).
Down(containerID); err != nil {
return errors.Wrapf(err, "stop function on machine %s failed: %v", name, err)
return func(ctx *cli.Context) (err error) {
services := ctx.Args()
c := context.Background()
var runner deploy.Deployer
if os.Getenv("KUBECONFIG") != "" {
runner, err = k8sDeployer.Create()
if err != nil {
return err
}
} else {
runner, err = dockerDeployer.CreateClient(c)
if err != nil {
return err
}
}
for _, svc := range services {
if err := runner.Destroy(c, svc); err != nil {
return err
}
log.Infof("stop function on machine %s: %v", name, constants.CheckedSymbol)
}
return nil
}

View File

@@ -1,15 +1,17 @@
package handlers
import (
"context"
"fmt"
"io/ioutil"
"os"
"github.com/apex/log"
"github.com/metrue/fx/config"
"github.com/metrue/fx/constants"
api "github.com/metrue/fx/container_runtimes/docker/http"
"github.com/metrue/fx/packer"
"github.com/metrue/fx/provision"
"github.com/metrue/fx/deploy"
dockerDeployer "github.com/metrue/fx/deploy/docker"
k8sDeployer "github.com/metrue/fx/deploy/kubernetes"
"github.com/metrue/fx/types"
"github.com/metrue/fx/utils"
"github.com/pkg/errors"
@@ -31,8 +33,6 @@ func Up(cfg config.Configer) HandleFunc {
funcFile := ctx.Args().First()
name := ctx.String("name")
port := ctx.Int("port")
healtcheck := ctx.Bool("healthcheck")
force := ctx.Bool("force")
defer func() {
if r := recover(); r != nil {
@@ -48,69 +48,47 @@ func Up(cfg config.Configer) HandleFunc {
if port < PortRange.min || port > PortRange.max {
return fmt.Errorf("invalid port number: %d, port number should in range of %d - %d", port, PortRange.min, PortRange.max)
}
hosts, err := cfg.ListActiveMachines()
if err != nil {
return errors.Wrap(err, "list active machines failed")
}
if len(hosts) == 0 {
log.Warnf("no active machines")
return nil
}
// try to stop service firt
if force {
for n, host := range hosts {
if err := api.MustCreate(host.Host, constants.AgentPort).
Stop(name); err != nil {
log.Infof("stop function %s on machine %s failed: %v", name, n, err)
} else {
log.Infof("stop function %s on machine %s: %v", name, n, constants.CheckedSymbol)
}
}
}
body, err := ioutil.ReadFile(funcFile)
if err != nil {
return errors.Wrap(err, "read source failed")
}
lang := utils.GetLangFromFileName(funcFile)
fn := types.Func{
Language: lang,
Source: string(body),
}
project, err := packer.Pack(name, fn)
if err != nil {
return errors.Wrapf(err, "could pack function %s (%s)", name, funcFile)
}
for n, host := range hosts {
if !host.Provisioned {
provisionor := provision.New(host)
if err := provisionor.Start(); err != nil {
return errors.Wrapf(err, "could not provision %s", n)
}
log.Infof("provision machine %v: %s", n, constants.CheckedSymbol)
if err := cfg.UpdateProvisionedStatus(n, true); err != nil {
return errors.Wrap(err, "update machine provision status failed")
}
var deployer deploy.Deployer
var bindings []types.PortBinding
if os.Getenv("KUBECONFIG") != "" {
deployer, err = k8sDeployer.Create()
if err != nil {
return err
}
if err := api.MustCreate(host.Host, constants.AgentPort).
Up(api.UpOptions{
Body: body,
Lang: lang,
Name: name,
Port: port,
HealtCheck: healtcheck,
Project: project,
}); err != nil {
return errors.Wrapf(err, "up function %s(%s) to machine %s failed", name, funcFile, n)
bindings = []types.PortBinding{
types.PortBinding{
ServiceBindingPort: 80,
ContainerExposePort: constants.FxContainerExposePort,
},
types.PortBinding{
ServiceBindingPort: 443,
ContainerExposePort: constants.FxContainerExposePort,
},
}
} else {
bctx := context.Background()
deployer, err = dockerDeployer.CreateClient(bctx)
if err != nil {
return err
}
bindings = []types.PortBinding{
types.PortBinding{
ServiceBindingPort: int32(port),
ContainerExposePort: constants.FxContainerExposePort,
},
}
log.Infof("up function %s(%s) to machine %s: %v", name, funcFile, n, constants.CheckedSymbol)
}
return nil
return deployer.Deploy(
context.Background(),
types.Func{Language: lang, Source: string(body)},
name,
bindings,
)
}
}

View File

@@ -13,18 +13,6 @@ run() {
$fx down ${service}_${lang} # | grep "Down Service ${service}"
}
deploy() {
local lang=$1
local port=$2
if [[ -z "$DOCKER_USERNAME" || -z "$DOCKER_PASSWORD" ]];then
echo "skip deploy test since no DOCKER_USERNAME and DOCKER_PASSWORD set"
else
$fx deploy --name ${service}-${lang} --port ${port} test/functions/func.${lang}
docker ps
$fx destroy ${service}-${lang}
fi
}
build_image() {
local lang=$1
local tag=$2
@@ -47,8 +35,6 @@ for lang in 'js' 'rb' 'py' 'go' 'php' 'java' 'd'; do
run $lang $port
((port++))
deploy $lang $port
build_image $lang "test-fx-image-build-${lang}"
mkdir -p /tmp/${lang}/images
export_image ${lang} /tmp/${lang}/images