Compare commits

..

23 Commits

Author SHA1 Message Date
Minghe
a1ccbd6cab Add not fetch fx node bas (#439)
* add node-fetch to fx-node-base image, then we can use 'fetch' in
JavaScript/Node function

* bump version
2019-12-31 13:34:48 +08:00
Minghe
33cb4ce63c verify deploy function to Google Kubernetes Engine and update Doc (#435)
* verify deploy function to Google Kubernetes Engine and update Doc
* bump version
2019-12-27 23:08:51 +08:00
Minghe
aefb4497e2 When check a file is handler or not, should take extension account, (#433)
otherewise it might be a directory, not a file
2019-12-26 20:40:57 +08:00
Minghe Huang
0047e66f10 bump version 2019-12-26 20:38:50 +08:00
Minghe Huang
6bae4254af When check a file is handler or not, should take extension account,
otherewise it might be a directory, not a file
2019-12-26 18:47:11 +08:00
Minghe
a9689993b0 Fix packr missing auto release (#431)
* install packr

* install packr

* commit generate packr file

* no need change
2019-12-24 18:30:44 +08:00
Minghe
8c0182b29f move some installation into base image (#427) 2019-12-24 11:13:10 +08:00
Minghe Huang
02d55c7143 bump version 2019-12-23 13:57:31 +08:00
Minghe
f343b537f1 upgrade go-ssh-client pkg (#426)
* upgrade go-ssh-client pkg

* bump version
2019-12-20 10:24:14 +08:00
Minghe
a84e7da65f When there is services deployed via k8s mode on Desktop Docker for Mac (#424)
with kubernetes single node cluster, "fx list" will crash since some
service has no names, but the code trying to access names at index 0

Signed-off-by: Minghe Huang <h.minghe@gmail.com>
2019-12-19 16:05:40 +08:00
Minghe
f3b64387cb dependencies should be field of brew (#423) 2019-12-19 14:28:53 +08:00
Minghe
e132435ff8 add docker to dependency (#422)
* add docker to dependency

Signed-off-by: Minghe Huang <h.minghe@gmail.com>

* bump version
2019-12-19 13:41:26 +08:00
Minghe
fb492fa6f7 Merge pull request #415 from metrue/perl
Perl
2019-12-19 10:14:22 +08:00
Minghe Huang
159714491d add perl support,
fx up func.pl

Signed-off-by: Minghe Huang <h.minghe@gmail.com>
2019-12-18 21:06:56 +08:00
Minghe Huang
64cbbc70bb Squashed commit of the following:
commit f4e3d78e516f889a0256c6ddf922922ec12bc757
Author: Minghe Huang <h.minghe@gmail.com>
Date:   Wed Dec 18 21:02:43 2019 +0800

    check error when walk directory

    Signed-off-by: Minghe Huang <h.minghe@gmail.com>
2019-12-18 21:03:05 +08:00
Minghe
d0559f627e simple and clean code (#420)
Signed-off-by: Minghe Huang <h.minghe@gmail.com>
2019-12-18 20:56:26 +08:00
Minghe
0a6784e270 fix wrong cloud type issue (#419)
Signed-off-by: Minghe Huang <h.minghe@gmail.com>
2019-12-18 20:22:03 +08:00
Minghe
b6fd3c7e98 add contributor badge (#418) 2019-12-18 17:29:16 +08:00
Minghe
1c05534071 fix KUBECONFIG no effect issue (#416)
Signed-off-by: Minghe Huang <h.minghe@gmail.com>
2019-12-18 16:59:10 +08:00
Minghe
3627d5bb40 fix ci script (#414) 2019-12-18 11:20:32 +08:00
Minghe
1f7714c1e9 Refactor kubeconfig persist logic (#412)
* add Dir function to config

* clean up

* persist the kubeconf on the fly

Signed-off-by: Minghe Huang <h.minghe@gmail.com>

* fix test

Signed-off-by: Minghe Huang <h.minghe@gmail.com>

* simplicity the cloud config fetch

Signed-off-by: Minghe Huang <h.minghe@gmail.com>
2019-12-18 10:58:34 +08:00
Minghe
d868ebf4a1 enable CI check (#410)
* enable CI check

Signed-off-by: Minghe Huang <h.minghe@gmail.com>

* lint

* clean up lint issue

* fix Makefile

* uncomment

* fix coverage

* check infrastructure during up command (#411)

* check infrastructure during up command

Signed-off-by: Minghe Huang <h.minghe@gmail.com>

* add unit test

Signed-off-by: Minghe Huang <h.minghe@gmail.com>
2019-12-17 22:54:54 +08:00
Minghe
4640379b06 clean up no use deps (#408) 2019-12-16 18:02:45 +08:00
39 changed files with 633 additions and 206 deletions

View File

@@ -29,7 +29,7 @@ jobs:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: |
export KUBECONFIG="$(kind get kubeconfig-path)"
./scripts/coverage.sh
make unit-test
bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN}
- name: build fx

View File

@@ -35,10 +35,10 @@ jobs:
docker build -t metrue/fx-d-base:latest -f ./assets/dockerfiles/base/d/Dockerfile ./assets/dockerfiles/base/d
docker push metrue/fx-d-base:latest
# - name: build and publish fx java image
# run: |
# docker build -t metrue/fx-go-base:latest -f ./assets/dockerfiles/base/java/Dockerfile ./assets/dockerfiles/base/java
# docker push metrue/fx-java-base:latest
- name: build and publish fx go image
run: |
docker build -t metrue/fx-go-base:latest -f ./assets/dockerfiles/base/go/Dockerfile ./assets/dockerfiles/base/go
docker push metrue/fx-go-base:latest
- name: build and publish fx node image
if: always()

View File

@@ -33,7 +33,7 @@ jobs:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
run: |
export KUBECONFIG="$(kind get kubeconfig-path)"
DEBUG=true go test -v ./...
make unit-test
- name: build fx
run: |

View File

@@ -32,3 +32,5 @@ brews:
caveats: ""
homepage: "https://github.com/metrue/fx"
description: "fx, a simple but powerful Function as a Service build tools"
dependencies:
- docker

View File

@@ -26,13 +26,13 @@ clean:
rm -rf ${DIST_DIR}
unit-test:
./scripts/coverage.sh
CI=true ./scripts/coverage.sh
cli-test-ci:
./scripts/test_cli.sh 'js'
cli-test:
./scripts/test_cli.sh 'js rb py go php java d rs'
./scripts/test_cli.sh 'js rb py go php java d rs pl'
http-test:
./scripts/http_test.sh

View File

@@ -4,6 +4,7 @@ fx
Poor man's function as a service.
<br/>
![CI](https://github.com/metrue/fx/workflows/ci/badge.svg)
![GitHub contributors](https://img.shields.io/github/contributors/metrue/fx)
[![CodeCov](https://codecov.io/gh/metrue/fx/branch/master/graph/badge.svg)](https://codecov.io/gh/metrue/fx)
[![Go Report Card](https://goreportcard.com/badge/github.com/metrue/fx?style=flat-square)](https://goreportcard.com/report/github.com/metrue/fx)
[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](http://godoc.org/github.com/metrue/fx)
@@ -36,6 +37,7 @@ Feel free hacking fx to support the languages not listed. Welcome to tweet me [@
| PHP | Supported | [@chlins](https://github.com/chlins)| [/examples/PHP](https://github.com/metrue/fx/tree/master/examples/functions/PHP) |
| Julia | Supported | [@matbesancon](https://github.com/matbesancon)| [/examples/Julia](https://github.com/metrue/fx/tree/master/examples/functions/Julia) |
| D | Supported | [@andre2007](https://github.com/andre2007)| [/examples/D](https://github.com/metrue/fx/tree/master/examples/functions/D) |
| Perl | Supported | fx | [/examples/Perl](https://github.com/metrue/fx/tree/master/examples/functions/Perl) |
| R | Working on [need your help](https://github.com/metrue/fx/issues/31) | ||
# Installation
@@ -204,8 +206,26 @@ But we would suggest you run `kubectl config current-context` to check if the cu
* Amazon Elastic Kubernetes Service (EKS)
TODO
* Google Kubernetes Engine (GKET)
TODO
* Google Kubernetes Engine (GKE)
First you should create a Kubernetes cluster in your GKE, then make sure your KUBECONFIG is ready in `~/.kube/config`, if not, you can run following commands,
``` shell
$ gcloud auth login
$ gcloud container clusters get-credentials <your cluster> --zone <zone> --project <project>
```
Then make sure you current context is GKE cluster, you can check it with command,
``` shell
$ kubectl config current-context
```
Then you can deploy your function onto GKE cluster with,
```shell
$ KUBECONFIG=~/.kube/config fx up examples/functions/JavaScript/func.js --name hellojs
```
* Setup your own Kubernetes cluster

4
assets/dockerfiles/base/go/Dockerfile vendored Normal file
View File

@@ -0,0 +1,4 @@
FROM golang:latest
# dependency management
RUN go get github.com/gin-gonic/gin

13
assets/dockerfiles/base/node/package-lock.json generated vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"name": "aok",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
}
}
}

View File

@@ -1,5 +1,5 @@
{
"name": "aok",
"name": "fx-node-base",
"version": "1.0.0",
"description": "",
"main": "index.js",
@@ -13,9 +13,7 @@
"get-port": "^3.2.0",
"is-generator-function": "^1.0.6",
"koa": "^2.3.0",
"koa-bodyparser": "^4.2.0"
},
"devDependencies": {
"get-port-cli": "^1.1.0"
"koa-bodyparser": "^4.2.0",
"node-fetch": "^2.6.0"
}
}

10
assets/dockerfiles/base/perl/Dockerfile vendored Normal file
View File

@@ -0,0 +1,10 @@
FROM alpine:3.4
MAINTAINER Mojolicious
ADD . .
COPY cpanfile /
ENV EV_EXTRA_DEFS -DEV_NO_ATFORK
RUN apk update && \
apk add perl perl-io-socket-ssl perl-dbd-pg perl-dev g++ make wget curl && \
curl -L https://cpanmin.us | perl - App::cpanminus && cpanm --installdeps . -M https://cpan.metacpan.org

2
assets/dockerfiles/base/perl/cpanfile vendored Normal file
View File

@@ -0,0 +1,2 @@
requires "EV";
requires "Mojolicious::Lite";

View File

@@ -0,0 +1,3 @@
FROM ruby:latest
RUN gem install sinatra

View File

@@ -7,6 +7,7 @@ import (
"os"
"os/user"
"path"
"path/filepath"
dockerInfra "github.com/metrue/fx/infra/docker"
"github.com/metrue/fx/types"
@@ -22,6 +23,7 @@ type Configer interface {
UseCloud(name string) error
View() ([]byte, error)
AddCloud(name string, meta []byte) error
Dir() (string, error)
}
// Config config of fx
@@ -191,6 +193,15 @@ func (c *Config) writeDefaultConfig() error {
return c.UseCloud("default")
}
// Dir get directory of config
func (c *Config) Dir() (string, error) {
p, err := filepath.Abs(c.configFile)
if err != nil {
return "", err
}
return path.Dir(p), nil
}
var (
_ Configer = &Config{}
)

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"os/user"
"path/filepath"
"reflect"
"testing"
@@ -49,32 +50,39 @@ func TestConfig(t *testing.T) {
t.Fatalf("should get %s but got %s", "default", cloudMeta["name"])
}
// add k8s cloud
kCloud := k8sInfra.Cloud{
Type: types.CloudTypeK8S,
Config: "sample kubeconfg",
Token: "",
URL: "",
Nodes: map[string]k8sInfra.Noder{
"master-node": &k8sInfra.Node{
IP: "1.1.1.1",
User: "user-1",
Type: "k3s-master",
Name: "master-node",
},
"agent-node-1": &k8sInfra.Node{
IP: "1.1.1.1",
User: "user-1",
Type: "k3s-agent",
Name: "agent-node-1",
},
},
n1, err := k8sInfra.CreateNode(
"1.1.1.1",
"user-1",
"k3s-master",
"master-node",
)
if err != nil {
t.Fatal(err)
}
n2, err := k8sInfra.CreateNode(
"1.1.1.1",
"user-1",
"k3s-agent",
"agent-node-1",
)
if err != nil {
t.Fatal(err)
}
kName := "k8s-1"
kubeconf := "./tmp/" + kName + "config.yml"
defer func() {
if err := os.RemoveAll(kubeconf); err != nil {
t.Fatal(err)
}
}()
// add k8s cloud
kCloud := k8sInfra.NewCloud(kubeconf, n1, n2)
kMeta, err := kCloud.Dump()
if err != nil {
t.Fatal(err)
}
kName := "k8s-1"
if err := c.AddCloud(kName, kMeta); err != nil {
t.Fatal(err)
}
@@ -108,4 +116,16 @@ func TestConfig(t *testing.T) {
t.Fatal(err)
}
fmt.Println(string(body))
dir, err := c.Dir()
if err != nil {
t.Fatal(err)
}
here, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
if dir != filepath.Join(here, "./tmp") {
t.Fatalf("should get %s but got %s", "./tmp", dir)
}
}

View File

@@ -227,17 +227,27 @@ func (api *API) ListContainer(ctx context.Context, name string) ([]types.Service
svs := make(map[string]types.Service)
for _, container := range containers {
name := "UNKNOWN"
if len(container.Names) > 0 {
name = container.Names[0]
}
port := -1
ip := "UNKNOWN"
if len(container.Ports) > 0 {
ip = container.Ports[0].IP
port = int(container.Ports[0].PublicPort)
}
// container name have extra forward slash
// https://github.com/moby/moby/issues/6705
if strings.HasPrefix(container.Names[0], fmt.Sprintf("/%s", name)) {
svs[container.Image] = types.Service{
Name: container.Names[0],
Image: container.Image,
ID: container.ID,
Host: container.Ports[0].IP,
Port: int(container.Ports[0].PublicPort),
State: container.State,
}
svs[container.Image] = types.Service{
Name: name,
Image: container.Image,
ID: container.ID,
Host: ip,
Port: port,
State: container.State,
}
}
services := []types.Service{}

36
examples/functions/Perl/README.md vendored Normal file
View File

@@ -0,0 +1,36 @@
# Make a Perl function a service with fx
Write a function like,
```perl
sub fx {
my $ctx = shift;
return 'hello fx'
}
1;
```
then deploy it with `fx up` command,
```shell
$ fx up -p 8080:3000 func.pl
```
test it using `curl`
```shell
$ curl 127.0.0.1:8080
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 11
Content-Type: text/plain; charset=utf-8
Date: Tue, 06 Aug 2019 15:58:41 GMT
hello fx
```
### ctx
The `ctx` object is exactly the [Controller](https://mojolicious.org/perldoc/Mojolicious/Controller) of [Mojolicious](https://mojolicious.org/perldoc/Mojolicious) framework.

6
examples/functions/Perl/func.pl vendored Normal file
View File

@@ -0,0 +1,6 @@
sub fx {
my $ctx = shift;
return 'hello fx'
}
1;

3
fx.go
View File

@@ -14,9 +14,10 @@ import (
"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.8.74"
const version = "0.8.85"
func init() {
go checkForUpdate()

12
go.mod
View File

@@ -13,6 +13,7 @@ require (
github.com/docker/go-units v0.3.3 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/gin-gonic/gin v1.4.0
github.com/gobuffalo/envy v1.8.1 // indirect
github.com/gobuffalo/packr v1.30.1
github.com/golang/mock v1.3.1
github.com/golang/snappy v0.0.1 // indirect
@@ -22,7 +23,7 @@ require (
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-20191209160027-5773243a8bc9
github.com/metrue/go-ssh-client v0.0.0-20191219103445-1f07b67e2b29
github.com/mholt/archiver v3.1.1+incompatible
github.com/mitchellh/go-homedir v1.1.0
github.com/morikuni/aec v1.0.0 // indirect
@@ -34,16 +35,17 @@ require (
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2
github.com/pierrec/lz4 v0.0.0-20190222153722-062282ea0dcf // indirect
github.com/pkg/errors v0.8.1
github.com/rogpeppe/go-internal v1.5.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.6.1
github.com/stretchr/testify v1.4.0
github.com/ugorji/go v1.1.7 // indirect
github.com/urfave/cli v1.22.2
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 // indirect
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
google.golang.org/grpc v1.21.0 // indirect
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915 // indirect
golang.org/x/sys v0.0.0-20191223224216-5a3cf8467b4e // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.2.7
gopkg.in/yaml.v2 v2.2.7 // indirect
gotest.tools v2.2.0+incompatible // indirect
k8s.io/api v0.0.0-20190925180651-d58b53da08f5
k8s.io/apimachinery v0.0.0-20190925235427-62598f38f24e

35
go.sum
View File

@@ -1,5 +1,6 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
@@ -87,11 +88,15 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.8.1 h1:RUr68liRvs0TS1D5qdW3mQv2SjAsu1QWMCx1tG4kDjs=
github.com/gobuffalo/envy v1.8.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
github.com/gobuffalo/logger v1.0.0 h1:xw9Ko9EcC5iAFprrjJ6oZco9UpzS5MQ4jAwghsLHdy4=
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
github.com/gobuffalo/packr/v2 v2.5.1 h1:TFOeY2VoGamPjQLiNDT3mn//ytzk236VMO2j7iHxJR4=
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@@ -131,6 +136,7 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsC
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@@ -147,6 +153,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
@@ -158,6 +165,7 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/karrick/godirwalk v1.10.12 h1:BqUm+LuJcXjGv1d2mj3gBiQyrQ57a0rYoAmhvJQ7RDU=
@@ -191,15 +199,11 @@ github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/metrue/go-ssh-client v0.0.0-20191125030649-4ac058ee958b h1:JGD0sJ44XzhsT1voOg00zji4ubuMNcVNK3m7d9GI88k=
github.com/metrue/go-ssh-client v0.0.0-20191125030649-4ac058ee958b/go.mod h1:ERHOEBrDy6+8vfoJjjmhdmBpOzdvvP7bLtwYTTK6LOs=
github.com/metrue/go-ssh-client v0.0.0-20191209160027-5773243a8bc9 h1:HHfMhG77ZLn3FOH3AGXW/F5RpAABVH6Fr5mVZZ97S6w=
github.com/metrue/go-ssh-client v0.0.0-20191209160027-5773243a8bc9/go.mod h1:aPG/JtXTyLliKDDlkv+nzHbSbz2p2CBMAjNJRK4uhzY=
github.com/metrue/go-ssh-client v0.0.0-20191219103445-1f07b67e2b29 h1:ENoMPMVc24XbBuVZ7guZmTB/7MSd+vqOkImSu9UUiJw=
github.com/metrue/go-ssh-client v0.0.0-20191219103445-1f07b67e2b29/go.mod h1:aPG/JtXTyLliKDDlkv+nzHbSbz2p2CBMAjNJRK4uhzY=
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=
@@ -222,8 +226,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.3 h1:i0LBnzgiChAWHJYTQAZJDOgf8MNxAVYZJ2m63SIDimI=
github.com/olekukonko/tablewriter v0.0.3/go.mod h1:YZeBtGzYYEsCHp2LST/u/0NDwGkRoBtmn1cIWCJiS6M=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -237,7 +239,9 @@ github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVo
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc=
github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95 h1:+OLn68pqasWca0z5ryit9KGfp3sUsW4Lqg32iRMJyzs=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@@ -267,6 +271,9 @@ github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.5.1 h1:asQ0uD7BN9RU5Im41SEEZTwCi/zAXdMOLS3npYaos2g=
github.com/rogpeppe/go-internal v1.5.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
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=
@@ -279,8 +286,10 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -297,6 +306,8 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
@@ -325,8 +336,6 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
@@ -347,6 +356,8 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915 h1:aJ0ex187qoXrJHPo8ZasVTASQB7llQP6YeNzgDALPRk=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -394,8 +405,8 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191223224216-5a3cf8467b4e h1:z2Flw7sLy7DxaQi3zDOvI9X+Kb06+G9iZJlkEyHvujE=
golang.org/x/sys v0.0.0-20191223224216-5a3cf8467b4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@@ -2,6 +2,7 @@ package handlers
import (
"fmt"
"path/filepath"
"strings"
"github.com/metrue/fx/config"
@@ -11,7 +12,7 @@ import (
"github.com/metrue/fx/pkg/spinner"
)
func setupK8S(masterInfo string, agentsInfo string) ([]byte, error) {
func setupK8S(configDir string, name, masterInfo string, agentsInfo string) ([]byte, error) {
info := strings.Split(masterInfo, "@")
if len(info) != 2 {
return nil, fmt.Errorf("incorrect master info, should be <user>@<ip> format")
@@ -36,7 +37,8 @@ func setupK8S(masterInfo string, agentsInfo string) ([]byte, error) {
nodes = append(nodes, node)
}
}
cloud := k8sInfra.NewCloud(nodes...)
kubeconfigPath := filepath.Join(configDir, name+".kubeconfig")
cloud := k8sInfra.NewCloud(kubeconfigPath, nodes...)
if err := cloud.Provision(); err != nil {
return nil, err
}
@@ -91,7 +93,11 @@ func Setup(ctx context.Contexter) (err error) {
switch strings.ToLower(typ) {
case "k8s":
kubeconf, err := setupK8S(cli.String("master"), cli.String("agents"))
dir, err := fxConfig.Dir()
if err != nil {
return err
}
kubeconf, err := setupK8S(dir, name, cli.String("master"), cli.String("agents"))
if err != nil {
return err
}

View File

@@ -2,8 +2,11 @@ package docker
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/metrue/fx/infra"
"github.com/metrue/fx/types"
@@ -68,18 +71,18 @@ func Load(meta []byte) (*Cloud, error) {
// Provision a host
func (c *Cloud) Provision() error {
if err := c.sshClient.RunCommand(infra.Scripts["docker_version"].(string), ssh.CommandOptions{}); err != nil {
if err := c.sshClient.RunCommand(infra.Scripts["install_docker"].(string), ssh.CommandOptions{}); err != nil {
if err := c.runCmd(infra.Scripts["docker_version"].(string)); err != nil {
if err := c.runCmd(infra.Scripts["install_docker"].(string)); err != nil {
return err
}
if err := c.sshClient.RunCommand(infra.Scripts["start_dockerd"].(string), ssh.CommandOptions{}); err != nil {
if err := c.runCmd(infra.Scripts["start_dockerd"].(string)); err != nil {
return err
}
}
if err := c.sshClient.RunCommand(infra.Scripts["check_fx_agent"].(string), ssh.CommandOptions{}); err != nil {
if err := c.sshClient.RunCommand(infra.Scripts["start_fx_agent"].(string), ssh.CommandOptions{}); err != nil {
if err := c.runCmd(infra.Scripts["check_fx_agent"].(string)); err != nil {
if err := c.runCmd(infra.Scripts["start_fx_agent"].(string)); err != nil {
return err
}
}
@@ -103,11 +106,48 @@ func (c *Cloud) Dump() ([]byte, error) {
return json.Marshal(c)
}
// IsHealth check if cloud is in health
func (c *Cloud) IsHealth() (bool, error) {
if err := c.runCmd(infra.Scripts["check_fx_agent"].(string)); err != nil {
if err := c.runCmd(infra.Scripts["start_fx_agent"].(string)); err != nil {
return false, err
}
}
return true, nil
}
// NOTE only using for unit testing
func (c *Cloud) setsshClient(client ssh.Clienter) {
c.sshClient = client
}
// nolint:unparam
func (c *Cloud) runCmd(script string, options ...ssh.CommandOptions) error {
option := ssh.CommandOptions{}
if len(options) >= 1 {
option = options[0]
}
local := c.IP == "127.0.0.1" || c.IP == "localhost"
if local && os.Getenv("CI") == "" {
params := strings.Split(script, " ")
if len(params) == 0 {
return fmt.Errorf("invalid script: %s", script)
}
// nolint
cmd := exec.Command(params[0], params[1:]...)
cmd.Stdout = option.Stdout
cmd.Stderr = option.Stderr
err := cmd.Run()
if err != nil {
return err
}
return nil
}
return c.sshClient.RunCommand(script, option)
}
// NOTE the reason putting sshkey() and sshport here inside node.go is because
// ssh key and ssh port is related to node it self, we may extend this in future
func sshkey() (string, error) {

View File

@@ -12,7 +12,7 @@ import (
"github.com/mitchellh/go-homedir"
)
func TestCloud(t *testing.T) {
func TestCloudProvision(t *testing.T) {
t.Run("fx agent started", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
@@ -60,6 +60,61 @@ func TestCloud(t *testing.T) {
})
}
func TestCloudIsHealth(t *testing.T) {
t.Run("agent started", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cloud := New("127.0.0.1", "fx", "master")
sshClient := sshMocks.NewMockClienter(ctrl)
cloud.setsshClient(sshClient)
sshClient.EXPECT().RunCommand(infra.Scripts["check_fx_agent"].(string), ssh.CommandOptions{}).Return(nil)
ok, err := cloud.IsHealth()
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatalf("cloud should be healthy")
}
})
t.Run("agent not started, and retart ok", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cloud := New("127.0.0.1", "fx", "master")
sshClient := sshMocks.NewMockClienter(ctrl)
cloud.setsshClient(sshClient)
sshClient.EXPECT().RunCommand(infra.Scripts["check_fx_agent"].(string), ssh.CommandOptions{}).Return(fmt.Errorf("fx agent not started"))
sshClient.EXPECT().RunCommand(infra.Scripts["start_fx_agent"].(string), ssh.CommandOptions{}).Return(nil)
ok, err := cloud.IsHealth()
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatalf("cloud should be healthy")
}
})
t.Run("agent not started, but restart failed", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cloud := New("127.0.0.1", "fx", "master")
sshClient := sshMocks.NewMockClienter(ctrl)
cloud.setsshClient(sshClient)
sshClient.EXPECT().RunCommand(infra.Scripts["check_fx_agent"].(string), ssh.CommandOptions{}).Return(fmt.Errorf("fx agent not started"))
sshClient.EXPECT().RunCommand(infra.Scripts["start_fx_agent"].(string), ssh.CommandOptions{}).Return(fmt.Errorf("fx agent started failed"))
ok, err := cloud.IsHealth()
if err == nil {
t.Fatal("should got failed starting")
}
if ok {
t.Fatalf("cloud should not be healthy")
}
})
}
func TestGetSSHKeyFile(t *testing.T) {
t.Run("defaut", func(t *testing.T) {
defau, err := sshkey()

View File

@@ -12,6 +12,7 @@ type Clouder interface {
GetConfig() (string, error)
GetType() string
Dump() ([]byte, error)
IsHealth() (bool, error)
}
// Deployer deploy interface

View File

@@ -3,18 +3,22 @@ package k8s
import (
"encoding/json"
"fmt"
"io/ioutil"
"github.com/metrue/fx/infra"
"github.com/metrue/fx/types"
"github.com/metrue/fx/utils"
)
// Cloud define a cloud
type Cloud struct {
Config string `json:"config"`
URL string `json:"url"`
Token string `json:"token"`
Type string `json:"type"`
Nodes map[string]Noder `json:"nodes"`
// Define where is the location of kubeconf would be saved to
KubeConfig string `json:"config"`
Type string `json:"type"`
Nodes map[string]Noder `json:"nodes"`
token string
url string
}
// Load a cloud from config
@@ -39,14 +43,16 @@ func Load(meta []byte, messup ...func(n Noder) (Noder, error)) (*Cloud, error) {
}
// NewCloud new a cloud
func NewCloud(node ...Noder) *Cloud {
func NewCloud(kubeconf string, node ...Noder) *Cloud {
nodes := map[string]Noder{}
for _, n := range node {
nodes[n.GetName()] = n
}
return &Cloud{
Type: types.CloudTypeK8S,
Nodes: nodes,
KubeConfig: kubeconf,
Type: types.CloudTypeK8S,
Nodes: nodes,
}
}
@@ -64,7 +70,7 @@ func (c *Cloud) Provision() error {
// when it's k3s cluster
if master != nil {
c.URL = fmt.Sprintf("https://%s:6443", master.GetIP())
c.url = fmt.Sprintf("https://%s:6443", master.GetIP())
if err := master.Provision(map[string]string{}); err != nil {
return err
}
@@ -73,22 +79,19 @@ func (c *Cloud) Provision() error {
if err != nil {
return err
}
c.Token = tok
c.token = tok
config, err := master.GetConfig()
if err != nil {
return err
}
c.Config = config
}
// when it's a docker agent
if len(agents) == 1 && agents[0].GetType() == NodeTypeDocker {
config, err := agents[0].GetConfig()
if err != nil {
if err := utils.EnsureFile(c.KubeConfig); err != nil {
return err
}
if err := ioutil.WriteFile(c.KubeConfig, []byte(config), 0666); err != nil {
return err
}
c.Config = config
}
if len(agents) > 0 {
@@ -98,8 +101,8 @@ func (c *Cloud) Provision() error {
for _, agent := range agents {
go func(node Noder) {
errCh <- node.Provision(map[string]string{
"url": c.URL,
"token": c.Token,
"url": c.url,
"token": c.token,
})
}(agent)
}
@@ -118,8 +121,8 @@ func (c *Cloud) Provision() error {
func (c *Cloud) AddNode(n Noder, skipProvision bool) error {
if !skipProvision {
if err := n.Provision(map[string]string{
"url": c.URL,
"token": c.Token,
"url": c.url,
"token": c.token,
}); err != nil {
return err
}
@@ -173,16 +176,16 @@ func (c *Cloud) UnmarshalJSON(data []byte) error {
} else if k == "token" {
tok, ok := v.(string)
if ok {
c.Token = tok
c.token = tok
} else {
c.Token = ""
c.token = ""
}
} else if k == "config" {
config, ok := v.(string)
if ok {
c.Config = config
c.KubeConfig = config
} else {
c.Config = ""
c.KubeConfig = ""
}
} else if k == "type" {
typ, ok := v.(string)
@@ -194,9 +197,9 @@ func (c *Cloud) UnmarshalJSON(data []byte) error {
} else if k == "url" {
url, ok := v.(string)
if ok {
c.URL = url
c.url = url
} else {
c.URL = ""
c.url = ""
}
}
}
@@ -217,17 +220,17 @@ func (c *Cloud) MarshalJSON() ([]byte, error) {
}
body, err := json.Marshal(struct {
URL string `json:"url"`
Config string `json:"config"`
Type string `json:"type"`
Token string `json:"token"`
Nodes map[string]Node `json:"nodes"`
URL string `json:"url"`
KubeConfig string `json:"config"`
Type string `json:"type"`
Token string `json:"token"`
Nodes map[string]Node `json:"nodes"`
}{
URL: c.URL,
Config: c.Config,
Type: c.Type,
Token: c.Token,
Nodes: nodes,
KubeConfig: c.KubeConfig,
Type: c.Type,
Token: c.token,
URL: c.url,
Nodes: nodes,
})
if err != nil {
return nil, err
@@ -248,13 +251,18 @@ func (c *Cloud) Dump() ([]byte, error) {
// GetConfig get config
func (c *Cloud) GetConfig() (string, error) {
if c.Config != "" {
return c.Config, nil
if c.KubeConfig != "" {
return c.KubeConfig, nil
}
if err := c.Provision(); err != nil {
return "", err
}
return c.Config, nil
return c.KubeConfig, nil
}
// IsHealth check if cloud is in health
func (c *Cloud) IsHealth() (bool, error) {
return true, nil
}
var (

View File

@@ -3,6 +3,8 @@ package k8s
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/golang/mock/gomock"
@@ -25,6 +27,13 @@ func TestLoad(t *testing.T) {
})
t.Run("only master node", func(t *testing.T) {
kubeconfig := "./kubeconfig.yml"
defer func() {
if err := os.RemoveAll("./kubeconfig.yml"); err != nil {
t.Fatal(err)
}
}()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
@@ -36,18 +45,19 @@ func TestLoad(t *testing.T) {
name := "master"
ip := "127.0.0.1"
user := "testuser"
kubeconfContent := "sample-content"
master.EXPECT().GetName().Return(name)
master.EXPECT().GetType().Return(typ).Times(2)
master.EXPECT().GetIP().Return(ip).Times(2)
master.EXPECT().GetUser().Return(user)
master.EXPECT().GetConfig().Return("sample-config", nil)
master.EXPECT().GetConfig().Return(kubeconfContent, nil)
claud := &Cloud{
Config: "",
URL: "",
Token: "",
Type: "k8s",
Nodes: map[string]Noder{"master-node": master},
KubeConfig: kubeconfig,
Type: "k8s",
url: "",
token: "",
Nodes: map[string]Noder{"master-node": master},
}
meta, err := json.Marshal(claud)
@@ -67,9 +77,24 @@ func TestLoad(t *testing.T) {
if err := cloud.Provision(); err != nil {
t.Fatal(err)
}
content, err := ioutil.ReadFile(claud.KubeConfig)
if err != nil {
t.Fatal(err)
}
if string(content) != kubeconfContent {
t.Fatalf("should get %s but got %s", kubeconfContent, content)
}
})
t.Run("one master node and one agent", func(t *testing.T) {
kubeconfig := "./kubeconfig.yml"
defer func() {
if err := os.RemoveAll("./kubeconfig.yml"); err != nil {
t.Fatal(err)
}
}()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
@@ -85,10 +110,11 @@ func TestLoad(t *testing.T) {
name := "master"
ip := "127.0.0.1"
user := "testuser"
kubeconfContent := "sample-config"
master.EXPECT().GetName().Return(name)
master.EXPECT().GetType().Return(typ).Times(2)
master.EXPECT().GetIP().Return(ip).Times(3)
master.EXPECT().GetConfig().Return("sample-config", nil)
master.EXPECT().GetConfig().Return(kubeconfContent, nil)
master.EXPECT().GetUser().Return(user)
nodeType := NodeTypeAgent
@@ -96,18 +122,18 @@ func TestLoad(t *testing.T) {
nodeIP := "12.12.12.12"
nodeUser := "testuser"
node.EXPECT().GetName().Return(nodeName)
node.EXPECT().GetType().Return(nodeType).Times(3)
node.EXPECT().GetType().Return(nodeType).Times(2)
node.EXPECT().GetIP().Return(nodeIP)
node.EXPECT().GetUser().Return(nodeUser)
url := fmt.Sprintf("https://%s:6443", master.GetIP())
tok := "tok-1"
claud := &Cloud{
Config: "",
URL: url,
Token: tok,
Type: "k8s",
Nodes: map[string]Noder{"master-node": master, "agent-node": node},
KubeConfig: kubeconfig,
url: url,
token: tok,
Type: "k8s",
Nodes: map[string]Noder{"master-node": master, "agent-node": node},
}
meta, err := json.Marshal(claud)
if err != nil {
@@ -125,12 +151,19 @@ func TestLoad(t *testing.T) {
master.EXPECT().Provision(map[string]string{}).Return(nil)
master.EXPECT().GetToken().Return(tok, nil)
node.EXPECT().Provision(map[string]string{
"url": cloud.URL,
"token": cloud.Token,
"url": cloud.url,
"token": cloud.token,
}).Return(nil)
if err := cloud.Provision(); err != nil {
t.Fatal(err)
}
content, err := ioutil.ReadFile(claud.KubeConfig)
if err != nil {
t.Fatal(err)
}
if string(content) != kubeconfContent {
t.Fatalf("should get %s but got %s", kubeconfContent, content)
}
})
}

View File

@@ -23,6 +23,9 @@ func Build(ctx context.Contexter) (err error) {
}()
workdir := fmt.Sprintf("/tmp/fx-%d", time.Now().Unix())
if err := utils.EnsureDir(workdir); err != nil {
return err
}
defer os.RemoveAll(workdir)
// Cases supports
@@ -66,11 +69,11 @@ func Build(ctx context.Contexter) (err error) {
if err := docker.BuildImage(ctx.GetContext(), workdir, name); err != nil {
return err
}
nameWithTag := name + ":latest"
if err := docker.TagImage(ctx.GetContext(), name, nameWithTag); err != nil {
return err
}
ctx.Set("image", nameWithTag)
}

View File

@@ -39,6 +39,16 @@ func Provision(ctx context.Contexter) (err error) {
if err != nil {
return err
}
ok, err := cloud.IsHealth()
if err != nil {
return err
}
if !ok {
return fmt.Errorf("infrastrure is not health, please try to run create infrastructure use 'fx infra create ...' command")
}
ctx.Set("cloud", cloud)
conf, err := cloud.GetConfig()
@@ -47,12 +57,12 @@ func Provision(ctx context.Contexter) (err error) {
}
var deployer infra.Deployer
if os.Getenv("KUBECONFIG") != "" {
deployer, err = k8sInfra.CreateDeployer(os.Getenv("KUBECONFIG"))
if err != nil {
return err
}
cloudType = types.CloudTypeK8S
conf = os.Getenv("KUBECONFIG")
ctx.Set("cloud_type", types.CloudTypeK8S)
} else if cloud.GetType() == types.CloudTypeDocker {
}
if cloudType == types.CloudTypeDocker {
var meta map[string]string
if err := json.Unmarshal([]byte(conf), &meta); err != nil {
return err
@@ -68,12 +78,8 @@ func Provision(ctx context.Contexter) (err error) {
if err != nil {
return err
}
} else if cloud.GetType() == types.CloudTypeK8S {
kubeconfig, err := fxConfig.GetKubeConfig()
if err != nil {
return err
}
deployer, err = k8sInfra.CreateDeployer(kubeconfig)
} else if cloudType == types.CloudTypeK8S {
deployer, err = k8sInfra.CreateDeployer(conf)
if err != nil {
return err
}

File diff suppressed because one or more lines are too long

View File

@@ -1,11 +1,8 @@
FROM golang:latest
FROM metrue/fx-go-base
COPY . /go/src/github.com/metrue/fx
WORKDIR /go/src/github.com/metrue/fx
# dependency management
RUN go get github.com/gin-gonic/gin
RUN go build -ldflags "-w -s" -o fx fx.go app.go
EXPOSE 3000

6
packer/images/perl/Dockerfile vendored Normal file
View File

@@ -0,0 +1,6 @@
FROM metrue/fx-perl-base
ADD . .
EXPOSE 3000
CMD ["perl", "app.pl", "daemon"]

17
packer/images/perl/app.pl vendored Normal file
View File

@@ -0,0 +1,17 @@
use Mojolicious::Lite;
require "./fx.pl";
get '/' => sub {
my $ctx = shift;
my $res = fx($ctx);
$ctx->render(json => $res);
};
post '/' => sub {
my $ctx = shift;
my $res = fx($ctx);
$ctx->render(json => $res);
};
app->start;

6
packer/images/perl/fx.pl vendored Normal file
View File

@@ -0,0 +1,6 @@
sub fx {
my $ctx = shift;
return 'hello fx'
}
1;

View File

@@ -1,6 +1,4 @@
FROM ruby:latest
RUN gem install sinatra
FROM metrue/fx-ruby-base
COPY . .
EXPOSE 3000

View File

@@ -26,17 +26,23 @@ func Pack(output string, input ...string) error {
return fmt.Errorf("source file or directory required")
}
var lang string
var language string
for _, f := range input {
if utils.IsRegularFile(f) {
lang = langFromFileName(f)
lang, err := langFromFileName(f)
if err == nil {
language = lang
}
} else if utils.IsDir(f) {
if err := filepath.Walk(f, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if utils.IsRegularFile(path) {
lang = langFromFileName(path)
lang, err := langFromFileName(path)
if err == nil {
language = lang
}
}
return nil
}); err != nil {
@@ -45,11 +51,11 @@ func Pack(output string, input ...string) error {
}
}
if lang == "" {
if language == "" {
return fmt.Errorf("could not tell programe language of your input source codes")
}
if err := restore(output, lang); err != nil {
if err := restore(output, language); err != nil {
return err
}
@@ -64,7 +70,7 @@ func Pack(output string, input ...string) error {
return err
}
if isHandler(path) {
if isHandler(path, language) {
if err := copy.Copy(input[0], path); err != nil {
return err
}
@@ -78,8 +84,8 @@ func Pack(output string, input ...string) error {
return nil
}
if !hasFxHandleFile(input...) {
msg := `it requires a fx handle file when input is not a single file function, e.g.  
if !hasFxHandleFile(language, input...) {
msg := `it requires a fx handle file when input is not a single file function, e.g.
fx.go for Golang
Fx.java for Java
fx.php for PHP
@@ -87,6 +93,7 @@ fx.py for Python
fx.js for JavaScript or Node
fx.rb for Ruby
fx.jl for Julia
fx.pl for Perl
fx.d for D`
return fmt.Errorf(msg)
}
@@ -154,54 +161,6 @@ func merge(dest string, input ...string) error {
return nil
}
func isHandler(name string) bool {
basename := filepath.Base(name)
nameWithoutExt := strings.TrimSuffix(basename, filepath.Ext(basename))
return nameWithoutExt == "fx" ||
nameWithoutExt == "Fx" || // Fx is for Java
nameWithoutExt == "mod" // mod.rs is for Rust
}
func langFromFileName(fileName string) string {
extLangMap := map[string]string{
".js": "node",
".go": "go",
".rb": "ruby",
".py": "python",
".php": "php",
".jl": "julia",
".java": "java",
".d": "d",
".rs": "rust",
}
return extLangMap[filepath.Ext(fileName)]
}
func hasFxHandleFile(input ...string) bool {
var handleFile string
for _, file := range input {
if utils.IsRegularFile(file) && isHandler(file) {
handleFile = file
break
} else if utils.IsDir(file) {
if err := filepath.Walk(file, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if utils.IsRegularFile(path) && isHandler(info.Name()) {
handleFile = path
}
return nil
}); err != nil {
return false
}
}
}
return handleFile != ""
}
// PackIntoK8SConfigMapFile pack function a K8S config map file
func PackIntoK8SConfigMapFile(dir string) (string, error) {
tree := map[string]string{}

76
packer/rules.go Normal file
View File

@@ -0,0 +1,76 @@
package packer
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/metrue/fx/utils"
)
// ExtLangMapping file extension mapping with programming language
var ExtLangMapping = map[string]string{
".js": "node",
".go": "go",
".rb": "ruby",
".py": "python",
".php": "php",
".jl": "julia",
".java": "java",
".d": "d",
".rs": "rust",
".pl": "perl",
}
func isHandler(name string, lang string) bool {
basename := filepath.Base(name)
nameWithoutExt := strings.TrimSuffix(basename, filepath.Ext(basename))
if ExtLangMapping[filepath.Ext(basename)] != lang {
return false
}
return (nameWithoutExt == "fx" ||
// Fx is for Java
nameWithoutExt == "Fx" ||
// mod.rs is for Rust)
nameWithoutExt == "mod")
}
func langFromFileName(fileName string) (string, error) {
if fileName == "" {
return "", fmt.Errorf("file name should not be empty")
}
ext := filepath.Ext(fileName)
lang, ok := ExtLangMapping[ext]
if !ok {
return "", fmt.Errorf("could not find corresponse programming language for file extension %s", ext)
}
return lang, nil
}
func hasFxHandleFile(lang string, input ...string) bool {
var handleFile string
for _, file := range input {
if utils.IsRegularFile(file) && isHandler(file, lang) {
handleFile = file
break
} else if utils.IsDir(file) {
if err := filepath.Walk(file, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if utils.IsRegularFile(path) && isHandler(info.Name(), lang) {
handleFile = path
}
return nil
}); err != nil {
return false
}
}
}
return handleFile != ""
}

61
packer/rules_test.go Normal file
View File

@@ -0,0 +1,61 @@
package packer
import "testing"
func TestLangFromFileName(t *testing.T) {
cases := []struct {
name string
lang string
}{
{
name: "a.js",
lang: "node",
},
{
name: "a.py",
lang: "python",
},
{
name: "a.go",
lang: "go",
},
{
name: "a.rb",
lang: "ruby",
},
{
name: "a.php",
lang: "php",
},
{
name: "a.jl",
lang: "julia",
},
{
name: "a.d",
lang: "d",
},
{
name: "a.rs",
lang: "rust",
},
{
name: "a.java",
lang: "java",
},
{
name: "a.pl",
lang: "perl",
},
}
for _, c := range cases {
lang, err := langFromFileName(c.name)
if err != nil {
t.Fatal(err)
}
if lang != c.lang {
t.Fatalf("should get %s but got %s", c.lang, lang)
}
}
}

View File

@@ -10,7 +10,7 @@ run() {
local port=$2
# localhost
$fx up --name ${service}_${lang} --port ${port} --healthcheck test/functions/func.${lang}
$fx list # | jq ''
$fx list
$fx down ${service}_${lang} || true
}
@@ -32,7 +32,7 @@ export_image() {
if [[ "$DOCKER_REMOTE_HOST_ADDR" != "" ]];then
cloud_name='fx-remote-docker-host'
$fx infra create --name ${cloud_name} --type docker --host ${DOCKER_REMOTE_HOST_USER}@${DOCKER_REMOTE_HOST_ADDR}
$fx use ${cloud_name}
$fx infra use ${cloud_name}
fi
port=20000

View File

@@ -9,6 +9,9 @@ import (
func HasDockerfile(dir string) bool {
var dockerfile string
if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// nolint
if info.Mode().IsRegular() && info.Name() == "Dockerfile" {
dockerfile = path