Compare commits
6 Commits
0.8.1-alph
...
0.8.3-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41bc98ab64 | ||
|
|
b007ac315a | ||
|
|
940f6b8f72 | ||
|
|
f9690b74a5 | ||
|
|
f2c58d545a | ||
|
|
4732426629 |
@@ -40,6 +40,8 @@ Feel free hacking fx to support the languages not listed. Welcome to tweet me [@
|
||||
|
||||
# Installation
|
||||
|
||||
Binaries are available for Windows, MacOS and Linux/Unix on x86. For other architectures and platforms, follow instructions to [build fx from source](#buildtest).
|
||||
|
||||
* MacOS
|
||||
|
||||
```
|
||||
@@ -59,9 +61,9 @@ curl -o- https://raw.githubusercontent.com/metrue/fx/master/scripts/install.sh |
|
||||
curl -o- https://raw.githubusercontent.com/metrue/fx/master/scripts/install.sh | sudo bash
|
||||
```
|
||||
|
||||
fx will be installed into /usr/local/bin, sometimes you may need `source ~/.zshrc` or `source ~/.bashrc` to make fx available in `$PAHT`.
|
||||
fx will be installed into /usr/local/bin, sometimes you may need `source ~/.zshrc` or `source ~/.bashrc` to make fx available in `$PATH`.
|
||||
|
||||
* Window
|
||||
* Windows
|
||||
|
||||
You can go the release page to [download](https://github.com/metrue/fx/releases) fx manually;
|
||||
|
||||
@@ -229,6 +231,7 @@ fx uses [Project](https://github.com/metrue/fx/projects/4) to manage the develop
|
||||
Docker: make sure [Docker](https://docs.docker.com/engine/installation/) installed and running on your server.
|
||||
|
||||
|
||||
<a name="buildtest"></a>
|
||||
#### Build & Test
|
||||
|
||||
```
|
||||
|
||||
@@ -377,8 +377,6 @@ func (api *API) StartContainer(ctx context.Context, name string, image string, b
|
||||
return fmt.Errorf("container id is missing")
|
||||
}
|
||||
|
||||
log.Infof("container %s created", name)
|
||||
|
||||
// start container
|
||||
path = fmt.Sprintf("/containers/%s/start", createRes.ID)
|
||||
url := fmt.Sprintf("%s%s", api.endpoint, path)
|
||||
@@ -402,7 +400,6 @@ func (api *API) StartContainer(ctx context.Context, name string, image string, b
|
||||
msg := fmt.Sprintf("start container met issue: %s", string(b))
|
||||
return errors.New(msg)
|
||||
}
|
||||
log.Infof("container %s started", name)
|
||||
|
||||
if _, err = api.inspect(createRes.ID); err != nil {
|
||||
msg := fmt.Sprintf("inspect container %s error", name)
|
||||
|
||||
@@ -76,7 +76,9 @@ func (d *Docker) BuildImage(ctx context.Context, workdir string, name string) er
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info(string(body))
|
||||
if os.Getenv("DEBUG") != "" {
|
||||
log.Info(string(body))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
// Deployer make a image a service
|
||||
type Deployer interface {
|
||||
Deploy(ctx context.Context, fn types.Func, name string, bindings []types.PortBinding) error
|
||||
Deploy(ctx context.Context, fn types.Func, name string, image string, bindings []types.PortBinding) error
|
||||
Destroy(ctx context.Context, name string) error
|
||||
Update(ctx context.Context, name string) error
|
||||
GetStatus(ctx context.Context, name string) error
|
||||
|
||||
@@ -2,20 +2,14 @@ package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/metrue/fx/constants"
|
||||
containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||
dockerHTTP "github.com/metrue/fx/container_runtimes/docker/http"
|
||||
dockerSDK "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"
|
||||
)
|
||||
|
||||
// Docker manage container
|
||||
@@ -44,38 +38,8 @@ func CreateClient(ctx context.Context) (d *Docker, err error) {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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.cli.BuildImage(ctx, workdir, name); err != nil {
|
||||
log.Fatalf("could not build image: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
nameWithTag := name + ":latest"
|
||||
if err := d.cli.TagImage(ctx, name, nameWithTag); err != nil {
|
||||
log.Fatalf("could not tag image: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// when deploy a function on a bare Docker running without Kubernetes,
|
||||
// image would be built on-demand on host locally, so there is no need to
|
||||
// pull image from remote.
|
||||
// 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.cli.InspectImage(ctx, name, &imgInfo)
|
||||
}, time.Second*1, 5); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.cli.StartContainer(ctx, name, name, ports)
|
||||
func (d *Docker) Deploy(ctx context.Context, fn types.Func, name string, image string, ports []types.PortBinding) error {
|
||||
return d.cli.StartContainer(ctx, name, image, ports)
|
||||
}
|
||||
|
||||
// Update a container
|
||||
|
||||
@@ -1,47 +1,43 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/metrue/fx/types"
|
||||
)
|
||||
|
||||
func TestDocker(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
cli, err := CreateClient(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
name := "helloworld"
|
||||
bindings := []types.PortBinding{
|
||||
types.PortBinding{
|
||||
ServiceBindingPort: 80,
|
||||
ContainerExposePort: 3000,
|
||||
},
|
||||
types.PortBinding{
|
||||
ServiceBindingPort: 443,
|
||||
ContainerExposePort: 3000,
|
||||
},
|
||||
}
|
||||
|
||||
fn := types.Func{
|
||||
Language: "node",
|
||||
Source: `
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello world'
|
||||
}
|
||||
`,
|
||||
}
|
||||
if err := cli.Deploy(ctx, fn, name, bindings); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
if err := cli.Destroy(ctx, name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// ctx := context.Background()
|
||||
// cli, err := CreateClient(ctx)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// name := "helloworld"
|
||||
// bindings := []types.PortBinding{
|
||||
// types.PortBinding{
|
||||
// ServiceBindingPort: 80,
|
||||
// ContainerExposePort: 3000,
|
||||
// },
|
||||
// types.PortBinding{
|
||||
// ServiceBindingPort: 443,
|
||||
// ContainerExposePort: 3000,
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// fn := types.Func{
|
||||
// Language: "node",
|
||||
// Source: `
|
||||
// module.exports = (ctx) => {
|
||||
// ctx.body = 'hello world'
|
||||
// }
|
||||
// `,
|
||||
// }
|
||||
// if err := cli.Deploy(ctx, fn, name, name, bindings); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// time.Sleep(1 * time.Second)
|
||||
//
|
||||
// if err := cli.Destroy(ctx, name); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
}
|
||||
|
||||
8
deploy/k3s/constants.go
Normal file
8
deploy/k3s/constants.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package k3s
|
||||
|
||||
// ConfigMap is the key to function docker project source code in configmap
|
||||
var ConfigMap = struct {
|
||||
AppMetaEnvName string
|
||||
}{
|
||||
AppMetaEnvName: "APP_META",
|
||||
}
|
||||
102
deploy/k3s/deployment.go
Normal file
102
deploy/k3s/deployment.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package k3s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/metrue/fx/types"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func generateDeploymentSpec(
|
||||
name string,
|
||||
image string,
|
||||
bindPorts []types.PortBinding,
|
||||
replicas int32,
|
||||
selector map[string]string,
|
||||
) *appsv1.Deployment {
|
||||
ports := []apiv1.ContainerPort{}
|
||||
for index, binding := range bindPorts {
|
||||
ports = append(ports, apiv1.ContainerPort{
|
||||
Name: fmt.Sprintf("fx-container-%d", index),
|
||||
ContainerPort: binding.ContainerExposePort,
|
||||
})
|
||||
}
|
||||
|
||||
container := apiv1.Container{
|
||||
Name: "fx-placeholder-container-name",
|
||||
Image: image,
|
||||
Ports: ports,
|
||||
ImagePullPolicy: v1.PullIfNotPresent,
|
||||
}
|
||||
return &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Replicas: &replicas,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: selector,
|
||||
},
|
||||
Template: apiv1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: selector,
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{container},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetDeployment get a deployment
|
||||
func (k *K3S) GetDeployment(namespace string, name string) (*appsv1.Deployment, error) {
|
||||
return k.AppsV1().Deployments(namespace).Get(name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// CreateDeployment create a deployment
|
||||
func (k *K3S) CreateDeployment(
|
||||
namespace string,
|
||||
name string,
|
||||
image string,
|
||||
ports []types.PortBinding,
|
||||
replicas int32,
|
||||
selector map[string]string,
|
||||
) (*appsv1.Deployment, error) {
|
||||
deployment := generateDeploymentSpec(name, image, ports, replicas, selector)
|
||||
return k.AppsV1().Deployments(namespace).Create(deployment)
|
||||
}
|
||||
|
||||
// UpdateDeployment update a deployment
|
||||
func (k *K3S) UpdateDeployment(
|
||||
namespace string,
|
||||
name string,
|
||||
image string,
|
||||
ports []types.PortBinding,
|
||||
replicas int32,
|
||||
selector map[string]string,
|
||||
) (*appsv1.Deployment, error) {
|
||||
deployment := generateDeploymentSpec(name, image, ports, replicas, selector)
|
||||
return k.AppsV1().Deployments(namespace).Update(deployment)
|
||||
}
|
||||
|
||||
// DeleteDeployment delete a deployment
|
||||
func (k *K3S) DeleteDeployment(namespace string, name string) error {
|
||||
return k.AppsV1().Deployments(namespace).Delete(name, &metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
// CreateDeploymentWithInitContainer create a deployment which will wait InitContainer to do the image build before function container start
|
||||
func (k *K3S) CreateDeploymentWithInitContainer(
|
||||
namespace string,
|
||||
name string,
|
||||
ports []types.PortBinding,
|
||||
replicas int32,
|
||||
selector map[string]string,
|
||||
) (*appsv1.Deployment, error) {
|
||||
deployment := generateDeploymentSpec(name, name, ports, replicas, selector)
|
||||
updatedDeployment := injectInitContainer(name, deployment)
|
||||
return k.AppsV1().Deployments(namespace).Create(updatedDeployment)
|
||||
}
|
||||
61
deploy/k3s/deployment_test.go
Normal file
61
deploy/k3s/deployment_test.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package k3s
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/metrue/fx/types"
|
||||
)
|
||||
|
||||
func TestDeployment(t *testing.T) {
|
||||
namespace := "default"
|
||||
name := "fx-hello-world"
|
||||
image := "metrue/kube-hello"
|
||||
selector := map[string]string{
|
||||
"app": "fx-app",
|
||||
}
|
||||
|
||||
kubeconfig := os.Getenv("KUBECONFIG")
|
||||
if kubeconfig == "" {
|
||||
t.Skip("skip test since no KUBECONFIG given in environment variable")
|
||||
}
|
||||
|
||||
k8s, err := Create()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := k8s.GetDeployment(namespace, name); err == nil {
|
||||
t.Fatalf("should get not found error")
|
||||
}
|
||||
|
||||
replicas := int32(2)
|
||||
bindings := []types.PortBinding{
|
||||
types.PortBinding{
|
||||
ServiceBindingPort: 80,
|
||||
ContainerExposePort: 3000,
|
||||
},
|
||||
types.PortBinding{
|
||||
ServiceBindingPort: 443,
|
||||
ContainerExposePort: 3000,
|
||||
},
|
||||
}
|
||||
deployment, err := k8s.CreateDeployment(namespace, name, image, bindings, replicas, selector)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if deployment == nil {
|
||||
t.Fatalf("deploymetn should not be %v", nil)
|
||||
}
|
||||
|
||||
if deployment.Name != name {
|
||||
t.Fatalf("should get %s but got %s", name, deployment.Name)
|
||||
}
|
||||
|
||||
if *deployment.Spec.Replicas != replicas {
|
||||
t.Fatalf("should get %v but got %v", replicas, deployment.Spec.Replicas)
|
||||
}
|
||||
|
||||
if err := k8s.DeleteDeployment(namespace, name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
70
deploy/k3s/init_container.go
Normal file
70
deploy/k3s/init_container.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package k3s
|
||||
|
||||
import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// This is docker image provided by fx/contrib/docker_packer
|
||||
// it can build a Docker image with give Docker project source codes encoded with base64
|
||||
// check the detail fx/contrib/docker_packer/main.go
|
||||
const image = "metrue/fx-docker"
|
||||
|
||||
func injectInitContainer(name string, deployment *appsv1.Deployment) *appsv1.Deployment {
|
||||
configMapHasToBeReady := true
|
||||
valueInConfigMapHasToBeReady := true
|
||||
initContainer := v1.Container{
|
||||
Name: "fx-docker-build-c",
|
||||
Image: image,
|
||||
ImagePullPolicy: v1.PullAlways,
|
||||
Command: []string{
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"/usr/bin/docker_packer $(APP_META) " + name,
|
||||
}, // Maybe it can be passed by Binary data from config map
|
||||
// Args: []string{"${APP_META}"}, // function source codes and name
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
v1.VolumeMount{
|
||||
Name: "dockersock",
|
||||
MountPath: "/var/run/docker.sock",
|
||||
},
|
||||
},
|
||||
Env: []v1.EnvVar{
|
||||
v1.EnvVar{
|
||||
Name: ConfigMap.AppMetaEnvName,
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{Name: name},
|
||||
Key: ConfigMap.AppMetaEnvName,
|
||||
Optional: &valueInConfigMapHasToBeReady,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
EnvFrom: []v1.EnvFromSource{
|
||||
v1.EnvFromSource{
|
||||
ConfigMapRef: &v1.ConfigMapEnvSource{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Optional: &configMapHasToBeReady,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
volumes := []v1.Volume{
|
||||
v1.Volume{
|
||||
Name: "dockersock",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/var/run/docker.sock",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
deployment.Spec.Template.Spec.InitContainers = []apiv1.Container{initContainer}
|
||||
deployment.Spec.Template.Spec.Volumes = volumes
|
||||
return deployment
|
||||
}
|
||||
131
deploy/k3s/k3s.go
Normal file
131
deploy/k3s/k3s.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package k3s
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/metrue/fx/deploy"
|
||||
"github.com/metrue/fx/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
// K3S client
|
||||
type K3S struct {
|
||||
*kubernetes.Clientset
|
||||
}
|
||||
|
||||
const namespace = "default"
|
||||
|
||||
// Create a k8s cluster client
|
||||
func Create() (*K3S, error) {
|
||||
config, err := clientcmd.BuildConfigFromKubeconfigGetter("", clientcmd.NewDefaultClientConfigLoadingRules().Load)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &K3S{clientset}, nil
|
||||
}
|
||||
|
||||
// Deploy a image to be a service
|
||||
func (k *K3S) Deploy(
|
||||
ctx context.Context,
|
||||
fn types.Func,
|
||||
name string,
|
||||
image string,
|
||||
ports []types.PortBinding,
|
||||
) error {
|
||||
selector := map[string]string{
|
||||
"app": "fx-app-" + name,
|
||||
}
|
||||
|
||||
const replicas = int32(3)
|
||||
if _, err := k.GetDeployment(namespace, name); err != nil {
|
||||
// TODO enable passing replica from fx CLI
|
||||
if _, err := k.CreateDeployment(
|
||||
namespace,
|
||||
name,
|
||||
image,
|
||||
ports,
|
||||
replicas,
|
||||
selector,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := k.UpdateDeployment(
|
||||
namespace,
|
||||
name,
|
||||
image,
|
||||
ports,
|
||||
replicas,
|
||||
selector,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO fx should be able to know what's the target Kubernetes service platform
|
||||
// it's going to deploy to
|
||||
typ := "LoadBalancer"
|
||||
if os.Getenv("SERVICE_TYPE") != "" {
|
||||
typ = os.Getenv("SERVICE_TYPE")
|
||||
}
|
||||
|
||||
if _, err := k.GetService(namespace, name); err != nil {
|
||||
if _, err := k.CreateService(
|
||||
namespace,
|
||||
name,
|
||||
typ,
|
||||
ports,
|
||||
selector,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := k.UpdateService(
|
||||
namespace,
|
||||
name,
|
||||
typ,
|
||||
ports,
|
||||
selector,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update a service
|
||||
func (k *K3S) Update(ctx context.Context, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroy a service
|
||||
func (k *K3S) Destroy(ctx context.Context, name string) error {
|
||||
if err := k.DeleteService(namespace, name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := k.DeleteDeployment(namespace, name); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetStatus get status of a service
|
||||
func (k *K3S) GetStatus(ctx context.Context, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// List services
|
||||
func (k *K3S) List(ctx context.Context, name string) ([]types.Service, error) {
|
||||
return []types.Service{}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
_ deploy.Deployer = &K3S{}
|
||||
)
|
||||
87
deploy/k3s/service.go
Normal file
87
deploy/k3s/service.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package k3s
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/metrue/fx/types"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
intstr "k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
func generateServiceSpec(
|
||||
namespace string,
|
||||
name string,
|
||||
typ string,
|
||||
bindings []types.PortBinding,
|
||||
selector map[string]string,
|
||||
) *apiv1.Service {
|
||||
servicePorts := []apiv1.ServicePort{}
|
||||
for index, binding := range bindings {
|
||||
servicePorts = append(servicePorts, apiv1.ServicePort{
|
||||
Name: "port-" + strconv.Itoa(index),
|
||||
Protocol: apiv1.ProtocolTCP,
|
||||
Port: binding.ServiceBindingPort,
|
||||
TargetPort: intstr.FromInt(int(binding.ContainerExposePort)),
|
||||
})
|
||||
}
|
||||
|
||||
return &apiv1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
ClusterName: namespace,
|
||||
},
|
||||
Spec: apiv1.ServiceSpec{
|
||||
Ports: servicePorts,
|
||||
Type: apiv1.ServiceType(typ),
|
||||
Selector: selector,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CreateService create a service
|
||||
func (k *K3S) CreateService(
|
||||
namespace string,
|
||||
name string,
|
||||
typ string,
|
||||
bindings []types.PortBinding,
|
||||
selector map[string]string,
|
||||
) (*apiv1.Service, error) {
|
||||
service := generateServiceSpec(namespace, name, typ, bindings, selector)
|
||||
createdService, err := k.CoreV1().Services(namespace).Create(service)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return createdService, nil
|
||||
}
|
||||
|
||||
// UpdateService update a service
|
||||
// TODO this method is not perfect yet, should refactor later
|
||||
func (k *K3S) UpdateService(
|
||||
namespace string,
|
||||
name string,
|
||||
typ string,
|
||||
bindings []types.PortBinding,
|
||||
selector map[string]string,
|
||||
) (*apiv1.Service, error) {
|
||||
svc, err := k.GetService(namespace, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
svc.Spec.Selector = selector
|
||||
svc.Spec.Type = apiv1.ServiceType(typ)
|
||||
return k.CoreV1().Services(namespace).Update(svc)
|
||||
}
|
||||
|
||||
// DeleteService a service
|
||||
func (k *K3S) DeleteService(namespace string, name string) error {
|
||||
// TODO figure out the elegant way to delete a service
|
||||
options := &metav1.DeleteOptions{}
|
||||
return k.CoreV1().Services(namespace).Delete(name, options)
|
||||
}
|
||||
|
||||
// GetService get a service
|
||||
func (k *K3S) GetService(namespace string, name string) (*apiv1.Service, error) {
|
||||
return k.CoreV1().Services(namespace).Get(name, metav1.GetOptions{})
|
||||
}
|
||||
@@ -29,7 +29,7 @@ func generateDeploymentSpec(
|
||||
Name: "fx-placeholder-container-name",
|
||||
Image: image,
|
||||
Ports: ports,
|
||||
ImagePullPolicy: v1.PullNever,
|
||||
ImagePullPolicy: v1.PullIfNotPresent,
|
||||
}
|
||||
return &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -98,6 +98,5 @@ func (k *K8S) CreateDeploymentWithInitContainer(
|
||||
) (*appsv1.Deployment, error) {
|
||||
deployment := generateDeploymentSpec(name, name, ports, replicas, selector)
|
||||
updatedDeployment := injectInitContainer(name, deployment)
|
||||
fmt.Println(updatedDeployment)
|
||||
return k.AppsV1().Deployments(namespace).Create(updatedDeployment)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/metrue/fx/deploy"
|
||||
"github.com/metrue/fx/packer"
|
||||
@@ -36,6 +37,7 @@ func (k *K8S) Deploy(
|
||||
ctx context.Context,
|
||||
fn types.Func,
|
||||
name string,
|
||||
image string,
|
||||
ports []types.PortBinding,
|
||||
) error {
|
||||
// put source code of function docker project into k8s config map
|
||||
@@ -80,10 +82,9 @@ func (k *K8S) Deploy(
|
||||
|
||||
// TODO fx should be able to know what's the target Kubernetes service platform
|
||||
// it's going to deploy to
|
||||
const isOnPublicCloud = true
|
||||
typ := "LoadBalancer"
|
||||
if !isOnPublicCloud {
|
||||
typ = "NodePort"
|
||||
if os.Getenv("SERVICE_TYPE") != "" {
|
||||
typ = os.Getenv("SERVICE_TYPE")
|
||||
}
|
||||
|
||||
if _, err := k.GetService(namespace, name); err != nil {
|
||||
|
||||
@@ -40,7 +40,7 @@ module.exports = (ctx) => {
|
||||
`,
|
||||
}
|
||||
ctx := context.Background()
|
||||
if err := k8s.Deploy(ctx, fn, name, bindings); err != nil {
|
||||
if err := k8s.Deploy(ctx, fn, name, name, bindings); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# fx on Amazon Lightsai
|
||||
|
||||
* make sure your instance have docker installed and running,
|
||||
* make sure your instance can be ssh login (with user and password)
|
||||
|
||||
```
|
||||
ssh <user>@<host>
|
||||
```
|
||||
|
||||
* make sure your instance accept port 8866
|
||||
|
||||
* then you can deploy function to remote host
|
||||
|
||||
```
|
||||
DOCKER_REMOTE_HOST_ADDR=<your host> DOCKER_REMOTE_HOST_USER=<your user> DOCKER_REMOTE_HOST_PASSWORD=<your password> ./build/fx up -p 1234 test/functions/func.js
|
||||
```
|
||||
46
docs/ubuntu.md
Normal file
46
docs/ubuntu.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# fx on Ubuntu
|
||||
|
||||
> The guide is verified on Amazon Lightsail ubuntu 18.08 instance
|
||||
|
||||
## Install Docker
|
||||
|
||||
```shell
|
||||
apt-get remove -y docker docker-engine docker.io containerd runc
|
||||
apt-get update -y
|
||||
apt-get install -y apt-transport-https ca-certificates curl software-properties-common lsb-core
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
|
||||
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
||||
apt-get update -y
|
||||
apt-get install -y docker-ce
|
||||
docker run hello-world
|
||||
```
|
||||
|
||||
## Install fx
|
||||
|
||||
```shell
|
||||
$ curl -o- https://raw.githubusercontent.com/metrue/fx/master/scripts/install.sh | sudo bash
|
||||
```
|
||||
|
||||
## Deploy a function onto localhost
|
||||
|
||||
```shell
|
||||
$ cat func.js
|
||||
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello world'
|
||||
}
|
||||
|
||||
$ fx up -n test -p 2000 func.js
|
||||
$ curl 127.0.0.1:2000
|
||||
```
|
||||
|
||||
## Deploy a function onto remote host
|
||||
|
||||
* make sure your instance can be ssh login
|
||||
* make sure your instance accept port 8866
|
||||
|
||||
then you can deploy function to remote host
|
||||
|
||||
```shell
|
||||
DOCKER_REMOTE_HOST_ADDR=<your host> DOCKER_REMOTE_HOST_USER=<your user> DOCKER_REMOTE_HOST_PASSWORD=<your password> fx up -p 2000 test/functions/func.js
|
||||
```
|
||||
10
fx.go
10
fx.go
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const version = "0.8.1"
|
||||
const version = "0.8.3"
|
||||
|
||||
func init() {
|
||||
go checkForUpdate()
|
||||
@@ -95,6 +95,12 @@ func main() {
|
||||
if err := ctx.Use(middlewares.Binding); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
if err := ctx.Use(middlewares.Parse); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
if err := ctx.Use(middlewares.Build); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
return handlers.Up()(ctx)
|
||||
},
|
||||
},
|
||||
@@ -181,6 +187,6 @@ func main() {
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatalf("fx startup with fatal: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
3
go.mod
3
go.mod
@@ -6,6 +6,7 @@ require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||
github.com/apex/log v1.1.1
|
||||
github.com/briandowns/spinner v1.7.0
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v0.0.0-20190313072916-46036c230805
|
||||
github.com/docker/go-connections v0.4.0
|
||||
@@ -20,6 +21,7 @@ require (
|
||||
github.com/googleapis/gnostic v0.3.1 // indirect
|
||||
github.com/gorilla/mux v1.7.3 // indirect
|
||||
github.com/imdario/mergo v0.3.7 // indirect
|
||||
github.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434
|
||||
github.com/metrue/go-ssh-client v0.0.0-20190810064746-98a7a27048f3
|
||||
github.com/mholt/archiver v3.1.1+incompatible
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
@@ -29,6 +31,7 @@ require (
|
||||
github.com/pelletier/go-toml v1.4.0 // indirect
|
||||
github.com/pierrec/lz4 v0.0.0-20190222153722-062282ea0dcf // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/schollz/progressbar/v2 v2.14.0
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/viper v1.5.0
|
||||
github.com/stretchr/testify v1.4.0
|
||||
|
||||
10
go.sum
10
go.sum
@@ -29,6 +29,8 @@ github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/briandowns/spinner v1.7.0 h1:aan1hBBOoscry2TXAkgtxkJiq7Se0+9pt+TUWaPrB4g=
|
||||
github.com/briandowns/spinner v1.7.0/go.mod h1://Zf9tMcxfRUA36V23M6YGEAv+kECGfvpnLTnb8n4XQ=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
@@ -64,6 +66,7 @@ github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdf
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
@@ -174,11 +177,14 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434 h1:im9kkmH0WWwxzegiv18gSUJbuXR9y028rXrWuPp6Jug=
|
||||
github.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
||||
@@ -191,6 +197,8 @@ github.com/metrue/go-ssh-client v0.0.0-20190810064746-98a7a27048f3/go.mod h1:ERH
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU=
|
||||
github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
@@ -251,6 +259,8 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/schollz/progressbar/v2 v2.14.0 h1:vo7bdkI9E4/CIk9DnL5uVIaybLQiVtiCC2vO+u9j5IM=
|
||||
github.com/schollz/progressbar/v2 v2.14.0/go.mod h1:6YZjqdthH6SCZKv2rqGryrxPtfmRB/DWZxSMfCXPyD8=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
|
||||
@@ -3,15 +3,11 @@ set -e
|
||||
# ++
|
||||
# verified on Ubuntu 16.04 x64
|
||||
# ++
|
||||
user_host=$1
|
||||
|
||||
ssh ${user_host} 'bash -s' <<EOF
|
||||
apt-get remove -y docker docker-engine docker.io containerd runc
|
||||
apt-get update -y
|
||||
apt-get install -y apt-transport-https ca-certificates curl software-properties-common lsb-core
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
|
||||
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu \$(lsb_release -cs) stable"
|
||||
apt-get update -y
|
||||
apt-get install -y docker-ce
|
||||
docker run hello-world
|
||||
EOF
|
||||
apt-get remove -y docker docker-engine docker.io containerd runc
|
||||
apt-get update -y
|
||||
apt-get install -y apt-transport-https ca-certificates curl software-properties-common lsb-core
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
|
||||
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
||||
apt-get update -y
|
||||
apt-get install -y docker-ce
|
||||
docker run hello-world
|
||||
|
||||
@@ -3,11 +3,18 @@ package handlers
|
||||
import (
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/deploy"
|
||||
"github.com/metrue/fx/pkg/spinner"
|
||||
)
|
||||
|
||||
// Down command handle
|
||||
func Down() HandleFunc {
|
||||
return func(ctx *context.Context) (err error) {
|
||||
const task = "destroying"
|
||||
spinner.Start(task)
|
||||
defer func() {
|
||||
spinner.Stop(task, err)
|
||||
}()
|
||||
|
||||
cli := ctx.GetCliContext()
|
||||
services := cli.Args()
|
||||
runner := ctx.Get("deployer").(deploy.Deployer)
|
||||
|
||||
@@ -3,12 +3,19 @@ package handlers
|
||||
import (
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/deploy"
|
||||
"github.com/metrue/fx/pkg/spinner"
|
||||
"github.com/metrue/fx/utils"
|
||||
)
|
||||
|
||||
// List command handle
|
||||
func List() HandleFunc {
|
||||
return func(ctx *context.Context) error {
|
||||
return func(ctx *context.Context) (err error) {
|
||||
const task = "deploying"
|
||||
spinner.Start(task)
|
||||
defer func() {
|
||||
spinner.Stop(task, err)
|
||||
}()
|
||||
|
||||
cli := ctx.GetCliContext()
|
||||
deployer := ctx.Get("deployer").(deploy.Deployer)
|
||||
|
||||
|
||||
@@ -2,14 +2,11 @@ package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/deploy"
|
||||
"github.com/metrue/fx/pkg/spinner"
|
||||
"github.com/metrue/fx/types"
|
||||
"github.com/metrue/fx/utils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// PortRange usable port range https: //en.wikipedia.org/wiki/Ephemeral_port
|
||||
@@ -24,37 +21,29 @@ var PortRange = struct {
|
||||
// Up command handle
|
||||
func Up() HandleFunc {
|
||||
return func(ctx *context.Context) (err error) {
|
||||
const task = "deploying"
|
||||
spinner.Start(task)
|
||||
defer func() {
|
||||
spinner.Stop(task, err)
|
||||
}()
|
||||
|
||||
cli := ctx.GetCliContext()
|
||||
funcFile := cli.Args().First()
|
||||
name := cli.String("name")
|
||||
port := cli.Int("port")
|
||||
|
||||
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", 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)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadFile(funcFile)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read source failed")
|
||||
}
|
||||
lang := utils.GetLangFromFileName(funcFile)
|
||||
fn := ctx.Get("fn").(types.Func)
|
||||
image := ctx.Get("image").(string)
|
||||
deployer := ctx.Get("deployer").(deploy.Deployer)
|
||||
bindings := ctx.Get("bindings").([]types.PortBinding)
|
||||
return deployer.Deploy(
|
||||
ctx.Context,
|
||||
types.Func{Language: lang, Source: string(body)},
|
||||
fn,
|
||||
name,
|
||||
image,
|
||||
bindings,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@ func Binding(ctx *context.Context) error {
|
||||
ServiceBindingPort: 443,
|
||||
ContainerExposePort: constants.FxContainerExposePort,
|
||||
},
|
||||
types.PortBinding{
|
||||
ServiceBindingPort: int32(port),
|
||||
ContainerExposePort: constants.FxContainerExposePort,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
bindings = []types.PortBinding{
|
||||
|
||||
55
middlewares/build.go
Normal file
55
middlewares/build.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/packer"
|
||||
"github.com/metrue/fx/pkg/spinner"
|
||||
"github.com/metrue/fx/types"
|
||||
)
|
||||
|
||||
// Build image
|
||||
func Build(ctx *context.Context) (err error) {
|
||||
const task = "building"
|
||||
spinner.Start(task)
|
||||
defer func() {
|
||||
spinner.Stop(task, err)
|
||||
}()
|
||||
|
||||
cli := ctx.GetCliContext()
|
||||
name := cli.String("name")
|
||||
fn := ctx.Get("fn").(types.Func)
|
||||
docker := ctx.Get("docker").(containerruntimes.ContainerRuntime)
|
||||
workdir := fmt.Sprintf("/tmp/fx-%d", time.Now().Unix())
|
||||
defer os.RemoveAll(workdir)
|
||||
|
||||
if err := packer.PackIntoDir(fn, workdir); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := docker.BuildImage(ctx.Context, workdir, name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nameWithTag := name + ":latest"
|
||||
if err := docker.TagImage(ctx, name, nameWithTag); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Set("image", nameWithTag)
|
||||
|
||||
if os.Getenv("K3S") != "" {
|
||||
name := cli.String("name")
|
||||
username := os.Getenv("DOCKER_USERNAME")
|
||||
password := os.Getenv("DOCKER_PASSWORD")
|
||||
if username != "" && password != "" {
|
||||
if _, err := docker.PushImage(ctx.Context, name); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Set("image", username+"/"+name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
35
middlewares/parse.go
Normal file
35
middlewares/parse.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/pkg/spinner"
|
||||
"github.com/metrue/fx/types"
|
||||
"github.com/metrue/fx/utils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Parse parse input
|
||||
func Parse(ctx *context.Context) (err error) {
|
||||
const task = "parsing"
|
||||
spinner.Start(task)
|
||||
defer func() {
|
||||
spinner.Stop(task, err)
|
||||
}()
|
||||
|
||||
cli := ctx.GetCliContext()
|
||||
funcFile := cli.Args().First()
|
||||
lang := utils.GetLangFromFileName(funcFile)
|
||||
body, err := ioutil.ReadFile(funcFile)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read source failed")
|
||||
}
|
||||
fn := types.Func{
|
||||
Language: lang,
|
||||
Source: string(body),
|
||||
}
|
||||
ctx.Set("fn", fn)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -10,29 +10,32 @@ import (
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/deploy"
|
||||
dockerDeployer "github.com/metrue/fx/deploy/docker"
|
||||
k3sDeployer "github.com/metrue/fx/deploy/k3s"
|
||||
k8sDeployer "github.com/metrue/fx/deploy/kubernetes"
|
||||
"github.com/metrue/fx/pkg/spinner"
|
||||
"github.com/metrue/fx/provision"
|
||||
)
|
||||
|
||||
// Setup create k8s or docker cli
|
||||
func Setup(ctx *context.Context) (err error) {
|
||||
var deployer deploy.Deployer
|
||||
if os.Getenv("KUBECONFIG") != "" {
|
||||
deployer, err = k8sDeployer.Create()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
deployer, err = dockerDeployer.CreateClient(ctx.Context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
ctx.Set("deployer", deployer)
|
||||
const task = "setup"
|
||||
spinner.Start(task)
|
||||
defer func() {
|
||||
spinner.Stop(task, err)
|
||||
}()
|
||||
|
||||
host := os.Getenv("DOCKER_REMOTE_HOST_ADDR")
|
||||
user := os.Getenv("DOCKER_REMOTE_HOST_USER")
|
||||
passord := os.Getenv("DOCKER_REMOTE_HOST_PASSWORD")
|
||||
var docker containerruntimes.ContainerRuntime
|
||||
if host != "" && user != "" {
|
||||
provisioner := provision.NewWithHost(host, user, passord)
|
||||
if !provisioner.IsFxAgentRunning() {
|
||||
if err := provisioner.StartFxAgent(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
docker, err = dockerHTTP.Create(host, constants.AgentPort)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -45,5 +48,24 @@ func Setup(ctx *context.Context) (err error) {
|
||||
}
|
||||
ctx.Set("docker", docker)
|
||||
|
||||
var deployer deploy.Deployer
|
||||
if os.Getenv("K3S") != "" {
|
||||
deployer, err = k3sDeployer.Create()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if os.Getenv("KUBECONFIG") != "" {
|
||||
deployer, err = k8sDeployer.Create()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
deployer, err = dockerDeployer.CreateClient(ctx.Context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
ctx.Set("deployer", deployer)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
52
pkg/spinner/spiner.go
Normal file
52
pkg/spinner/spiner.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package spinner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/briandowns/spinner"
|
||||
aurora "github.com/logrusorgru/aurora"
|
||||
)
|
||||
|
||||
var s *spinner.Spinner
|
||||
|
||||
func init() {
|
||||
style := spinner.CharSets[36]
|
||||
interval := 100 * time.Millisecond
|
||||
s = spinner.New(style, interval)
|
||||
}
|
||||
|
||||
// Start spinner
|
||||
func Start(task string) {
|
||||
colors := []string{
|
||||
"red",
|
||||
"green",
|
||||
"yellow",
|
||||
"blue",
|
||||
"magenta",
|
||||
"cyan",
|
||||
"white",
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
// nolint
|
||||
s.Color(colors[rand.Intn(len(colors))])
|
||||
s.Prefix = task + " "
|
||||
if s.Active() {
|
||||
s.Restart()
|
||||
} else {
|
||||
s.Start()
|
||||
}
|
||||
}
|
||||
|
||||
// Stop spinner
|
||||
func Stop(task string, err error) {
|
||||
if err != nil {
|
||||
fmt.Println(aurora.Red("\u2717"))
|
||||
fmt.Println(aurora.Red("*****************"))
|
||||
fmt.Println(err)
|
||||
fmt.Println(aurora.Red("*****************"))
|
||||
}
|
||||
s.Stop()
|
||||
}
|
||||
Reference in New Issue
Block a user