Compare commits
12 Commits
0.9.2-alph
...
0.9.33-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
779679a809 | ||
|
|
ec49f75717 | ||
|
|
23598ce1c6 | ||
|
|
2831949814 | ||
|
|
e712e3d0e2 | ||
|
|
7df1c64740 | ||
|
|
91325f9ca3 | ||
|
|
7326325c19 | ||
|
|
59e195fa94 | ||
|
|
5ed4f8795a | ||
|
|
a02e7e66af | ||
|
|
9c1d093bb8 |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -19,8 +19,7 @@ jobs:
|
||||
|
||||
- name: lint
|
||||
run: |
|
||||
docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint \
|
||||
golangci-lint run -v
|
||||
make lint
|
||||
|
||||
- name: unit test
|
||||
env:
|
||||
@@ -43,11 +42,6 @@ jobs:
|
||||
make docker-build
|
||||
make test
|
||||
# make docker-publish #TODO in release workflow
|
||||
- name: test fx docker cloud
|
||||
run: |
|
||||
make start_docker_infra
|
||||
make test_docker_infra
|
||||
make stop_docker_infra
|
||||
|
||||
- name: test fx cli
|
||||
env:
|
||||
|
||||
@@ -7,6 +7,8 @@ run:
|
||||
- examples
|
||||
- api/images
|
||||
- test/functions
|
||||
- assets/
|
||||
- bundler/go/assets
|
||||
linters:
|
||||
enable:
|
||||
- goimports
|
||||
|
||||
3
Makefile
3
Makefile
@@ -4,7 +4,8 @@ DOCKER_REMOTE_HOST_ADDR ?= "127.0.0.1"
|
||||
DOCKER_REMOTE_HOST_USER ?= $(whoami)
|
||||
|
||||
lint:
|
||||
golangci-lint run
|
||||
docker pull golangci/golangci-lint
|
||||
docker run --rm -v $(CURDIR):/app -w /app golangci/golangci-lint golangci-lint run -v
|
||||
|
||||
generate:
|
||||
packr
|
||||
|
||||
67
README.md
67
README.md
@@ -15,7 +15,6 @@ Poor man's function as a service.
|
||||
- [Introduction](#introduction)
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Manage Infrastructure](#manage-infrastructure)
|
||||
- [Contribute](#contribute)
|
||||
|
||||
|
||||
@@ -71,8 +70,6 @@ You can go the release page to [download](https://github.com/metrue/fx/releases)
|
||||
|
||||
## Usage
|
||||
|
||||
Make sure [Docker](https://docs.docker.com/engine/installation/) installed and running on your server first. then type `fx -h` on your terminal to check out basic help.
|
||||
|
||||
```
|
||||
NAME:
|
||||
fx - makes function as a service
|
||||
@@ -81,16 +78,13 @@ USAGE:
|
||||
fx [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
0.8.7
|
||||
0.9.33
|
||||
|
||||
COMMANDS:
|
||||
infra manage infrastructure
|
||||
up deploy a function
|
||||
down destroy a service
|
||||
list, ls list deployed services
|
||||
call run a function instantly
|
||||
image manage image of service
|
||||
doctor health check for fx
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
@@ -98,22 +92,40 @@ GLOBAL OPTIONS:
|
||||
--version, -v print the version
|
||||
```
|
||||
|
||||
### Deploy your function to Docker
|
||||
### Deploy function
|
||||
|
||||
#### Local Docker environment
|
||||
|
||||
By default, function will be deployed on localhost, make sure [Docker](https://docs.docker.com/engine/installation/) installed and running on your server first. then type `fx -h` on your terminal to check out basic help.
|
||||
|
||||
```
|
||||
$ fx up --name hello-fx ./examples/functions/JavaScript/func.js
|
||||
$ fx up --name hello ./examples/functions/JavaScript/func.js
|
||||
|
||||
+------------------------------------------------------------------+-----------+---------------+
|
||||
| ID | NAME | ENDPOINT |
|
||||
+------------------------------------------------------------------+-----------+---------------+
|
||||
| 5b24d36608ee392c937a61a530805f74551ddec304aea3aca2ffa0fabcf98cf3 | /hello-fx | 0.0.0.0:58328 |
|
||||
| 5b24d36608ee392c937a61a530805f74551ddec304aea3aca2ffa0fabcf98cf3 | /hello | 0.0.0.0:58328 |
|
||||
+------------------------------------------------------------------+-----------+---------------+
|
||||
```
|
||||
|
||||
### Deploy your function to Kubernetes
|
||||
#### Remote host
|
||||
|
||||
Use `--host` to specify the target host for your function,
|
||||
|
||||
```shell
|
||||
$ fx up --host roo@<your host> --name hello ./examples/functions/JavaScript/func.js
|
||||
|
||||
+------------------------------------------------------------------+-----------+---------------+
|
||||
| ID | NAME | ENDPOINT |
|
||||
+------------------------------------------------------------------+-----------+---------------+
|
||||
| 5b24d36608ee392c937a61a530805f74551ddec304aea3aca2ffa0fabcf98cf3 | /hello | 0.0.0.0:58345 |
|
||||
+------------------------------------------------------------------+-----------+---------------+
|
||||
```
|
||||
|
||||
#### Kubernetes
|
||||
|
||||
```
|
||||
$ KUBECONFIG=~/.kube/config ./build/fx up examples/functions/JavaScript/func.js --name hello-fx
|
||||
$ FX_KUBECONF=~/.kube/config fx up examples/functions/JavaScript/func.js --name hello
|
||||
|
||||
+-------------------------------+------+----------------+
|
||||
| ID | NAME | ENDPOINT |
|
||||
@@ -122,7 +134,7 @@ $ KUBECONFIG=~/.kube/config ./build/fx up examples/functions/JavaScript/func.js
|
||||
+------------------------+-------------+----------------+
|
||||
```
|
||||
|
||||
### Test your service
|
||||
### Test service
|
||||
|
||||
then you can test your service:
|
||||
|
||||
@@ -149,31 +161,6 @@ hello world
|
||||
|
||||
```
|
||||
|
||||
## Manage Infrastructure
|
||||
|
||||
**fx** is originally designed to turn a function into a runnable Docker container in a easiest way, on a host with Docker running, you can just deploy your function with `fx up` command, and now **fx** supports deploy function to be a service onto Kubernetes cluster infrasture, and we encourage you to do that other than on bare Docker environment, there are lots of advantage to run your function on Kubernetes like self-healing, load balancing, easy horizontal scaling, etc. It's pretty simple to deploy your function onto Kubernetes with **fx**, you just set KUBECONFIG in your enviroment.
|
||||
|
||||
By default. **fx** use localhost as target infrastructure to run your service, and you can also setup your remote virtual machines as **fx**'s infrastructure and deploy your functions onto it.
|
||||
|
||||
### `fx infra create`
|
||||
|
||||
You can create types (docker and k8s) of infrastructures for **fx** to deploy functions
|
||||
|
||||
```shell
|
||||
$ fx infra create --name infra_us --type docker --host <user>@<ip> ## create docker type infrasture on <ip>
|
||||
$ fx infra create --name infra_bj --type k8s --master <user>@<ip> --agents '<user1>@<ip1>,<user2>@<ip2>' ## create k8s type infrasture use <ip> as master node, and <ip1> and <ip2> as agents nodes
|
||||
```
|
||||
|
||||
### `fx infra use`
|
||||
|
||||
To use a infrastructure, you can use `fx infra use` command to activate it.
|
||||
|
||||
```shell
|
||||
fx infra use <infrastructure name>
|
||||
```
|
||||
|
||||
and you can list your infrastructure with `fx infra list`
|
||||
|
||||
## Use Public Cloud Kubernetes Service as infrastructure to run your functions
|
||||
|
||||
* Azure Kubernetes Service (AKS)
|
||||
@@ -198,7 +185,7 @@ aks-nodepool1-31718369-0 Ready agent 6m44s v1.12.8
|
||||
Since AKS's config will be merged into `~/.kube/config` and set to be current context after you run `az aks get-credentials` command, so you can just set KUBECONFIG to default config also,
|
||||
|
||||
```shell
|
||||
$ export KUBECONFIG=~/.kube/config # then fx will take the config to deloy function
|
||||
$ export FX_KUBECONF=~/.kube/config # then fx will take the config to deloy function
|
||||
```
|
||||
|
||||
But we would suggest you run `kubectl config current-context` to check if the current context is what you want.
|
||||
@@ -224,7 +211,7 @@ $ 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
|
||||
$ FX_KUBECONF=~/.kube/config fx up examples/functions/JavaScript/func.js --name hellojs
|
||||
```
|
||||
|
||||
* Setup your own Kubernetes cluster
|
||||
|
||||
8
bundler/d/d-packr.go
Normal file
8
bundler/d/d-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package d
|
||||
|
||||
import _ "github.com/metrue/fx/bundler/d/packrd"
|
||||
@@ -13,7 +13,7 @@ type D struct {
|
||||
// New a koa bundler
|
||||
func New() *D {
|
||||
return &D{
|
||||
assets: packr.New("", "./assets"),
|
||||
assets: packr.New("d", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
35
bundler/d/packrd/packed-packr.go
Normal file
35
bundler/d/packrd/packed-packr.go
Normal file
File diff suppressed because one or more lines are too long
@@ -13,7 +13,7 @@ type Gin struct {
|
||||
// New a koa bundler
|
||||
func New() *Gin {
|
||||
return &Gin{
|
||||
assets: packr.New("", "./assets"),
|
||||
assets: packr.New("go", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
8
bundler/go/golang-packr.go
Normal file
8
bundler/go/golang-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package golang
|
||||
|
||||
import _ "github.com/metrue/fx/bundler/go/packrd"
|
||||
33
bundler/go/packrd/packed-packr.go
Normal file
33
bundler/go/packrd/packed-packr.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr2 clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package packrd
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/gobuffalo/packr/v2/file/resolver"
|
||||
)
|
||||
|
||||
var _ = func() error {
|
||||
const gk = "449af63614707e60b8b2ffe119b470cf"
|
||||
g := packr.New(gk, "")
|
||||
hgr, err := resolver.NewHexGzip(map[string]string{
|
||||
"76eca10e3a22b8409f5af0b284f82ee4": "1f8b08000000000000ff7ccabdaac2301407f0fd3cc59fec490af711da5e10a929115111877ee5586839a569318fefe6e8fefbf7aec23c6ceb3ed890348b6e9b3810e5aebec3c0b2d8b87696c7edb5b7a693d97e315d9d3f1607ff1b91bf9cc082761fa71e7aeac3d47084d26fe8a8a005212124c38266590c0b5179abddb9c45f9665447955e0a18c0d493de9130000ffff44ef0fc1ae000000",
|
||||
"cf8b44c6431c48afd768cf293ee38cab": "1f8b08000000000000ff4c8cc1aac2301045d799af98975502efa585b7ab74a7b854d41f88218983edb40c0d08d27f9766e5eede03e7cc3e3c7d8e387a62001ae7491634a074a6e551ee2e4c639389fff2c414b6a5c102a4c2a12ac6e21b9460d7632676fb987c19166341893b1e6e4637fa17d3abfef3e9fa0d2861946a8abb1436bafb6fdb56db5dc53f3d320d5b5ccd9e2998286241adb0c2270000ffff1428eddcb3000000",
|
||||
"da1f5928cfe751551db26cc0459d944e": "1f8b08000000000000ff14c9b10e82301000d0b9f715974e601089a3ab8b71d0c12fa8b5948bed1d81233621fcbb617bc91b9dffba18303b6200caa34c8a36920ecbbbf5924f91f81885c9efb200fdc21efb52792d7888c4ed555843d11a57305e4b7b7f3d1fd5b9eb1adcf7b682313687797631d80bda21a424f893297d6c0366ab61837f000000ffffe12418cf85000000",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.DefaultResolver = hgr
|
||||
|
||||
func() {
|
||||
b := packr.New("go", "./assets")
|
||||
b.SetResolver("Dockerfile", packr.Pointer{ForwardBox: gk, ForwardPath: "76eca10e3a22b8409f5af0b284f82ee4"})
|
||||
b.SetResolver("app.go", packr.Pointer{ForwardBox: gk, ForwardPath: "cf8b44c6431c48afd768cf293ee38cab"})
|
||||
b.SetResolver("fx.go", packr.Pointer{ForwardBox: gk, ForwardPath: "da1f5928cfe751551db26cc0459d944e"})
|
||||
}()
|
||||
return nil
|
||||
}()
|
||||
8
bundler/java/java-packr.go
Normal file
8
bundler/java/java-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package java
|
||||
|
||||
import _ "github.com/metrue/fx/bundler/java/packrd"
|
||||
@@ -13,7 +13,7 @@ type Java struct {
|
||||
// New a koa bundler
|
||||
func New() *Java {
|
||||
return &Java{
|
||||
assets: packr.New("", "./assets"),
|
||||
assets: packr.New("java", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
35
bundler/java/packrd/packed-packr.go
Normal file
35
bundler/java/packrd/packed-packr.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr2 clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package packrd
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/gobuffalo/packr/v2/file/resolver"
|
||||
)
|
||||
|
||||
var _ = func() error {
|
||||
const gk = "472df26e20796ab2f7966f2dbcce8ee2"
|
||||
g := packr.New(gk, "")
|
||||
hgr, err := resolver.NewHexGzip(map[string]string{
|
||||
"3ce2296391d96d76aaff2d829f1f612d": "1f8b08000000000000ff4c4fb16ec32014dcf98a274f586da9a58e563a66c8d00c19ab0e0f4c5d2801042f2955e57faf9c10c7b71cdc9dee20a2fac651c367e91933c718128109c2e2199df16277e5fe6685340a9b8317bbc3fe6d2fad56d433164fd21905ca61ce8031c21f0300a8722624a3e01ccc0047349e1f28193fbe7f00a631b7353ca3ae5d2a36b79bc88489f84bd7756dbf44b705bed00f4e27d880d73fb02d7c65638c22864cbc796e1e415181a7d7d5d0656cf90104696bcb5de48a8a9061f8e5edaa77c66c249d4f8e78d33cd457882bf320ed3a3fd5f3c426f61f0000ffff88067e0e6b010000",
|
||||
"74b2f70d9674f4e1a69013f26355fca9": "1f8b08000000000000ff8c55516fdb20107ecfafb0fc1eb0bb4aab2a4a1f264daad46ad3d64d7bab6ef8ec90da808024debf9f0c4e63c775dc3c85fb3eeebeef8033bb6f9b3ad9a37552abbb3427599aa012ba90aaba4b7f3d7f5ddfa4f77cc58cd55b143e699b5ab9bb74e3bdb9a5b4813d2a0206c40689b615fdfeed895e93accb1298b7ad936fecc3e1400e9f02ef2acb72fae7e9f1a7d860036ba99c0725305d254992b44edeba003c6a013e085b2c98cc314260bdbf7ec95e32d2ba22e5a1086b7481f5ef689c87148c8e62915659bd330f05d7b622ce58a9aad24283076d5f193d82910ad6cb12847f2878d9aec198f516f6c0e8201e8906c42b5452557c0b96d1d332c2fd69f08ce49da8e37215d1bf3b59f78962b27a5749e54e9141741c9cf8e9bb145bd6e7393335da3b3012bbea3650e03aee9cfa1ced3dbab822f9c0d284862d8a5d77e46e0a8e09efe3d1fc061cf2d85664342ee7e995867aa6dc88c3835d46c3fff97474211f135a95b2da59b86c2370bd05e54a6d1bb40b12cfe98be4ee271b536383caf7cf6cee5690609d58747a6705922750b244e77ff481e753dd74596690da80545f6a708e972d0163183d45968dd281d38516d28ff590d10f9c0ba317eedf003c7f8ef4fc3d1e233d91d1c1b3ee86ad41eb250ed2b0781e423746d66849ec3bbf61f47d6076a3075ba17f67630fac82b8910256a04155a01293c0bfb3312935e9865eddcd83d9e9f8c6984cc6e360c8497e3627181dd79c97d0dde1add317050478b6fa55967fceb3fce6b2003aee4a685af78de4abff010000ffff2998100355070000",
|
||||
"9d097ccc5e31491bd2ee70619fc36101": "1f8b08000000000000ff74cbc1aac2301005d07dbe62e82ae1417ea0bced83be855df40b26718ca97512922914a4ff2ed282a87897f79e9bd15f30109c9656a978cda908a412ec5813dbffa13ff46e242fad52797653f4e027ac15fe16b8290080bd8d2c70463e4ea49f27889c6731bb7c6490123900c2efb6d940a21b6c8c95b46ddab4efdabd6af7451792b930742c14a8d88ca552c7a2d1c0cf67ebf6e7aa56750f0000ffffeb12798604010000",
|
||||
"bde0ba82beaa4757c20608eb0bcc46bd": "1f8b08000000000000ff2ccecf4a033110c7f17b9e6288d7661351c46b71ebad562a82507a984da64bd2e60f93ece2e3cbae3dcde1cbfcf8bc1f0f7b88d478227df955016754035612e201b6cef93442cd135bda80cdb1f81b01260705ed1547029f5a06840b3608c862dbf750d982b6d991ae6cc5f1fb034e32ce496e40debfe45988ddcfe7e16b074fc618f1b6efe124f55459dffca0c31cf5ea7855b9500aeeaa30ba97673df8b486654a05e4e536e491da42c752fef9a67bec4cb7f4b3f80b0000ffff3eecd9bedf000000",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.DefaultResolver = hgr
|
||||
|
||||
func() {
|
||||
b := packr.New("java", "./assets")
|
||||
b.SetResolver("Dockerfile", packr.Pointer{ForwardBox: gk, ForwardPath: "bde0ba82beaa4757c20608eb0bcc46bd"})
|
||||
b.SetResolver("pom.xml", packr.Pointer{ForwardBox: gk, ForwardPath: "74b2f70d9674f4e1a69013f26355fca9"})
|
||||
b.SetResolver("src/main/java/fx/Fx.java", packr.Pointer{ForwardBox: gk, ForwardPath: "9d097ccc5e31491bd2ee70619fc36101"})
|
||||
b.SetResolver("src/main/java/fx/app.java", packr.Pointer{ForwardBox: gk, ForwardPath: "3ce2296391d96d76aaff2d829f1f612d"})
|
||||
}()
|
||||
return nil
|
||||
}()
|
||||
8
bundler/julia/julia-packr.go
Normal file
8
bundler/julia/julia-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package julia
|
||||
|
||||
import _ "github.com/metrue/fx/bundler/julia/packrd"
|
||||
@@ -13,7 +13,7 @@ type Julia struct {
|
||||
// New a koa bundler
|
||||
func New() *Julia {
|
||||
return &Julia{
|
||||
assets: packr.New("", "./assets"),
|
||||
assets: packr.New("julia", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
37
bundler/julia/packrd/packed-packr.go
Normal file
37
bundler/julia/packrd/packed-packr.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr2 clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package packrd
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/gobuffalo/packr/v2/file/resolver"
|
||||
)
|
||||
|
||||
var _ = func() error {
|
||||
const gk = "9e78c349d756cf23066df37690663267"
|
||||
g := packr.New(gk, "")
|
||||
hgr, err := resolver.NewHexGzip(map[string]string{
|
||||
"17bc9961cffa014e8d7ac818d8a3ec14": "1f8b08000000000000ff4c8fc16ac3300c86ef7e8a9f9e6c2826b09d0a39edb2edb08d959dc61866515a975449243bf4f18793a69bc020ebe3d36f678d7cc0634ac39e642231f13cf492f0bc7f7d59fb0f3e07d163e88c89fcd3e586eca6bdf853b771c6a84ca8b1c8d6a1e92134ee76ef3466d2b48590969b0e3d2b1900882d3ae2433a5aa1d137210587ba4635c35242ea3585941535eeab055077d54b0d41949aefe2a29edfeadfca48fc4ceca98f6c3f1f8e41ece4d0f6820991b1e67d39775b75dd71fba3cf6b679f78c869fb3fec4fd324910fb6bdd879be00e2c6946324b35599b6b8abaaca99df000000ffffa9ee463365010000",
|
||||
"516dab4c6ff105b6210ce90d2afa826f": "1f8b08000000000000ff2a2e292a4d2e51f0cc2b282de15250505048b4b2f22bcd4d4a2d02f392e0bcd4bc142eaeb40a055b85b4d2bce492ccfc3c8d4c90262b2bb05e4db0f2a2d492d2a23c05b0845ea28236949504d60d080000ffff662990f46b000000",
|
||||
"b4a52bf8c0e242a9573eebd6bd5881fd": "1f8b08000000000000ff4ccd31cbc2301080e1fd7ec54dddd206bee103d75ab7da5211743c9b5053cf723417417fbd98415c9fe17d7743d7e29c38d0c696ff0075d79fb1c48a440086e31e49d44c5e318923f558145f094b546246f3c4691c33270d1c312daf2078a79b470e97abaa18a135fad538ffc8d13ccc93ca7989e5cc0075bbfd7512f97073eabb43837fd65a78070000ffff383c868aac000000",
|
||||
"c85770f1f90561b2df722b26d344976a": "1f8b08000000000000ff14c9310ac3300c46e1593a85f0642fcd093a06da2d0df4002ebf5c4c832de4f4fec1c35bded74d5b0c4b365bf6f5f57eee6b48822e85096a43eee29a71d4a62396c454ba0ba43699ca44dbef7bcb404462d2069ecdf7f9d703313cced3b6ec433d24be020000ffff9afc26056c000000",
|
||||
"ccfa88310f9e4f3419a50e352a228292": "1f8b08000000000000fff228292908482c2a4e2de2023183538bca528bb8bc82fdfdb842f372138b8a33127300010000ffffd7a8f0a324000000",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.DefaultResolver = hgr
|
||||
|
||||
func() {
|
||||
b := packr.New("julia", "./assets")
|
||||
b.SetResolver("Dockerfile", packr.Pointer{ForwardBox: gk, ForwardPath: "b4a52bf8c0e242a9573eebd6bd5881fd"})
|
||||
b.SetResolver("REQUIRE", packr.Pointer{ForwardBox: gk, ForwardPath: "ccfa88310f9e4f3419a50e352a228292"})
|
||||
b.SetResolver("app.jl", packr.Pointer{ForwardBox: gk, ForwardPath: "17bc9961cffa014e8d7ac818d8a3ec14"})
|
||||
b.SetResolver("deps.jl", packr.Pointer{ForwardBox: gk, ForwardPath: "c85770f1f90561b2df722b26d344976a"})
|
||||
b.SetResolver("fx.jl", packr.Pointer{ForwardBox: gk, ForwardPath: "516dab4c6ff105b6210ce90d2afa826f"})
|
||||
}()
|
||||
return nil
|
||||
}()
|
||||
8
bundler/node/node-packr.go
Normal file
8
bundler/node/node-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package node
|
||||
|
||||
import _ "github.com/metrue/fx/bundler/node/packrd"
|
||||
@@ -15,7 +15,7 @@ type Node struct {
|
||||
// New a koa bundler
|
||||
func New() *Node {
|
||||
return &Node{
|
||||
assets: packr.New("", "./assets"),
|
||||
assets: packr.New("node", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
33
bundler/node/packrd/packed-packr.go
Normal file
33
bundler/node/packrd/packed-packr.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr2 clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package packrd
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/gobuffalo/packr/v2/file/resolver"
|
||||
)
|
||||
|
||||
var _ = func() error {
|
||||
const gk = "099a799bb033d71a85f319790ea9425b"
|
||||
g := packr.New(gk, "")
|
||||
hgr, err := resolver.NewHexGzip(map[string]string{
|
||||
"06d0f22f31cc0848f733e8dcaa11962c": "1f8b08000000000000ffcacd4f29cd49d54bad28c82f2a2956b055d0482ea9d054b0b553a8e65250482ea9d04bca4fa954b05550cf48cdc9c95728cf2fca4951e7aae502040000ffff4a686e4639000000",
|
||||
"2c75a41a4116ea4ba707c129527fd11c": "1f8b08000000000000ff5c8ec10ac2301044eff98abd25116d0bde2c05ef5efc8558b7122ad9b869b122febbac0a091e67de63989e429ae0400e3a60bccd9ed1e8919cb6adfab2139d1f47c709f94fd908891f92ed9e3895de7e24574b9995612985aa1e16613fe862840e02dee593b1ad7231567342231be6a90088fdc5871de8955eab972d94fcd494f5b0c8bca4ab4f1306b36d9ac6b6ea1d0000ffffc9e8e169fb000000",
|
||||
"98bb98a34bd158dd2fdd41ca90f4e64d": "1f8b08000000000000ff720bf2f755c84d2d292a4dd54fabd0cdcb4f49d54d4a2c4ee5e272f60f8854d053d0e3728d08f00f765530363030e072f67551885602a952d251504a2c28d0cb2a568ae502040000ffff6c95391846000000",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.DefaultResolver = hgr
|
||||
|
||||
func() {
|
||||
b := packr.New("node", "./assets")
|
||||
b.SetResolver("Dockerfile", packr.Pointer{ForwardBox: gk, ForwardPath: "98bb98a34bd158dd2fdd41ca90f4e64d"})
|
||||
b.SetResolver("app.js", packr.Pointer{ForwardBox: gk, ForwardPath: "2c75a41a4116ea4ba707c129527fd11c"})
|
||||
b.SetResolver("fx.js", packr.Pointer{ForwardBox: gk, ForwardPath: "06d0f22f31cc0848f733e8dcaa11962c"})
|
||||
}()
|
||||
return nil
|
||||
}()
|
||||
33
bundler/perl/packrd/packed-packr.go
Normal file
33
bundler/perl/packrd/packed-packr.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr2 clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package packrd
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/gobuffalo/packr/v2/file/resolver"
|
||||
)
|
||||
|
||||
var _ = func() error {
|
||||
const gk = "77d2d2753a1e41552ea722fb94d9bca6"
|
||||
g := packr.New(gk, "")
|
||||
hgr, err := resolver.NewHexGzip(map[string]string{
|
||||
"2e6c11f0e86d189a5f33d2eda0bc7406": "1f8b08000000000000ff720bf2f755c84d2d292a4dd54fabd02d482dcad14d4a2c4ee5e272747151d053d0e3e2728d08f00f765530363030e072f67551885602a952d251504a2c28d02b00b352125373f3f39462b900010000ffff0583b6ed50000000",
|
||||
"b3a2e75d2833aa5c271d28326dee512c": "1f8b08000000000000ff2a2e4d5248ab50a8e65250c8ad5450492ea950b05528cec84c2bb1e65250284a2d292dca5350cf48cdc9c95748ab50e7aae5e232b4e602040000ffffd1aa6a1336000000",
|
||||
"cd2f2d8e12b35147ad2b9cb35d65d3ab": "1f8b08000000000000ffb48c41aa83401044f77d8a423ea80b75efa027f83984316d32629c49770f1842ee1e067285ec8a578f9794710a6bd8fcec43d2befff7c68e48f891bc308ab65b8e366e8523bab2a1ec4a0c23349df122e0fec4df6c0706e8cd2fe6be4858316039aa7cd699e6d18cc2fb85a55a35ecb992bddad1db11c5a0bf8b4f3136a3da24e6e8130000ffff6c5481b5f1000000",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.DefaultResolver = hgr
|
||||
|
||||
func() {
|
||||
b := packr.New("perl", "./assets")
|
||||
b.SetResolver("Dockerfile", packr.Pointer{ForwardBox: gk, ForwardPath: "2e6c11f0e86d189a5f33d2eda0bc7406"})
|
||||
b.SetResolver("app.pl", packr.Pointer{ForwardBox: gk, ForwardPath: "cd2f2d8e12b35147ad2b9cb35d65d3ab"})
|
||||
b.SetResolver("fx.pl", packr.Pointer{ForwardBox: gk, ForwardPath: "b3a2e75d2833aa5c271d28326dee512c"})
|
||||
}()
|
||||
return nil
|
||||
}()
|
||||
8
bundler/perl/perl-packr.go
Normal file
8
bundler/perl/perl-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package perl
|
||||
|
||||
import _ "github.com/metrue/fx/bundler/perl/packrd"
|
||||
@@ -13,7 +13,7 @@ type Julia struct {
|
||||
// New a koa bundler
|
||||
func New() *Julia {
|
||||
return &Julia{
|
||||
assets: packr.New("", "./assets"),
|
||||
assets: packr.New("perl", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
33
bundler/python/packrd/packed-packr.go
Normal file
33
bundler/python/packrd/packed-packr.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr2 clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package packrd
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/gobuffalo/packr/v2/file/resolver"
|
||||
)
|
||||
|
||||
var _ = func() error {
|
||||
const gk = "37124a38bea92e4ec995016569c93ec9"
|
||||
g := packr.New(gk, "")
|
||||
hgr, err := resolver.NewHexGzip(map[string]string{
|
||||
"0541aa67f1217ee20e491552a545a7ca": "1f8b08000000000000ff4a494d5348abd0284a2d2c4d2d2ed1b4e252505050284a2d292dca5350ca48cdc9c95728cf2fca4951e202040000ffffc6a7b4282a000000",
|
||||
"46416dc28552c117e810b36196e043ca": "1f8b08000000000000ff720bf2f755c84d2d292a4dd54fabd02da82cc9c8cfd34d4a2c4ee5e272f60f8854d053d0e3728d08f00f765530363030e072f6755148cb492cce56282acd53d0cd5030d0034305dd02880240000000ffff21aa3b6b52000000",
|
||||
"65d09a53a70631fa1bbd7c5e1906d910": "1f8b08000000000000ff34cdbd0ac23014c5f1fd3ec5d99240d05d28b8a8a382dd4442a037b4da7c98a410df5ea43a9edf19fe2e470fd730f91473856bb4ca6ccbf38fc7efd0c8fc5ab8548d478961726fb229a15b5f694cb09e8d51447b9bd226c7a5b2145ba1e1b98e7128dd4d5cced75e6888d3a1177745033b8c360c334bb52300c85c971ce09afcc5147d020000ffff9da83eeaa1000000",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.DefaultResolver = hgr
|
||||
|
||||
func() {
|
||||
b := packr.New("python", "./assets")
|
||||
b.SetResolver("Dockerfile", packr.Pointer{ForwardBox: gk, ForwardPath: "46416dc28552c117e810b36196e043ca"})
|
||||
b.SetResolver("app.py", packr.Pointer{ForwardBox: gk, ForwardPath: "65d09a53a70631fa1bbd7c5e1906d910"})
|
||||
b.SetResolver("fx.py", packr.Pointer{ForwardBox: gk, ForwardPath: "0541aa67f1217ee20e491552a545a7ca"})
|
||||
}()
|
||||
return nil
|
||||
}()
|
||||
8
bundler/python/python-packr.go
Normal file
8
bundler/python/python-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package python
|
||||
|
||||
import _ "github.com/metrue/fx/bundler/python/packrd"
|
||||
@@ -13,7 +13,7 @@ type Julia struct {
|
||||
// New a koa bundler
|
||||
func New() *Julia {
|
||||
return &Julia{
|
||||
assets: packr.New("", "./assets"),
|
||||
assets: packr.New("python", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
33
bundler/ruby/packrd/packed-packr.go
Normal file
33
bundler/ruby/packrd/packed-packr.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr2 clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package packrd
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/gobuffalo/packr/v2/file/resolver"
|
||||
)
|
||||
|
||||
var _ = func() error {
|
||||
const gk = "5957c0f80ccc65e6ad8f2c329312596e"
|
||||
g := packr.New(gk, "")
|
||||
hgr, err := resolver.NewHexGzip(map[string]string{
|
||||
"5d0faa4e62eb53572bfc7410d0f064f9": "1f8b08000000000000ff720bf2f755c84d2d292a4dd54fabd02d2a4daad44d4a2c4ee5e272f60f8854d053d0e3728d08f00f765530363030e072f6755100a951482c28d02b4a52d02d008b2be8e62b18e8812117200000ffff79eb4a3952000000",
|
||||
"93130b893788876c223c2a0f3de05793": "1f8b08000000000000ffccce41aac23010c6f1fd9ce2dbcda6bc577057a8579168a75a91a4ce4ca520de5ddad4780537e19ffc207c2af7695001db10836b602a2f574b91e9733fa8dc820f0f01f7f39f1e99c8c4d18c49bdc2aeae6ba2319983ff195d220038f98c16cfb58166f949ccd1eeb165f5251b5334c996bba079f0c916ca55e022a1135d65cb4caff5ece7650049ec88cef29bc3de010000ffff22ee4db77f010000",
|
||||
"973570cec900912c2cdd5c5531be70ac": "1f8b08000000000000ff3ccd410ac2301085e1fd9ce2d16c14c40308f122e2a276a674111acd4c3022de5d6262773fef8319470e931578bcc9013825796451833fa3e7e10f7a8fab4a93d69dd446cb5aa1559f171959d26fef59e1432c33e6b29bacec09f5fb653b7e3dde22bfe0312c1242c433a6c003c9caf40d0000ffff99b9e753aa000000",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.DefaultResolver = hgr
|
||||
|
||||
func() {
|
||||
b := packr.New("ruby", "./assets")
|
||||
b.SetResolver("Dockerfile", packr.Pointer{ForwardBox: gk, ForwardPath: "5d0faa4e62eb53572bfc7410d0f064f9"})
|
||||
b.SetResolver("app.rb", packr.Pointer{ForwardBox: gk, ForwardPath: "93130b893788876c223c2a0f3de05793"})
|
||||
b.SetResolver("fx.rb", packr.Pointer{ForwardBox: gk, ForwardPath: "973570cec900912c2cdd5c5531be70ac"})
|
||||
}()
|
||||
return nil
|
||||
}()
|
||||
8
bundler/ruby/ruby-packr.go
Normal file
8
bundler/ruby/ruby-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package ruby
|
||||
|
||||
import _ "github.com/metrue/fx/bundler/ruby/packrd"
|
||||
@@ -13,7 +13,7 @@ type Julia struct {
|
||||
// New a koa bundler
|
||||
func New() *Julia {
|
||||
return &Julia{
|
||||
assets: packr.New("", "./assets"),
|
||||
assets: packr.New("ruby", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
41
bundler/rust/packrd/packed-packr.go
Normal file
41
bundler/rust/packrd/packed-packr.go
Normal file
File diff suppressed because one or more lines are too long
8
bundler/rust/rust-packr.go
Normal file
8
bundler/rust/rust-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package rust
|
||||
|
||||
import _ "github.com/metrue/fx/bundler/rust/packrd"
|
||||
@@ -13,7 +13,7 @@ type Julia struct {
|
||||
// New a koa bundler
|
||||
func New() *Julia {
|
||||
return &Julia{
|
||||
assets: packr.New("", "./assets"),
|
||||
assets: packr.New("rust", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
207
config/config.go
207
config/config.go
@@ -1,207 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
dockerInfra "github.com/metrue/fx/infra/docker"
|
||||
"github.com/metrue/fx/types"
|
||||
"github.com/metrue/fx/utils"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// Configer manage fx config
|
||||
type Configer interface {
|
||||
GetCurrentCloud() ([]byte, error)
|
||||
GetCurrentCloudType() (string, error)
|
||||
GetKubeConfig() (string, error)
|
||||
UseCloud(name string) error
|
||||
View() ([]byte, error)
|
||||
AddCloud(name string, meta []byte) error
|
||||
Dir() (string, error)
|
||||
}
|
||||
|
||||
// Config config of fx
|
||||
type Config struct {
|
||||
configFile string
|
||||
container *Container
|
||||
}
|
||||
|
||||
const defaultFxConfig = "~/.fx/config.yml"
|
||||
|
||||
// LoadDefault load default config
|
||||
func LoadDefault() (*Config, error) {
|
||||
configFile, err := homedir.Expand(defaultFxConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if os.Getenv("FX_CONFIG") != "" {
|
||||
configFile = os.Getenv("FX_CONFIG")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(configFile); os.IsNotExist(err) {
|
||||
if err := utils.EnsureFile(configFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return load(configFile)
|
||||
}
|
||||
|
||||
func load(configFile string) (*Config, error) {
|
||||
container, err := CreateContainer(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config := &Config{
|
||||
configFile: configFile,
|
||||
container: container,
|
||||
}
|
||||
|
||||
if container.get("clouds") == nil {
|
||||
if err := config.writeDefaultConfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// Load config
|
||||
func Load(configFile string) (*Config, error) {
|
||||
if configFile == "" {
|
||||
return nil, fmt.Errorf("invalid config file")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(configFile); os.IsNotExist(err) {
|
||||
if err := utils.EnsureFile(configFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return load(configFile)
|
||||
}
|
||||
|
||||
// AddCloud add k8s cloud
|
||||
func (c *Config) AddCloud(name string, meta []byte) error {
|
||||
var cloudMeta map[string]interface{}
|
||||
if err := json.Unmarshal(meta, &cloudMeta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cloudType, ok := cloudMeta["type"].(string)
|
||||
if !ok || cloudType == "" {
|
||||
return fmt.Errorf("unknown cloud type")
|
||||
}
|
||||
|
||||
if cloudType == types.CloudTypeK8S {
|
||||
dir := path.Dir(c.configFile)
|
||||
kubecfg := path.Join(dir, name+".kubeconfig")
|
||||
if err := utils.EnsureFile(kubecfg); err != nil {
|
||||
return err
|
||||
}
|
||||
config, ok := cloudMeta["config"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid k8s config")
|
||||
}
|
||||
if err := ioutil.WriteFile(kubecfg, []byte(config), 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.container.set("clouds."+name, cloudMeta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UseCloud set cloud instance with name as current context
|
||||
func (c *Config) UseCloud(name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("could not use empty name")
|
||||
}
|
||||
|
||||
if c.container.get("clouds."+name) == nil {
|
||||
return fmt.Errorf("no such cloud with name: %s", name)
|
||||
}
|
||||
return c.container.set("current_cloud", name)
|
||||
}
|
||||
|
||||
// View view current config
|
||||
func (c *Config) View() ([]byte, error) {
|
||||
return ioutil.ReadFile(c.configFile)
|
||||
}
|
||||
|
||||
// GetCurrentCloud get current using cloud's meta
|
||||
func (c *Config) GetCurrentCloud() ([]byte, error) {
|
||||
name, ok := c.container.get("current_cloud").(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no active cloud")
|
||||
}
|
||||
meta := c.container.get("clouds." + name)
|
||||
if meta == nil {
|
||||
return nil, fmt.Errorf("invalid config")
|
||||
}
|
||||
return json.Marshal(meta)
|
||||
}
|
||||
|
||||
// GetCurrentCloudType get current cloud type
|
||||
func (c *Config) GetCurrentCloudType() (string, error) {
|
||||
name, ok := c.container.get("current_cloud").(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("no active cloud")
|
||||
}
|
||||
return c.container.get("clouds." + name + ".type").(string), nil
|
||||
}
|
||||
|
||||
// GetKubeConfig get kubeconfig
|
||||
func (c *Config) GetKubeConfig() (string, error) {
|
||||
name, ok := c.container.get("current_cloud").(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("no active cloud")
|
||||
}
|
||||
dir := path.Dir(c.configFile)
|
||||
kubecfg := path.Join(dir, name+".kubeconfig")
|
||||
return kubecfg, nil
|
||||
}
|
||||
|
||||
func (c *Config) writeDefaultConfig() error {
|
||||
me, err := user.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defaultCloud := &dockerInfra.Cloud{
|
||||
IP: "127.0.0.1",
|
||||
User: me.Username,
|
||||
Name: "default",
|
||||
Type: types.CloudTypeDocker,
|
||||
}
|
||||
meta, err := defaultCloud.Dump()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.container.set("clouds", map[string]interface{}{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.AddCloud("default", meta); err != nil {
|
||||
return err
|
||||
}
|
||||
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{}
|
||||
)
|
||||
@@ -1,131 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
k8sInfra "github.com/metrue/fx/infra/k8s"
|
||||
"github.com/metrue/fx/types"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
configPath := "./tmp/config.yml"
|
||||
defer func() {
|
||||
if err := os.RemoveAll("./tmp/config.yml"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// default cloud
|
||||
c, err := Load(configPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defaultMeta, err := c.GetCurrentCloud()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var cloudMeta map[string]string
|
||||
if err := json.Unmarshal(defaultMeta, &cloudMeta); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cloudMeta["ip"] != "127.0.0.1" {
|
||||
t.Fatalf("should get %s but got %s", "127.0.0.1", cloudMeta["ip"])
|
||||
}
|
||||
|
||||
me, _ := user.Current()
|
||||
if cloudMeta["user"] != me.Username {
|
||||
t.Fatalf("should get %s but got %s", me.Username, cloudMeta["user"])
|
||||
}
|
||||
if cloudMeta["type"] != types.CloudTypeDocker {
|
||||
t.Fatalf("should get %s but got %s", types.CloudTypeDocker, cloudMeta["type"])
|
||||
}
|
||||
if cloudMeta["name"] != "default" {
|
||||
t.Fatalf("should get %s but got %s", "default", cloudMeta["name"])
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
if err := c.AddCloud(kName, kMeta); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
curMeta, err := c.GetCurrentCloud()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(curMeta, defaultMeta) {
|
||||
t.Fatalf("should get %v but got %v", defaultMeta, curMeta)
|
||||
}
|
||||
|
||||
if err := c.UseCloud("cloud-not-existed"); err == nil {
|
||||
t.Fatalf("should get error when there is not given cloud name")
|
||||
}
|
||||
|
||||
if err := c.UseCloud(kName); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
curMeta, err = c.GetCurrentCloud()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if reflect.DeepEqual(curMeta, kMeta) {
|
||||
t.Fatalf("should get %v but got %v", kMeta, curMeta)
|
||||
}
|
||||
|
||||
body, err := c.View()
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/metrue/fx/utils"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Container config container, wrap viper as a key-value store with lock
|
||||
type Container struct {
|
||||
mux sync.Mutex
|
||||
store string
|
||||
}
|
||||
|
||||
// CreateContainer new a container
|
||||
func CreateContainer(storeFile string) (*Container, error) {
|
||||
if err := utils.EnsureFile(storeFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dir := filepath.Dir(storeFile)
|
||||
ext := filepath.Ext(storeFile)
|
||||
name := filepath.Base(storeFile)
|
||||
viper.AddConfigPath(dir)
|
||||
viper.SetConfigName(strings.Replace(name, ext, "", 1))
|
||||
viper.SetConfigType(strings.Replace(ext, ".", "", 1))
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Container{
|
||||
store: storeFile,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func (c *Container) set(key string, value interface{}) error {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
if key == "" {
|
||||
return fmt.Errorf("empty key not allowed")
|
||||
}
|
||||
|
||||
keys := strings.Split(key, ".")
|
||||
if len(keys) == 1 {
|
||||
viper.Set(key, value)
|
||||
} else {
|
||||
prePath := keys[0]
|
||||
for i := 1; i < len(keys)-2; i++ {
|
||||
prePath += "." + keys[i]
|
||||
}
|
||||
if viper.Get(prePath) == nil {
|
||||
return fmt.Errorf("%s not existed", prePath)
|
||||
}
|
||||
viper.Set(key, value)
|
||||
}
|
||||
// viper.Set(key, value)
|
||||
if err := viper.WriteConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Container) get(key string) interface{} {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
return viper.Get(key)
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestContainer(t *testing.T) {
|
||||
configPath := "./tmp/container.yml"
|
||||
defer func() {
|
||||
if err := os.RemoveAll("./tmp/container.yml"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
c, err := CreateContainer(configPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := c.set("", ""); err == nil {
|
||||
t.Fatalf("should get error when key is empty")
|
||||
}
|
||||
|
||||
if c.get("1") != nil {
|
||||
t.Fatalf("should get %v but got %v", nil, c.get("key"))
|
||||
}
|
||||
|
||||
// create
|
||||
if err := c.set("1", "1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// read
|
||||
if c.get("1").(string) != "1" {
|
||||
t.Fatalf("should get %s but got %s", "val-1", c.get("key"))
|
||||
}
|
||||
|
||||
// invaliad set
|
||||
if err := c.set("1.1", "1.1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c.get("1.1").(string) != "1.1" {
|
||||
t.Fatalf("should get 1.1 but got %s", c.get("1.1"))
|
||||
}
|
||||
|
||||
// update
|
||||
if err := c.set("1", "11"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c.get("1").(string) != "11" {
|
||||
t.Fatalf("should get 11 but got %s", c.get("1").(string))
|
||||
}
|
||||
|
||||
// nested set
|
||||
if err := c.set("2.2.2.2", "2222"); err == nil {
|
||||
t.Fatalf("should throw error since 2.2.2 not ready yet")
|
||||
}
|
||||
|
||||
if err := c.set("2", map[string]interface{}{
|
||||
"2": map[string]interface{}{
|
||||
"2": "2",
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c.get("2.2.2").(string) != "2" {
|
||||
t.Fatalf("should get 2 but got %s", c.get("2.2.2"))
|
||||
}
|
||||
if err := c.set("2.2.2.2", "2222"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c.get("2.2.2.2").(string) != "2222" {
|
||||
t.Fatalf("should get 2222 but got %s", c.get("2.2.2.2"))
|
||||
}
|
||||
|
||||
if err := c.set("2.2.2.1", "1111"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c.get("2.2.2.1").(string) != "1111" {
|
||||
t.Fatalf("should get 1111 but got %s", c.get("2.2.2.1"))
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/apex/log"
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
dockerTypesContainer "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/google/go-querystring/query"
|
||||
@@ -32,10 +31,32 @@ import (
|
||||
|
||||
// API interact with dockerd http api
|
||||
type API struct {
|
||||
host string
|
||||
port string
|
||||
endpoint string
|
||||
version string
|
||||
}
|
||||
|
||||
// New a API
|
||||
func New(host string, port string) *API {
|
||||
return &API{
|
||||
host: host,
|
||||
port: port,
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize an API
|
||||
func (api *API) Initialize() error {
|
||||
addr := api.host + ":" + api.port
|
||||
v, err := version(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoint := fmt.Sprintf("http://%s:%s/v%s", api.host, api.port, v)
|
||||
api.endpoint = endpoint
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a API
|
||||
func Create(host string, port string) (*API, error) {
|
||||
addr := host + ":" + port
|
||||
@@ -134,7 +155,8 @@ func (api *API) post(path string, body []byte, expectStatus int, v interface{})
|
||||
|
||||
// Version get version of docker engine
|
||||
func (api *API) Version(ctx context.Context) (string, error) {
|
||||
return version(api.endpoint)
|
||||
addr := api.host + ":" + api.port
|
||||
return version(addr)
|
||||
}
|
||||
|
||||
func version(endpoint string) (string, error) {
|
||||
@@ -397,12 +419,12 @@ func (api *API) StartContainer(ctx context.Context, name string, image string, b
|
||||
portSet[port] = struct{}{}
|
||||
portMap[port] = bindings
|
||||
}
|
||||
config := &dockerTypesContainer.Config{
|
||||
config := &container.Config{
|
||||
Image: image,
|
||||
ExposedPorts: portSet,
|
||||
}
|
||||
|
||||
hostConfig := &dockerTypesContainer.HostConfig{
|
||||
hostConfig := &container.HostConfig{
|
||||
AutoRemove: !fxConfig.DisableContainerAutoremove,
|
||||
PortBindings: portMap,
|
||||
}
|
||||
|
||||
164
container_runtimes/mocks/runtime.go
Normal file
164
container_runtimes/mocks/runtime.go
Normal file
@@ -0,0 +1,164 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: runtimes.go
|
||||
|
||||
// Package mock_containerruntimes is a generated GoMock package.
|
||||
package mock_containerruntimes
|
||||
|
||||
import (
|
||||
context "context"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
types "github.com/metrue/fx/types"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockContainerRuntime is a mock of ContainerRuntime interface
|
||||
type MockContainerRuntime struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockContainerRuntimeMockRecorder
|
||||
}
|
||||
|
||||
// MockContainerRuntimeMockRecorder is the mock recorder for MockContainerRuntime
|
||||
type MockContainerRuntimeMockRecorder struct {
|
||||
mock *MockContainerRuntime
|
||||
}
|
||||
|
||||
// NewMockContainerRuntime creates a new mock instance
|
||||
func NewMockContainerRuntime(ctrl *gomock.Controller) *MockContainerRuntime {
|
||||
mock := &MockContainerRuntime{ctrl: ctrl}
|
||||
mock.recorder = &MockContainerRuntimeMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockContainerRuntime) EXPECT() *MockContainerRuntimeMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// BuildImage mocks base method
|
||||
func (m *MockContainerRuntime) BuildImage(ctx context.Context, workdir, name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "BuildImage", ctx, workdir, name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// BuildImage indicates an expected call of BuildImage
|
||||
func (mr *MockContainerRuntimeMockRecorder) BuildImage(ctx, workdir, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildImage", reflect.TypeOf((*MockContainerRuntime)(nil).BuildImage), ctx, workdir, name)
|
||||
}
|
||||
|
||||
// PushImage mocks base method
|
||||
func (m *MockContainerRuntime) PushImage(ctx context.Context, name string) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "PushImage", ctx, name)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// PushImage indicates an expected call of PushImage
|
||||
func (mr *MockContainerRuntimeMockRecorder) PushImage(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PushImage", reflect.TypeOf((*MockContainerRuntime)(nil).PushImage), ctx, name)
|
||||
}
|
||||
|
||||
// InspectImage mocks base method
|
||||
func (m *MockContainerRuntime) InspectImage(ctx context.Context, name string, img interface{}) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InspectImage", ctx, name, img)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// InspectImage indicates an expected call of InspectImage
|
||||
func (mr *MockContainerRuntimeMockRecorder) InspectImage(ctx, name, img interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InspectImage", reflect.TypeOf((*MockContainerRuntime)(nil).InspectImage), ctx, name, img)
|
||||
}
|
||||
|
||||
// TagImage mocks base method
|
||||
func (m *MockContainerRuntime) TagImage(ctx context.Context, name, tag string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "TagImage", ctx, name, tag)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// TagImage indicates an expected call of TagImage
|
||||
func (mr *MockContainerRuntimeMockRecorder) TagImage(ctx, name, tag interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TagImage", reflect.TypeOf((*MockContainerRuntime)(nil).TagImage), ctx, name, tag)
|
||||
}
|
||||
|
||||
// StartContainer mocks base method
|
||||
func (m *MockContainerRuntime) StartContainer(ctx context.Context, name, image string, bindings []types.PortBinding) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "StartContainer", ctx, name, image, bindings)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// StartContainer indicates an expected call of StartContainer
|
||||
func (mr *MockContainerRuntimeMockRecorder) StartContainer(ctx, name, image, bindings interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartContainer", reflect.TypeOf((*MockContainerRuntime)(nil).StartContainer), ctx, name, image, bindings)
|
||||
}
|
||||
|
||||
// StopContainer mocks base method
|
||||
func (m *MockContainerRuntime) StopContainer(ctx context.Context, name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "StopContainer", ctx, name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// StopContainer indicates an expected call of StopContainer
|
||||
func (mr *MockContainerRuntimeMockRecorder) StopContainer(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopContainer", reflect.TypeOf((*MockContainerRuntime)(nil).StopContainer), ctx, name)
|
||||
}
|
||||
|
||||
// InspectContainer mocks base method
|
||||
func (m *MockContainerRuntime) InspectContainer(ctx context.Context, name string, container interface{}) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InspectContainer", ctx, name, container)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// InspectContainer indicates an expected call of InspectContainer
|
||||
func (mr *MockContainerRuntimeMockRecorder) InspectContainer(ctx, name, container interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InspectContainer", reflect.TypeOf((*MockContainerRuntime)(nil).InspectContainer), ctx, name, container)
|
||||
}
|
||||
|
||||
// ListContainer mocks base method
|
||||
func (m *MockContainerRuntime) ListContainer(ctx context.Context, filter string) ([]types.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListContainer", ctx, filter)
|
||||
ret0, _ := ret[0].([]types.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ListContainer indicates an expected call of ListContainer
|
||||
func (mr *MockContainerRuntimeMockRecorder) ListContainer(ctx, filter interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListContainer", reflect.TypeOf((*MockContainerRuntime)(nil).ListContainer), ctx, filter)
|
||||
}
|
||||
|
||||
// Version mocks base method
|
||||
func (m *MockContainerRuntime) Version(ctx context.Context) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Version", ctx)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Version indicates an expected call of Version
|
||||
func (mr *MockContainerRuntimeMockRecorder) Version(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Version", reflect.TypeOf((*MockContainerRuntime)(nil).Version), ctx)
|
||||
}
|
||||
@@ -52,6 +52,7 @@ func (ctx *Context) GetCliContext() *cli.Context {
|
||||
|
||||
// Set a value with name
|
||||
func (ctx *Context) Set(name string, value interface{}) {
|
||||
// nolint
|
||||
newCtx := context.WithValue(ctx.Context, name, value)
|
||||
ctx.Context = newCtx
|
||||
}
|
||||
|
||||
@@ -36,11 +36,26 @@ $ curl 127.0.0.1:2000
|
||||
|
||||
## Deploy a function onto remote host
|
||||
|
||||
* make sure your instance can be ssh login
|
||||
* make sure you can ssh login to target host with root
|
||||
|
||||
Update `/etc/ssh/sshd_config` to allow login with root.
|
||||
|
||||
```
|
||||
PermitRootLogin yes
|
||||
```
|
||||
|
||||
Then restart sshd with,
|
||||
|
||||
```shell
|
||||
$ sudo service sshd restart
|
||||
```
|
||||
|
||||
* make sure your instance accept port 8866
|
||||
|
||||
[FYI](https://lightsail.aws.amazon.com/ls/docs/en_us/articles/understanding-firewall-and-port-mappings-in-amazon-lightsail)
|
||||
|
||||
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
|
||||
fx up --host root@<your host> test/functions/func.js
|
||||
```
|
||||
|
||||
@@ -6,48 +6,63 @@ import (
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||
"github.com/metrue/fx/infra"
|
||||
"github.com/metrue/fx/driver"
|
||||
"github.com/metrue/fx/pkg/spinner"
|
||||
"github.com/metrue/fx/types"
|
||||
)
|
||||
|
||||
// Deployer manage container
|
||||
type Deployer struct {
|
||||
cli containerruntimes.ContainerRuntime
|
||||
// Driver manage container
|
||||
type Driver struct {
|
||||
dockerClient containerruntimes.ContainerRuntime
|
||||
}
|
||||
|
||||
// CreateClient create a docker instance
|
||||
func CreateClient(client containerruntimes.ContainerRuntime) (d *Deployer, err error) {
|
||||
return &Deployer{cli: client}, nil
|
||||
// Options to initialize a fx docker driver
|
||||
type Options struct {
|
||||
DockerClient containerruntimes.ContainerRuntime
|
||||
}
|
||||
|
||||
// New a fx docker driver
|
||||
func New(options Options) *Driver {
|
||||
return &Driver{
|
||||
dockerClient: options.DockerClient,
|
||||
}
|
||||
}
|
||||
|
||||
// Ping check healty status of driver
|
||||
func (d *Driver) Ping(ctx context.Context) error {
|
||||
if _, err := d.dockerClient.Version(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deploy create a Docker container from given image, and bind the constants.FxContainerExposePort to given port
|
||||
func (d *Deployer) Deploy(ctx context.Context, fn string, name string, image string, ports []types.PortBinding) (err error) {
|
||||
func (d *Driver) Deploy(ctx context.Context, fn string, name string, image string, ports []types.PortBinding) (err error) {
|
||||
spinner.Start("deploying " + name)
|
||||
defer func() {
|
||||
spinner.Stop("deploying "+name, err)
|
||||
}()
|
||||
return d.cli.StartContainer(ctx, name, image, ports)
|
||||
return d.dockerClient.StartContainer(ctx, name, image, ports)
|
||||
}
|
||||
|
||||
// Update a container
|
||||
func (d *Deployer) Update(ctx context.Context, name string) error {
|
||||
func (d *Driver) Update(ctx context.Context, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroy stop and remove container
|
||||
func (d *Deployer) Destroy(ctx context.Context, name string) (err error) {
|
||||
func (d *Driver) Destroy(ctx context.Context, name string) (err error) {
|
||||
spinner.Start("destroying " + name)
|
||||
defer func() {
|
||||
spinner.Stop("destroying "+name, err)
|
||||
}()
|
||||
return d.cli.StopContainer(ctx, name)
|
||||
return d.dockerClient.StopContainer(ctx, name)
|
||||
}
|
||||
|
||||
// GetStatus get a service status
|
||||
func (d *Deployer) GetStatus(ctx context.Context, name string) (types.Service, error) {
|
||||
func (d *Driver) GetStatus(ctx context.Context, name string) (types.Service, error) {
|
||||
var container dockerTypes.ContainerJSON
|
||||
if err := d.cli.InspectContainer(ctx, name, &container); err != nil {
|
||||
if err := d.dockerClient.InspectContainer(ctx, name, &container); err != nil {
|
||||
return types.Service{}, err
|
||||
}
|
||||
|
||||
@@ -55,6 +70,7 @@ func (d *Deployer) GetStatus(ctx context.Context, name string) (types.Service, e
|
||||
ID: container.ID,
|
||||
Name: container.Name,
|
||||
}
|
||||
|
||||
for _, bindings := range container.NetworkSettings.Ports {
|
||||
if len(bindings) > 0 {
|
||||
binding := bindings[0]
|
||||
@@ -76,20 +92,12 @@ func (d *Deployer) GetStatus(ctx context.Context, name string) (types.Service, e
|
||||
return service, nil
|
||||
}
|
||||
|
||||
// Ping check healty status of infra
|
||||
func (d *Deployer) Ping(ctx context.Context) error {
|
||||
if _, err := d.cli.Version(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List services
|
||||
func (d *Deployer) List(ctx context.Context, name string) (svcs []types.Service, err error) {
|
||||
func (d *Driver) List(ctx context.Context, name string) (svcs []types.Service, err error) {
|
||||
// FIXME support remote host
|
||||
return d.cli.ListContainer(ctx, name)
|
||||
return d.dockerClient.ListContainer(ctx, name)
|
||||
}
|
||||
|
||||
var (
|
||||
_ infra.Deployer = &Deployer{}
|
||||
_ driver.Driver = &Driver{}
|
||||
)
|
||||
94
driver/docker/docker_test.go
Normal file
94
driver/docker/docker_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
dockerMock "github.com/metrue/fx/container_runtimes/mocks"
|
||||
"github.com/metrue/fx/types"
|
||||
)
|
||||
|
||||
func TestDriverPing(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
dockerClient := dockerMock.NewMockContainerRuntime(ctrl)
|
||||
n := New(Options{
|
||||
DockerClient: dockerClient,
|
||||
})
|
||||
ctx := context.Background()
|
||||
dockerClient.EXPECT().Version(ctx).Return("", nil)
|
||||
if err := n.Ping(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDriverDeploy(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
dockerClient := dockerMock.NewMockContainerRuntime(ctrl)
|
||||
n := New(Options{
|
||||
DockerClient: dockerClient,
|
||||
})
|
||||
ctx := context.Background()
|
||||
fn := "fn"
|
||||
name := "name"
|
||||
image := "image"
|
||||
ports := []types.PortBinding{}
|
||||
dockerClient.EXPECT().StartContainer(ctx, name, image, ports).Return(nil)
|
||||
if err := n.Deploy(ctx, fn, name, image, ports); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDriverDestroy(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
dockerClient := dockerMock.NewMockContainerRuntime(ctrl)
|
||||
n := New(Options{
|
||||
DockerClient: dockerClient,
|
||||
})
|
||||
ctx := context.Background()
|
||||
name := "name"
|
||||
dockerClient.EXPECT().StopContainer(ctx, name).Return(nil)
|
||||
if err := n.Destroy(ctx, name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDriverGetStatus(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
dockerClient := dockerMock.NewMockContainerRuntime(ctrl)
|
||||
n := New(Options{
|
||||
DockerClient: dockerClient,
|
||||
})
|
||||
ctx := context.Background()
|
||||
name := "name"
|
||||
err := errors.New("no such container")
|
||||
dockerClient.EXPECT().InspectContainer(ctx, name, gomock.Any()).Return(err)
|
||||
if _, err := n.GetStatus(ctx, name); err == nil {
|
||||
t.Fatalf("should get error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
dockerClient := dockerMock.NewMockContainerRuntime(ctrl)
|
||||
n := New(Options{
|
||||
DockerClient: dockerClient,
|
||||
})
|
||||
ctx := context.Background()
|
||||
name := "name"
|
||||
dockerClient.EXPECT().ListContainer(ctx, name).Return(nil, nil)
|
||||
if _, err := n.List(ctx, name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package infra
|
||||
package driver
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -6,17 +6,8 @@ import (
|
||||
"github.com/metrue/fx/types"
|
||||
)
|
||||
|
||||
// Clouder cloud interface
|
||||
type Clouder interface {
|
||||
Provision() error
|
||||
GetConfig() (string, error)
|
||||
GetType() string
|
||||
Dump() ([]byte, error)
|
||||
IsHealth() (bool, error)
|
||||
}
|
||||
|
||||
// Deployer deploy interface
|
||||
type Deployer interface {
|
||||
// Driver fx function running driver
|
||||
type Driver interface {
|
||||
Deploy(ctx context.Context, fn string, name string, image string, bindings []types.PortBinding) error
|
||||
Destroy(ctx context.Context, name string) error
|
||||
Update(ctx context.Context, name string) error
|
||||
@@ -24,8 +15,3 @@ type Deployer interface {
|
||||
List(ctx context.Context, name string) ([]types.Service, error)
|
||||
Ping(ctx context.Context) error
|
||||
}
|
||||
|
||||
// Infra infrastructure provision interface
|
||||
type Infra interface {
|
||||
Deployer
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/metrue/fx/infra"
|
||||
"github.com/metrue/fx/driver"
|
||||
"github.com/metrue/fx/pkg/spinner"
|
||||
"github.com/metrue/fx/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
@@ -37,6 +37,11 @@ func Create(kubeconfig string) (*K8S, error) {
|
||||
return &K8S{clientset}, nil
|
||||
}
|
||||
|
||||
// Provision TODO may need support manually k8s cluster creation
|
||||
func (k *K8S) Provision(ctx context.Context, isRemote bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deploy a image to be a service
|
||||
func (k *K8S) Deploy(
|
||||
ctx context.Context,
|
||||
@@ -186,5 +191,5 @@ func (k *K8S) Ping(ctx context.Context) error {
|
||||
}
|
||||
|
||||
var (
|
||||
_ infra.Deployer = &K8S{}
|
||||
_ driver.Driver = &K8S{}
|
||||
)
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -29,7 +28,7 @@ func generateDeploymentSpec(
|
||||
Name: "fx-placeholder-container-name",
|
||||
Image: image,
|
||||
Ports: ports,
|
||||
ImagePullPolicy: v1.PullIfNotPresent,
|
||||
ImagePullPolicy: apiv1.PullIfNotPresent,
|
||||
}
|
||||
return &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -2,7 +2,6 @@ package k8s
|
||||
|
||||
import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
@@ -64,7 +63,7 @@ func injectInitContainer(name string, deployment *appsv1.Deployment) *appsv1.Dep
|
||||
},
|
||||
},
|
||||
}
|
||||
deployment.Spec.Template.Spec.InitContainers = []apiv1.Container{initContainer}
|
||||
deployment.Spec.Template.Spec.InitContainers = []v1.Container{initContainer}
|
||||
deployment.Spec.Template.Spec.Volumes = volumes
|
||||
return deployment
|
||||
}
|
||||
135
driver/mocks/driver.go
Normal file
135
driver/mocks/driver.go
Normal file
@@ -0,0 +1,135 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: driver.go
|
||||
|
||||
// Package mock_driver is a generated GoMock package.
|
||||
package mock_driver
|
||||
|
||||
import (
|
||||
context "context"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
types "github.com/metrue/fx/types"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockDriver is a mock of Driver interface
|
||||
type MockDriver struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockDriverMockRecorder
|
||||
}
|
||||
|
||||
// MockDriverMockRecorder is the mock recorder for MockDriver
|
||||
type MockDriverMockRecorder struct {
|
||||
mock *MockDriver
|
||||
}
|
||||
|
||||
// NewMockDriver creates a new mock instance
|
||||
func NewMockDriver(ctrl *gomock.Controller) *MockDriver {
|
||||
mock := &MockDriver{ctrl: ctrl}
|
||||
mock.recorder = &MockDriverMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockDriver) EXPECT() *MockDriverMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Deploy mocks base method
|
||||
func (m *MockDriver) Deploy(ctx context.Context, fn, name, image string, bindings []types.PortBinding) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Deploy", ctx, fn, name, image, bindings)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Deploy indicates an expected call of Deploy
|
||||
func (mr *MockDriverMockRecorder) Deploy(ctx, fn, name, image, bindings interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deploy", reflect.TypeOf((*MockDriver)(nil).Deploy), ctx, fn, name, image, bindings)
|
||||
}
|
||||
|
||||
// Provision mocks base method
|
||||
func (m *MockDriver) Provision(ctx context.Context) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Provision", ctx)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Provision indicates an expected call of Provision
|
||||
func (mr *MockDriverMockRecorder) Provision(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Provision", reflect.TypeOf((*MockDriver)(nil).Provision), ctx)
|
||||
}
|
||||
|
||||
// Destroy mocks base method
|
||||
func (m *MockDriver) Destroy(ctx context.Context, name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Destroy", ctx, name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Destroy indicates an expected call of Destroy
|
||||
func (mr *MockDriverMockRecorder) Destroy(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Destroy", reflect.TypeOf((*MockDriver)(nil).Destroy), ctx, name)
|
||||
}
|
||||
|
||||
// Update mocks base method
|
||||
func (m *MockDriver) Update(ctx context.Context, name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Update", ctx, name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Update indicates an expected call of Update
|
||||
func (mr *MockDriverMockRecorder) Update(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockDriver)(nil).Update), ctx, name)
|
||||
}
|
||||
|
||||
// GetStatus mocks base method
|
||||
func (m *MockDriver) GetStatus(ctx context.Context, name string) (types.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetStatus", ctx, name)
|
||||
ret0, _ := ret[0].(types.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetStatus indicates an expected call of GetStatus
|
||||
func (mr *MockDriverMockRecorder) GetStatus(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatus", reflect.TypeOf((*MockDriver)(nil).GetStatus), ctx, name)
|
||||
}
|
||||
|
||||
// List mocks base method
|
||||
func (m *MockDriver) List(ctx context.Context, name string) ([]types.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "List", ctx, name)
|
||||
ret0, _ := ret[0].([]types.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// List indicates an expected call of List
|
||||
func (mr *MockDriverMockRecorder) List(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockDriver)(nil).List), ctx, name)
|
||||
}
|
||||
|
||||
// Ping mocks base method
|
||||
func (m *MockDriver) Ping(ctx context.Context) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Ping", ctx)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Ping indicates an expected call of Ping
|
||||
func (mr *MockDriverMockRecorder) Ping(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockDriver)(nil).Ping), ctx)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package infra
|
||||
package driver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package infra
|
||||
package driver
|
||||
|
||||
// Sudo append sudo when user is not root
|
||||
func Sudo(cmd string, user string) string {
|
||||
169
fx.go
169
fx.go
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/user"
|
||||
"regexp"
|
||||
|
||||
"github.com/apex/log"
|
||||
@@ -13,11 +14,12 @@ import (
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/handlers"
|
||||
"github.com/metrue/fx/middlewares"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/urfave/cli"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
)
|
||||
|
||||
const version = "0.9.2"
|
||||
const version = "0.9.33"
|
||||
|
||||
func init() {
|
||||
go checkForUpdate()
|
||||
@@ -77,61 +79,18 @@ func main() {
|
||||
fmt.Println(aurora.Red("*****************"))
|
||||
}
|
||||
}()
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defaultHost := user.Username + "@localhost"
|
||||
|
||||
defaultSSHKeyFile, err := homedir.Expand("~/.ssh/id_rsa")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "infra",
|
||||
Usage: "manage infrastructure",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "create",
|
||||
Usage: "create a infra for fx service",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "type, t",
|
||||
Usage: "infracture type, 'docker', 'k8s' and 'k3s' support",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "name, n",
|
||||
Usage: "name to identify the infrastructure",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "host",
|
||||
Usage: "user and ip of your host, eg. 'root@182.12.1.12'",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "master",
|
||||
Usage: "serve as master node in K3S cluster, eg. 'root@182.12.1.12'",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "agents",
|
||||
Usage: "serve as agent node in K3S cluster, eg. 'root@187.1. 2. 3,root@123.3.2.1'",
|
||||
},
|
||||
},
|
||||
|
||||
Action: handle(
|
||||
middlewares.LoadConfig,
|
||||
handlers.Setup,
|
||||
),
|
||||
},
|
||||
{
|
||||
Name: "list",
|
||||
Usage: "list all infrastructures",
|
||||
Action: handle(
|
||||
middlewares.LoadConfig,
|
||||
handlers.ListInfra,
|
||||
),
|
||||
},
|
||||
{
|
||||
Name: "use",
|
||||
Usage: "set current context to target cloud with given name",
|
||||
Action: handle(
|
||||
middlewares.LoadConfig,
|
||||
handlers.UseInfra,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "up",
|
||||
Usage: "deploy a function",
|
||||
@@ -146,6 +105,25 @@ func main() {
|
||||
Name: "port, p",
|
||||
Usage: "port number",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "host, H",
|
||||
Usage: "target host, <user>@<host>",
|
||||
Value: defaultHost,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "ssh_port, P",
|
||||
Usage: "SSH port for target host",
|
||||
Value: "22",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "ssh_key, K",
|
||||
Usage: "SSH key file for login target host",
|
||||
Value: defaultSSHKeyFile,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "kubeconf, C",
|
||||
Usage: "kubeconf of kubernetes cluster",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "healthcheck, hc",
|
||||
Usage: "do a health check after service up",
|
||||
@@ -156,11 +134,11 @@ func main() {
|
||||
},
|
||||
},
|
||||
Action: handle(
|
||||
middlewares.LoadConfig,
|
||||
middlewares.Provision,
|
||||
middlewares.Parse("up"),
|
||||
middlewares.Language(),
|
||||
middlewares.Binding,
|
||||
middlewares.SSH,
|
||||
middlewares.Driver,
|
||||
middlewares.Build,
|
||||
handlers.Up,
|
||||
),
|
||||
@@ -169,10 +147,31 @@ func main() {
|
||||
Name: "down",
|
||||
Usage: "destroy a service",
|
||||
ArgsUsage: "[service 1, service 2, ....]",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "ssh_port, P",
|
||||
Usage: "SSH port for target host",
|
||||
Value: "22",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "ssh_key, K",
|
||||
Usage: "SSH key file for login target host",
|
||||
Value: defaultSSHKeyFile,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "host, H",
|
||||
Usage: "target host, <user>@<host>",
|
||||
Value: defaultHost,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "kubeconf, C",
|
||||
Usage: "kubeconf of kubernetes cluster",
|
||||
},
|
||||
},
|
||||
Action: handle(
|
||||
middlewares.Parse("down"),
|
||||
middlewares.LoadConfig,
|
||||
middlewares.Provision,
|
||||
middlewares.SSH,
|
||||
middlewares.Driver,
|
||||
handlers.Down,
|
||||
),
|
||||
},
|
||||
@@ -186,25 +185,33 @@ func main() {
|
||||
Value: "table",
|
||||
Usage: "output format, 'table' and 'JSON' supported",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "ssh_port, P",
|
||||
Usage: "SSH port for target host",
|
||||
Value: "22",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "ssh_key, K",
|
||||
Usage: "SSH key file for login target host",
|
||||
Value: defaultSSHKeyFile,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "host, H",
|
||||
Usage: "target host, <user>@<host>",
|
||||
Value: defaultHost,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "kubeconf, C",
|
||||
Usage: "kubeconf of kubernetes cluster",
|
||||
},
|
||||
},
|
||||
Action: handle(
|
||||
middlewares.Parse("list"),
|
||||
middlewares.LoadConfig,
|
||||
middlewares.Provision,
|
||||
middlewares.SSH,
|
||||
middlewares.Driver,
|
||||
handlers.List,
|
||||
),
|
||||
},
|
||||
{
|
||||
Name: "call",
|
||||
Usage: "run a function instantly",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "host, H",
|
||||
Usage: "fx server host, default is localhost",
|
||||
},
|
||||
},
|
||||
Action: handle(handlers.Call),
|
||||
},
|
||||
{
|
||||
Name: "image",
|
||||
Usage: "manage image of service",
|
||||
@@ -213,14 +220,23 @@ func main() {
|
||||
Name: "build",
|
||||
Usage: "build a image",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "ssh_port, P",
|
||||
Usage: "SSH port for target host",
|
||||
Value: "22",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "ssh_key, K",
|
||||
Usage: "SSH key file for login target host",
|
||||
Value: defaultSSHKeyFile,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "tag, t",
|
||||
Usage: "image tag",
|
||||
Value: uuid.New().String(),
|
||||
},
|
||||
},
|
||||
Action: handle(
|
||||
middlewares.LoadConfig,
|
||||
middlewares.Provision,
|
||||
middlewares.Parse("image_build"),
|
||||
middlewares.Language(),
|
||||
handlers.BuildImage,
|
||||
@@ -236,8 +252,6 @@ func main() {
|
||||
},
|
||||
},
|
||||
Action: handle(
|
||||
middlewares.LoadConfig,
|
||||
middlewares.Provision,
|
||||
middlewares.Parse("image_export"),
|
||||
middlewares.Language(),
|
||||
handlers.ExportImage,
|
||||
@@ -245,11 +259,6 @@ func main() {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "doctor",
|
||||
Usage: "health check for fx",
|
||||
Action: handle(handlers.Doctor),
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
|
||||
13
go.mod
13
go.mod
@@ -14,9 +14,9 @@ require (
|
||||
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/packd v0.3.0
|
||||
github.com/gobuffalo/packr/v2 v2.5.1
|
||||
github.com/golang/mock v1.4.1
|
||||
github.com/gobuffalo/packd v1.0.0
|
||||
github.com/gobuffalo/packr/v2 v2.8.0
|
||||
github.com/golang/mock v1.4.2
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/google/go-querystring v1.0.0
|
||||
github.com/google/uuid v1.1.1
|
||||
@@ -24,7 +24,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-20191219103445-1f07b67e2b29
|
||||
github.com/metrue/go-ssh-client v0.0.0-20200317072149-19d54050aefd
|
||||
github.com/mholt/archiver v3.1.1+incompatible
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
@@ -32,16 +32,15 @@ require (
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/otiai10/copy v1.0.2
|
||||
github.com/otiai10/copy v1.1.1
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2
|
||||
github.com/pierrec/lz4 v0.0.0-20190222153722-062282ea0dcf // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rogpeppe/go-internal v1.5.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.6.2
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/ugorji/go v1.1.7 // indirect
|
||||
github.com/urfave/cli v1.22.2
|
||||
github.com/urfave/cli v1.22.3
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915 // indirect
|
||||
golang.org/x/sys v0.0.0-20191223224216-5a3cf8467b4e // indirect
|
||||
|
||||
44
go.sum
44
go.sum
@@ -45,6 +45,8 @@ github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -93,10 +95,16 @@ 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/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc=
|
||||
github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM=
|
||||
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/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM=
|
||||
github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI=
|
||||
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/gobuffalo/packr/v2 v2.8.0 h1:IULGd15bQL59ijXLxEvA5wlMxsmx/ZkQv9T282zNVIY=
|
||||
github.com/gobuffalo/packr/v2 v2.8.0/go.mod h1:PDk2k3vGevNE3SwVyVRgQCCXETC9SaONCNSXT1Q8M1g=
|
||||
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=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
|
||||
@@ -111,6 +119,8 @@ github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.1 h1:ocYkMQY5RrXTYgXl7ICpV0IXwlEQGwKIsery4gyXa1U=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.2 h1:fXIkPzOBCwDUPvYmOPzETABgbqpYlYNigQ2o64eMr5c=
|
||||
github.com/golang/mock v1.4.2/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -171,6 +181,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/karrick/godirwalk v1.10.12 h1:BqUm+LuJcXjGv1d2mj3gBiQyrQ57a0rYoAmhvJQ7RDU=
|
||||
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||
github.com/karrick/godirwalk v1.15.3 h1:0a2pXOgtB16CqIqXTiT7+K9L73f74n/aNQUnH6Ortew=
|
||||
github.com/karrick/godirwalk v1.15.3/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
@@ -192,6 +204,12 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
|
||||
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/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI=
|
||||
github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc=
|
||||
github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY=
|
||||
github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI=
|
||||
github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
|
||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||
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=
|
||||
@@ -205,6 +223,12 @@ github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
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/metrue/go-ssh-client v0.0.0-20200317034720-e3f45ef1916d h1:vLJfcEJfjiL2LBGBoZbS1mcNjVBXxM4v2vZuUX5XiBE=
|
||||
github.com/metrue/go-ssh-client v0.0.0-20200317034720-e3f45ef1916d/go.mod h1:aPG/JtXTyLliKDDlkv+nzHbSbz2p2CBMAjNJRK4uhzY=
|
||||
github.com/metrue/go-ssh-client v0.0.0-20200317035802-7c3bc0c87929 h1:Z/+UhJcqXfrTgosjU8s91ASl8JOqh29/YJh8Tu26hw8=
|
||||
github.com/metrue/go-ssh-client v0.0.0-20200317035802-7c3bc0c87929/go.mod h1:aPG/JtXTyLliKDDlkv+nzHbSbz2p2CBMAjNJRK4uhzY=
|
||||
github.com/metrue/go-ssh-client v0.0.0-20200317072149-19d54050aefd h1:HoDa3tI6njhpyhu7aIcIfib8QugB66ILgYRLc5IuP6s=
|
||||
github.com/metrue/go-ssh-client v0.0.0-20200317072149-19d54050aefd/go.mod h1:mgU+XR/ItF6PaQGpx0MthaaMP2dgGuck0IiXcrF3zMw=
|
||||
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=
|
||||
@@ -240,10 +264,14 @@ 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/copy v1.1.1 h1:PH7IFlRQ6Fv9vYmuXbDRLdgTHoP1w483kPNUP2bskpo=
|
||||
github.com/otiai10/copy v1.1.1/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
|
||||
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/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
@@ -277,6 +305,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||
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/rogpeppe/go-internal v1.5.2/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=
|
||||
@@ -304,6 +333,7 @@ github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
@@ -313,6 +343,7 @@ 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.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E=
|
||||
github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -343,6 +374,8 @@ 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.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.3 h1:FpNT6zq26xNpHZy08emi755QwzLPs6Pukqjlc7RfOMU=
|
||||
github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
@@ -361,6 +394,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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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=
|
||||
@@ -368,6 +403,7 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -384,6 +420,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68=
|
||||
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -395,6 +433,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -433,6 +472,11 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c h1:KfpJVdWhuRqNk4XVXzjXf2KAV4TBEP77SYdFGjeGuIE=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/metrue/fx/context"
|
||||
)
|
||||
|
||||
// Call command handle
|
||||
func Call(ctx context.Contexter) error {
|
||||
// TODO not supported
|
||||
return nil
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/metrue/fx/constants"
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/doctor"
|
||||
)
|
||||
|
||||
// Doctor command handle
|
||||
func Doctor(ctx context.Contexter) error {
|
||||
host := os.Getenv("DOCKER_REMOTE_HOST_ADDR")
|
||||
user := os.Getenv("DOCKER_REMOTE_HOST_USER")
|
||||
password := os.Getenv("DOCKER_REMOTE_HOST_PASSWORD")
|
||||
if host == "" {
|
||||
host = "localhost"
|
||||
}
|
||||
if err := doctor.New(host, user, password).Start(); err != nil {
|
||||
log.Warnf("machine %s is in dirty state: %v", host, err)
|
||||
} else {
|
||||
log.Infof("machine %s is in healthy state: %s", host, constants.CheckedSymbol)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2,17 +2,23 @@ package handlers
|
||||
|
||||
import (
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/infra"
|
||||
"github.com/metrue/fx/driver"
|
||||
)
|
||||
|
||||
// Down command handle
|
||||
func Down(ctx context.Contexter) (err error) {
|
||||
services := ctx.Get("services").([]string)
|
||||
runner := ctx.Get("deployer").(infra.Deployer)
|
||||
for _, svc := range services {
|
||||
if err := runner.Destroy(ctx.GetContext(), svc); err != nil {
|
||||
return err
|
||||
for _, targetdriver := range []string{"docker_driver", "k8s_driver"} {
|
||||
driver, ok := ctx.Get(targetdriver).(driver.Driver)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, svc := range services {
|
||||
if err := driver.Destroy(ctx.GetContext(), svc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
mockCtx "github.com/metrue/fx/context/mocks"
|
||||
mockDeployer "github.com/metrue/fx/infra/mocks"
|
||||
mockDeployer "github.com/metrue/fx/driver/mocks"
|
||||
)
|
||||
|
||||
func TestDown(t *testing.T) {
|
||||
@@ -14,13 +14,14 @@ func TestDown(t *testing.T) {
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := mockCtx.NewMockContexter(ctrl)
|
||||
deployer := mockDeployer.NewMockDeployer(ctrl)
|
||||
driver := mockDeployer.NewMockDriver(ctrl)
|
||||
|
||||
services := []string{"sample-name"}
|
||||
ctx.EXPECT().Get("services").Return(services)
|
||||
ctx.EXPECT().Get("deployer").Return(deployer)
|
||||
ctx.EXPECT().GetContext().Return(context.Background())
|
||||
deployer.EXPECT().Destroy(gomock.Any(), services[0]).Return(nil)
|
||||
ctx.EXPECT().Get("docker_driver").Return(driver)
|
||||
ctx.EXPECT().Get("k8s_driver").Return(driver)
|
||||
ctx.EXPECT().GetContext().Return(context.Background()).Times(2)
|
||||
driver.EXPECT().Destroy(gomock.Any(), services[0]).Return(nil).Times(2)
|
||||
if err := Down(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/metrue/fx/config"
|
||||
"github.com/metrue/fx/context"
|
||||
dockerInfra "github.com/metrue/fx/infra/docker"
|
||||
k8sInfra "github.com/metrue/fx/infra/k8s"
|
||||
"github.com/metrue/fx/pkg/spinner"
|
||||
)
|
||||
|
||||
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")
|
||||
}
|
||||
master, err := k8sInfra.CreateNode(info[1], info[0], "k3s_master", "master")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes := []k8sInfra.Noder{master}
|
||||
if agentsInfo != "" {
|
||||
agentsInfoList := strings.Split(agentsInfo, ",")
|
||||
for idx, agent := range agentsInfoList {
|
||||
info := strings.Split(agent, "@")
|
||||
if len(info) != 2 {
|
||||
return nil, fmt.Errorf("incorrect agent info, should be <user>@<ip> format")
|
||||
}
|
||||
node, err := k8sInfra.CreateNode(info[1], info[0], "k3s_agent", fmt.Sprintf("agent-%d", idx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
}
|
||||
kubeconfigPath := filepath.Join(configDir, name+".kubeconfig")
|
||||
cloud := k8sInfra.NewCloud(kubeconfigPath, nodes...)
|
||||
if err := cloud.Provision(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cloud.Dump()
|
||||
}
|
||||
|
||||
func setupDocker(hostInfo string, name string) ([]byte, error) {
|
||||
info := strings.Split(hostInfo, "@")
|
||||
if len(info) != 2 {
|
||||
return nil, fmt.Errorf("incorrect master info, should be <user>@<ip> format")
|
||||
}
|
||||
user := info[0]
|
||||
host := info[1]
|
||||
|
||||
cloud, err := dockerInfra.Create(host, user, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := cloud.Provision(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cloud.Dump()
|
||||
}
|
||||
|
||||
// Setup infra
|
||||
func Setup(ctx context.Contexter) (err error) {
|
||||
const task = "setup infra"
|
||||
spinner.Start(task)
|
||||
defer func() {
|
||||
spinner.Stop(task, err)
|
||||
}()
|
||||
|
||||
cli := ctx.GetCliContext()
|
||||
typ := cli.String("type")
|
||||
name := cli.String("name")
|
||||
if name == "" {
|
||||
return fmt.Errorf("name required")
|
||||
}
|
||||
if typ == "docker" {
|
||||
if cli.String("host") == "" {
|
||||
return fmt.Errorf("host required, eg. 'root@123.1.2.12'")
|
||||
}
|
||||
} else if typ == "k8s" {
|
||||
if cli.String("master") == "" {
|
||||
return fmt.Errorf("master required, eg. 'root@123.1.2.12'")
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("invalid type, 'docker' and 'k8s' support")
|
||||
}
|
||||
|
||||
fxConfig := ctx.Get("config").(*config.Config)
|
||||
|
||||
switch strings.ToLower(typ) {
|
||||
case "k8s":
|
||||
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
|
||||
}
|
||||
return fxConfig.AddCloud(name, kubeconf)
|
||||
case "docker":
|
||||
config, err := setupDocker(cli.String("host"), name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fxConfig.AddCloud(name, config)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2,20 +2,27 @@ package handlers
|
||||
|
||||
import (
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/infra"
|
||||
"github.com/metrue/fx/driver"
|
||||
"github.com/metrue/fx/pkg/renderrer"
|
||||
)
|
||||
|
||||
// List command handle
|
||||
func List(ctx context.Contexter) (err error) {
|
||||
cli := ctx.GetCliContext()
|
||||
deployer := ctx.Get("deployer").(infra.Deployer)
|
||||
format := ctx.Get("format").(string)
|
||||
|
||||
services, err := deployer.List(ctx.GetContext(), cli.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
for _, targetdriver := range []string{"docker_driver", "k8s_driver"} {
|
||||
driver, ok := ctx.Get(targetdriver).(driver.Driver)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
services, err := driver.List(ctx.GetContext(), cli.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := renderrer.Render(services, format); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return renderrer.Render(services, format)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/metrue/fx/config"
|
||||
"github.com/metrue/fx/context"
|
||||
)
|
||||
|
||||
// ListInfra list infra
|
||||
func ListInfra(ctx context.Contexter) (err error) {
|
||||
fxConfig := ctx.Get("config").(*config.Config)
|
||||
conf, err := fxConfig.View()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(conf))
|
||||
return nil
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package handlers
|
||||
import (
|
||||
"github.com/apex/log"
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/infra"
|
||||
"github.com/metrue/fx/driver"
|
||||
"github.com/metrue/fx/pkg/renderrer"
|
||||
"github.com/metrue/fx/types"
|
||||
)
|
||||
@@ -19,28 +19,37 @@ func Up(ctx context.Contexter) (err error) {
|
||||
image = ""
|
||||
}
|
||||
name := ctx.Get("name").(string)
|
||||
deployer := ctx.Get("deployer").(infra.Deployer)
|
||||
bindings := ctx.Get("bindings").([]types.PortBinding)
|
||||
force := ctx.Get("force").(bool)
|
||||
if force && name != "" {
|
||||
if err := deployer.Destroy(ctx.GetContext(), name); err != nil {
|
||||
log.Warnf("destroy service %s failed: %v", name, err)
|
||||
|
||||
for _, targetdriver := range []string{"docker_driver", "k8s_driver"} {
|
||||
driver, ok := ctx.Get(targetdriver).(driver.Driver)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if force && name != "" {
|
||||
if err := driver.Destroy(ctx.GetContext(), name); err != nil {
|
||||
log.Warnf("destroy service %s failed: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := driver.Deploy(
|
||||
ctx.GetContext(),
|
||||
fn,
|
||||
name,
|
||||
image,
|
||||
bindings,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service, err := driver.GetStatus(ctx.GetContext(), name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := renderrer.Render([]types.Service{service}, "table"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := deployer.Deploy(
|
||||
ctx.GetContext(),
|
||||
fn,
|
||||
name,
|
||||
image,
|
||||
bindings,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service, err := deployer.GetStatus(ctx.GetContext(), name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return renderrer.Render([]types.Service{service}, "table")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
mockCtx "github.com/metrue/fx/context/mocks"
|
||||
mockDeployer "github.com/metrue/fx/infra/mocks"
|
||||
mockDeployer "github.com/metrue/fx/driver/mocks"
|
||||
"github.com/metrue/fx/types"
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestUp(t *testing.T) {
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := mockCtx.NewMockContexter(ctrl)
|
||||
deployer := mockDeployer.NewMockDeployer(ctrl)
|
||||
driver := mockDeployer.NewMockDriver(ctrl)
|
||||
|
||||
bindings := []types.PortBinding{}
|
||||
name := "sample-name"
|
||||
@@ -24,18 +24,19 @@ func TestUp(t *testing.T) {
|
||||
data := "sample-data"
|
||||
ctx.EXPECT().Get("name").Return(name)
|
||||
ctx.EXPECT().Get("image").Return(image)
|
||||
ctx.EXPECT().Get("deployer").Return(deployer)
|
||||
ctx.EXPECT().Get("docker_driver").Return(driver)
|
||||
ctx.EXPECT().Get("k8s_driver").Return(driver)
|
||||
ctx.EXPECT().Get("bindings").Return(bindings)
|
||||
ctx.EXPECT().Get("data").Return(data)
|
||||
ctx.EXPECT().Get("force").Return(false)
|
||||
ctx.EXPECT().GetContext().Return(context.Background()).Times(2)
|
||||
deployer.EXPECT().Deploy(gomock.Any(), data, name, image, bindings).Return(nil)
|
||||
deployer.EXPECT().GetStatus(gomock.Any(), name).Return(types.Service{
|
||||
ctx.EXPECT().GetContext().Return(context.Background()).Times(4)
|
||||
driver.EXPECT().Deploy(gomock.Any(), data, name, image, bindings).Return(nil).Times(2)
|
||||
driver.EXPECT().GetStatus(gomock.Any(), name).Return(types.Service{
|
||||
ID: "id-1",
|
||||
Name: name,
|
||||
Host: "127.0.0.1",
|
||||
Port: 2100,
|
||||
}, nil)
|
||||
}, nil).Times(2)
|
||||
if err := Up(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -46,7 +47,7 @@ func TestUp(t *testing.T) {
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := mockCtx.NewMockContexter(ctrl)
|
||||
deployer := mockDeployer.NewMockDeployer(ctrl)
|
||||
driver := mockDeployer.NewMockDriver(ctrl)
|
||||
|
||||
bindings := []types.PortBinding{}
|
||||
name := "sample-name"
|
||||
@@ -54,19 +55,20 @@ func TestUp(t *testing.T) {
|
||||
data := "sample-data"
|
||||
ctx.EXPECT().Get("name").Return(name)
|
||||
ctx.EXPECT().Get("image").Return(image)
|
||||
ctx.EXPECT().Get("deployer").Return(deployer)
|
||||
ctx.EXPECT().Get("docker_driver").Return(driver)
|
||||
ctx.EXPECT().Get("k8s_driver").Return(driver)
|
||||
ctx.EXPECT().Get("bindings").Return(bindings)
|
||||
ctx.EXPECT().Get("data").Return(data)
|
||||
ctx.EXPECT().Get("force").Return(true)
|
||||
ctx.EXPECT().GetContext().Return(context.Background()).Times(3)
|
||||
deployer.EXPECT().Deploy(gomock.Any(), data, name, image, bindings).Return(nil)
|
||||
deployer.EXPECT().Destroy(gomock.Any(), name).Return(nil)
|
||||
deployer.EXPECT().GetStatus(gomock.Any(), name).Return(types.Service{
|
||||
ctx.EXPECT().GetContext().Return(context.Background()).Times(6)
|
||||
driver.EXPECT().Deploy(gomock.Any(), data, name, image, bindings).Return(nil).Times(2)
|
||||
driver.EXPECT().Destroy(gomock.Any(), name).Return(nil).Times(2)
|
||||
driver.EXPECT().GetStatus(gomock.Any(), name).Return(types.Service{
|
||||
ID: "id-1",
|
||||
Name: name,
|
||||
Host: "127.0.0.1",
|
||||
Port: 2100,
|
||||
}, nil)
|
||||
}, nil).Times(2)
|
||||
if err := Up(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/metrue/fx/config"
|
||||
"github.com/metrue/fx/context"
|
||||
)
|
||||
|
||||
// UseInfra use infra
|
||||
func UseInfra(ctx context.Contexter) error {
|
||||
fxConfig := ctx.Get("config").(*config.Config)
|
||||
cli := ctx.GetCliContext()
|
||||
return fxConfig.UseCloud(cli.Args().First())
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/metrue/fx/infra"
|
||||
"github.com/metrue/fx/types"
|
||||
"github.com/metrue/go-ssh-client"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// Cloud define a docker host
|
||||
type Cloud struct {
|
||||
IP string `json:"ip"`
|
||||
User string `json:"user"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
|
||||
sshClient ssh.Clienter
|
||||
}
|
||||
|
||||
// New new a docker cloud
|
||||
func New(ip string, user string, name string) *Cloud {
|
||||
return &Cloud{
|
||||
IP: ip,
|
||||
User: user,
|
||||
Name: name,
|
||||
Type: types.CloudTypeDocker,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a docker node
|
||||
func Create(ip string, user string, name string) (*Cloud, error) {
|
||||
key, err := sshkey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
port := sshport()
|
||||
sshClient := ssh.New(ip).WithUser(user).WithKey(key).WithPort(port)
|
||||
return &Cloud{
|
||||
IP: ip,
|
||||
User: user,
|
||||
Name: name,
|
||||
Type: types.CloudTypeDocker,
|
||||
|
||||
sshClient: sshClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Load a docker node from meta
|
||||
func Load(meta []byte) (*Cloud, error) {
|
||||
var cloud Cloud
|
||||
if err := json.Unmarshal(meta, &cloud); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := sshkey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
port := sshport()
|
||||
sshClient := ssh.New(cloud.IP).WithUser(cloud.User).WithKey(key).WithPort(port)
|
||||
cloud.sshClient = sshClient
|
||||
|
||||
return &cloud, nil
|
||||
}
|
||||
|
||||
// Provision a host
|
||||
func (c *Cloud) Provision() error {
|
||||
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.runCmd(infra.Scripts["start_dockerd"].(string)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetType cloud type
|
||||
func (c *Cloud) GetType() string {
|
||||
return c.Type
|
||||
}
|
||||
|
||||
func (c *Cloud) GetConfig() (string, error) {
|
||||
data, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
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) {
|
||||
path := os.Getenv("SSH_KEY_FILE")
|
||||
if path != "" {
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return absPath, nil
|
||||
}
|
||||
|
||||
key, err := homedir.Expand("~/.ssh/id_rsa")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func sshport() string {
|
||||
port := os.Getenv("SSH_PORT")
|
||||
if port != "" {
|
||||
return port
|
||||
}
|
||||
return "22"
|
||||
}
|
||||
|
||||
var (
|
||||
_ infra.Clouder = &Cloud{}
|
||||
)
|
||||
@@ -1,158 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/metrue/fx/infra"
|
||||
"github.com/metrue/go-ssh-client"
|
||||
sshMocks "github.com/metrue/go-ssh-client/mocks"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
func TestCloudProvision(t *testing.T) {
|
||||
t.Run("fx agent started", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
n := New("127.0.0.1", "fx", "master")
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n.setsshClient(sshClient)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["docker_version"].(string), ssh.CommandOptions{}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["check_fx_agent"].(string), ssh.CommandOptions{}).Return(nil)
|
||||
if err := n.Provision(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("fx agent not started", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
n := New("127.0.0.1", "fx", "master")
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n.setsshClient(sshClient)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["docker_version"].(string), ssh.CommandOptions{}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["check_fx_agent"].(string), ssh.CommandOptions{}).Return(fmt.Errorf("no such container"))
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["start_fx_agent"].(string), ssh.CommandOptions{}).Return(nil)
|
||||
if err := n.Provision(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("docker not installed and fx agent not started", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
n := New("127.0.0.1", "fx", "master")
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n.setsshClient(sshClient)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["docker_version"].(string), ssh.CommandOptions{}).Return(fmt.Errorf("no such command"))
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["install_docker"].(string), ssh.CommandOptions{}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["start_dockerd"].(string), ssh.CommandOptions{}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["check_fx_agent"].(string), ssh.CommandOptions{}).Return(fmt.Errorf("no such container"))
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["start_fx_agent"].(string), ssh.CommandOptions{}).Return(nil)
|
||||
if err := n.Provision(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defaultPath, _ := homedir.Expand("~/.ssh/id_rsa")
|
||||
if defau != defaultPath {
|
||||
t.Fatalf("should get %s but got %s", defaultPath, defau)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("override from env", func(t *testing.T) {
|
||||
os.Setenv("SSH_KEY_FILE", "/tmp/id_rsa")
|
||||
keyFile, err := sshkey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if keyFile != "/tmp/id_rsa" {
|
||||
t.Fatalf("should get %s but got %s", "/tmp/id_rsa", keyFile)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetSSHPort(t *testing.T) {
|
||||
t.Run("defaut", func(t *testing.T) {
|
||||
defau := sshport()
|
||||
if defau != "22" {
|
||||
t.Fatalf("should get %s but got %s", "22", defau)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("override from env", func(t *testing.T) {
|
||||
os.Setenv("SSH_PORT", "2222")
|
||||
defau := sshport()
|
||||
if defau != "2222" {
|
||||
t.Fatalf("should get %s but got %s", "2222", defau)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
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, name, bindings); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// time.Sleep(1 * time.Second)
|
||||
//
|
||||
// if err := cli.Destroy(ctx, name); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package docker
|
||||
|
||||
import containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||
|
||||
// CreateDeployer create a deployer
|
||||
func CreateDeployer(client containerruntimes.ContainerRuntime) (*Deployer, error) {
|
||||
return &Deployer{cli: client}, nil
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
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 {
|
||||
// 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
|
||||
func Load(meta []byte, messup ...func(n Noder) (Noder, error)) (*Cloud, error) {
|
||||
var cloud Cloud
|
||||
if err := json.Unmarshal(meta, &cloud); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for name, n := range cloud.Nodes {
|
||||
// NOTE messup function is just for unit testing
|
||||
// we use it to replace the real not with mock node
|
||||
if len(messup) > 0 {
|
||||
node, err := messup[0](n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloud.Nodes[name] = node
|
||||
}
|
||||
}
|
||||
return &cloud, nil
|
||||
}
|
||||
|
||||
// NewCloud new a cloud
|
||||
func NewCloud(kubeconf string, node ...Noder) *Cloud {
|
||||
nodes := map[string]Noder{}
|
||||
for _, n := range node {
|
||||
nodes[n.GetName()] = n
|
||||
}
|
||||
|
||||
return &Cloud{
|
||||
KubeConfig: kubeconf,
|
||||
Type: types.CloudTypeK8S,
|
||||
Nodes: nodes,
|
||||
}
|
||||
}
|
||||
|
||||
// Provision provision cloud
|
||||
func (c *Cloud) Provision() error {
|
||||
var master Noder
|
||||
agents := []Noder{}
|
||||
for _, n := range c.Nodes {
|
||||
if n.GetType() == NodeTypeMaster {
|
||||
master = n
|
||||
} else {
|
||||
agents = append(agents, n)
|
||||
}
|
||||
}
|
||||
|
||||
// when it's k3s cluster
|
||||
if master != nil {
|
||||
c.url = fmt.Sprintf("https://%s:6443", master.GetIP())
|
||||
if err := master.Provision(map[string]string{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tok, err := master.GetToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.token = tok
|
||||
|
||||
config, err := master.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := utils.EnsureFile(c.KubeConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(c.KubeConfig, []byte(config), 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(agents) > 0 {
|
||||
errCh := make(chan error, len(agents))
|
||||
defer close(errCh)
|
||||
|
||||
for _, agent := range agents {
|
||||
go func(node Noder) {
|
||||
errCh <- node.Provision(map[string]string{
|
||||
"url": c.url,
|
||||
"token": c.token,
|
||||
})
|
||||
}(agent)
|
||||
}
|
||||
|
||||
for range agents {
|
||||
err := <-errCh
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddNode a node
|
||||
func (c *Cloud) AddNode(n Noder, skipProvision bool) error {
|
||||
if !skipProvision {
|
||||
if err := n.Provision(map[string]string{
|
||||
"url": c.url,
|
||||
"token": c.token,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.Nodes[n.GetName()] = n
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteNode a node
|
||||
func (c *Cloud) DeleteNode(name string) error {
|
||||
node, ok := c.Nodes[name]
|
||||
if ok {
|
||||
delete(c.Nodes, name)
|
||||
}
|
||||
if node.GetType() == NodeTypeMaster && len(c.Nodes) > 0 {
|
||||
return fmt.Errorf("could not delete master node since there is still agent node running")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// State get cloud state
|
||||
func (c *Cloud) State() {}
|
||||
|
||||
// UnmarshalJSON unmarsha json
|
||||
func (c *Cloud) UnmarshalJSON(data []byte) error {
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal(data, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Nodes = make(map[string]Noder)
|
||||
|
||||
for k, v := range m {
|
||||
if k == "nodes" {
|
||||
nodes, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid nodes data")
|
||||
}
|
||||
for name, n := range nodes {
|
||||
node, ok := n.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid node data")
|
||||
}
|
||||
n, err := CreateNode(node["ip"].(string), node["user"].(string), node["type"].(string), node["name"].(string))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Nodes[name] = n
|
||||
}
|
||||
} else if k == "token" {
|
||||
tok, ok := v.(string)
|
||||
if ok {
|
||||
c.token = tok
|
||||
} else {
|
||||
c.token = ""
|
||||
}
|
||||
} else if k == "config" {
|
||||
config, ok := v.(string)
|
||||
if ok {
|
||||
c.KubeConfig = config
|
||||
} else {
|
||||
c.KubeConfig = ""
|
||||
}
|
||||
} else if k == "type" {
|
||||
typ, ok := v.(string)
|
||||
if ok {
|
||||
c.Type = typ
|
||||
} else {
|
||||
c.Type = ""
|
||||
}
|
||||
} else if k == "url" {
|
||||
url, ok := v.(string)
|
||||
if ok {
|
||||
c.url = url
|
||||
} else {
|
||||
c.url = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON cloud information
|
||||
func (c *Cloud) MarshalJSON() ([]byte, error) {
|
||||
nodes := map[string]Node{}
|
||||
for name, node := range c.Nodes {
|
||||
nodes[name] = Node{
|
||||
IP: node.GetIP(),
|
||||
Type: node.GetType(),
|
||||
User: node.GetUser(),
|
||||
Name: node.GetName(),
|
||||
}
|
||||
}
|
||||
|
||||
body, err := json.Marshal(struct {
|
||||
URL string `json:"url"`
|
||||
KubeConfig string `json:"config"`
|
||||
Type string `json:"type"`
|
||||
Token string `json:"token"`
|
||||
Nodes map[string]Node `json:"nodes"`
|
||||
}{
|
||||
KubeConfig: c.KubeConfig,
|
||||
Type: c.Type,
|
||||
Token: c.token,
|
||||
URL: c.url,
|
||||
Nodes: nodes,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// GetType get type of cloud
|
||||
func (c *Cloud) GetType() string {
|
||||
return c.Type
|
||||
}
|
||||
|
||||
// Dump cloud data
|
||||
func (c *Cloud) Dump() ([]byte, error) {
|
||||
return json.Marshal(c)
|
||||
}
|
||||
|
||||
// GetConfig get config
|
||||
func (c *Cloud) GetConfig() (string, error) {
|
||||
if c.KubeConfig != "" {
|
||||
return c.KubeConfig, nil
|
||||
}
|
||||
if err := c.Provision(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return c.KubeConfig, nil
|
||||
}
|
||||
|
||||
// IsHealth check if cloud is in health
|
||||
func (c *Cloud) IsHealth() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
var (
|
||||
_ infra.Clouder = &Cloud{}
|
||||
)
|
||||
@@ -1,170 +0,0 @@
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
mock_infra "github.com/metrue/fx/infra/k8s/mocks"
|
||||
)
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
t.Run("empty meta", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
var createNodeFn = func(n Noder) (Noder, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
_, err := Load([]byte{}, createNodeFn)
|
||||
if err == nil {
|
||||
t.Fatalf("should load with error")
|
||||
}
|
||||
})
|
||||
|
||||
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()
|
||||
|
||||
master := mock_infra.NewMockNoder(ctrl)
|
||||
var createNodeFn = func(n Noder) (Noder, error) {
|
||||
return master, nil
|
||||
}
|
||||
typ := NodeTypeMaster
|
||||
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(kubeconfContent, nil)
|
||||
|
||||
claud := &Cloud{
|
||||
KubeConfig: kubeconfig,
|
||||
Type: "k8s",
|
||||
url: "",
|
||||
token: "",
|
||||
Nodes: map[string]Noder{"master-node": master},
|
||||
}
|
||||
|
||||
meta, err := json.Marshal(claud)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cloud, err := Load(meta, createNodeFn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(cloud.Nodes) != 1 {
|
||||
t.Fatalf("should get %d but got %d", 1, len(cloud.Nodes))
|
||||
}
|
||||
|
||||
master.EXPECT().Provision(map[string]string{}).Return(nil)
|
||||
master.EXPECT().GetToken().Return("tok-1", 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)
|
||||
}
|
||||
})
|
||||
|
||||
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()
|
||||
|
||||
master := mock_infra.NewMockNoder(ctrl)
|
||||
node := mock_infra.NewMockNoder(ctrl)
|
||||
var createNodeFn = func(n Noder) (Noder, error) {
|
||||
if n.GetType() == NodeTypeMaster {
|
||||
return master, nil
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
typ := NodeTypeMaster
|
||||
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(kubeconfContent, nil)
|
||||
master.EXPECT().GetUser().Return(user)
|
||||
|
||||
nodeType := NodeTypeAgent
|
||||
nodeName := "agent_name"
|
||||
nodeIP := "12.12.12.12"
|
||||
nodeUser := "testuser"
|
||||
node.EXPECT().GetName().Return(nodeName)
|
||||
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{
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cloud, err := Load(meta, createNodeFn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(cloud.Nodes) != 2 {
|
||||
t.Fatalf("should get %d but got %d", 2, len(cloud.Nodes))
|
||||
}
|
||||
|
||||
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,
|
||||
}).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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestProvision(t *testing.T) {}
|
||||
@@ -1,147 +0,0 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: node.go
|
||||
|
||||
// Package mock_k8s is a generated GoMock package.
|
||||
package mock_k8s
|
||||
|
||||
import (
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockNoder is a mock of Noder interface
|
||||
type MockNoder struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockNoderMockRecorder
|
||||
}
|
||||
|
||||
// MockNoderMockRecorder is the mock recorder for MockNoder
|
||||
type MockNoderMockRecorder struct {
|
||||
mock *MockNoder
|
||||
}
|
||||
|
||||
// NewMockNoder creates a new mock instance
|
||||
func NewMockNoder(ctrl *gomock.Controller) *MockNoder {
|
||||
mock := &MockNoder{ctrl: ctrl}
|
||||
mock.recorder = &MockNoderMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockNoder) EXPECT() *MockNoderMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Provision mocks base method
|
||||
func (m *MockNoder) Provision(meta map[string]string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Provision", meta)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Provision indicates an expected call of Provision
|
||||
func (mr *MockNoderMockRecorder) Provision(meta interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Provision", reflect.TypeOf((*MockNoder)(nil).Provision), meta)
|
||||
}
|
||||
|
||||
// GetConfig mocks base method
|
||||
func (m *MockNoder) GetConfig() (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetConfig")
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetConfig indicates an expected call of GetConfig
|
||||
func (mr *MockNoderMockRecorder) GetConfig() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfig", reflect.TypeOf((*MockNoder)(nil).GetConfig))
|
||||
}
|
||||
|
||||
// GetType mocks base method
|
||||
func (m *MockNoder) GetType() string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetType")
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetType indicates an expected call of GetType
|
||||
func (mr *MockNoderMockRecorder) GetType() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetType", reflect.TypeOf((*MockNoder)(nil).GetType))
|
||||
}
|
||||
|
||||
// GetName mocks base method
|
||||
func (m *MockNoder) GetName() string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetName")
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetName indicates an expected call of GetName
|
||||
func (mr *MockNoderMockRecorder) GetName() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockNoder)(nil).GetName))
|
||||
}
|
||||
|
||||
// GetUser mocks base method
|
||||
func (m *MockNoder) GetUser() string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetUser")
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetUser indicates an expected call of GetUser
|
||||
func (mr *MockNoderMockRecorder) GetUser() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUser", reflect.TypeOf((*MockNoder)(nil).GetUser))
|
||||
}
|
||||
|
||||
// GetToken mocks base method
|
||||
func (m *MockNoder) GetToken() (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetToken")
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetToken indicates an expected call of GetToken
|
||||
func (mr *MockNoderMockRecorder) GetToken() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetToken", reflect.TypeOf((*MockNoder)(nil).GetToken))
|
||||
}
|
||||
|
||||
// GetIP mocks base method
|
||||
func (m *MockNoder) GetIP() string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetIP")
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetIP indicates an expected call of GetIP
|
||||
func (mr *MockNoderMockRecorder) GetIP() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIP", reflect.TypeOf((*MockNoder)(nil).GetIP))
|
||||
}
|
||||
|
||||
// Dump mocks base method
|
||||
func (m *MockNoder) Dump() map[string]string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Dump")
|
||||
ret0, _ := ret[0].(map[string]string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Dump indicates an expected call of Dump
|
||||
func (mr *MockNoderMockRecorder) Dump() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Dump", reflect.TypeOf((*MockNoder)(nil).Dump))
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/metrue/fx/infra"
|
||||
"github.com/metrue/go-ssh-client"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
const NodeTypeMaster = "k3s_master"
|
||||
const NodeTypeAgent = "k3s_agent"
|
||||
const NodeTypeDocker = "docker_agent"
|
||||
|
||||
// Noder node interface
|
||||
type Noder interface {
|
||||
Provision(meta map[string]string) error
|
||||
GetConfig() (string, error)
|
||||
GetType() string
|
||||
GetName() string
|
||||
GetUser() string
|
||||
GetToken() (string, error)
|
||||
GetIP() string
|
||||
Dump() map[string]string
|
||||
}
|
||||
|
||||
// Node define a node
|
||||
type Node struct {
|
||||
IP string `json:"ip"`
|
||||
User string `json:"user"`
|
||||
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
|
||||
sshClient ssh.Clienter
|
||||
}
|
||||
|
||||
// CreateNode create a node
|
||||
func CreateNode(ip string, user string, typ string, name string) (*Node, error) {
|
||||
key, err := sshkey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
port := sshport()
|
||||
sshClient := ssh.New(ip).WithUser(user).WithKey(key).WithPort(port)
|
||||
|
||||
return &Node{
|
||||
IP: ip,
|
||||
User: user,
|
||||
Type: typ,
|
||||
Name: name,
|
||||
|
||||
sshClient: sshClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *Node) runCmd(script string) error {
|
||||
return n.sshClient.RunCommand(script, ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
})
|
||||
}
|
||||
|
||||
// Provision provision node
|
||||
func (n *Node) Provision(meta map[string]string) error {
|
||||
if err := n.runCmd(infra.Scripts["docker_version"].(string)); err != nil {
|
||||
if err := n.runCmd(infra.Scripts["install_docker"].(string)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := n.runCmd(infra.Scripts["start_dockerd"].(string)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if n.Type == NodeTypeMaster {
|
||||
if err := n.runCmd(infra.Scripts["check_k3s_server"].(string)); err != nil {
|
||||
cmd := infra.Scripts["setup_k3s_master"].(func(ip string) string)(n.IP)
|
||||
if err := n.runCmd(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if n.Type == NodeTypeAgent {
|
||||
if err := n.runCmd(infra.Scripts["check_k3s_agent"].(string)); err != nil {
|
||||
cmd := infra.Scripts["setup_k3s_agent"].(func(url string, tok string) string)(meta["url"], meta["token"])
|
||||
if err := n.runCmd(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetToken get token from master node
|
||||
func (n *Node) GetToken() (string, error) {
|
||||
if n.Type != NodeTypeMaster {
|
||||
return "", fmt.Errorf("could not get token from a non-master node")
|
||||
}
|
||||
var outPipe bytes.Buffer
|
||||
if err := n.sshClient.RunCommand(infra.Scripts["get_k3s_token"].(string), ssh.CommandOptions{Stdout: bufio.NewWriter(&outPipe)}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return outPipe.String(), nil
|
||||
}
|
||||
|
||||
// State get node state
|
||||
func (n *Node) State() {}
|
||||
|
||||
// Dump node information to json
|
||||
func (n *Node) Dump() map[string]string {
|
||||
return map[string]string{
|
||||
"ip": n.IP,
|
||||
"name": n.Name,
|
||||
"user": n.User,
|
||||
"type": n.Type,
|
||||
}
|
||||
}
|
||||
|
||||
// GetType get node type
|
||||
func (n *Node) GetType() string {
|
||||
return n.Type
|
||||
}
|
||||
|
||||
// GetName get node type
|
||||
func (n *Node) GetName() string {
|
||||
return n.Name
|
||||
}
|
||||
|
||||
// GetIP get node type
|
||||
func (n *Node) GetIP() string {
|
||||
return n.IP
|
||||
}
|
||||
|
||||
// GetUser get user
|
||||
func (n *Node) GetUser() string {
|
||||
return n.User
|
||||
}
|
||||
|
||||
// GetConfig get config
|
||||
func (n *Node) GetConfig() (string, error) {
|
||||
if n.Type == NodeTypeMaster {
|
||||
var outPipe bytes.Buffer
|
||||
if err := n.sshClient.RunCommand(infra.Scripts["get_k3s_kubeconfig"].(string), ssh.CommandOptions{
|
||||
Stdout: bufio.NewWriter(&outPipe),
|
||||
}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(rewriteKubeconfig(outPipe.String(), n.IP, "default")), nil
|
||||
} else if n.Type == NodeTypeDocker {
|
||||
data, err := json.Marshal(n.Dump())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
return "", fmt.Errorf("no config for node type of %s", n.Type)
|
||||
}
|
||||
|
||||
// NOTE only using for unit testing
|
||||
func (n *Node) setsshClient(client ssh.Clienter) {
|
||||
n.sshClient = client
|
||||
}
|
||||
|
||||
// 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) {
|
||||
path := os.Getenv("SSH_KEY_FILE")
|
||||
if path != "" {
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return absPath, nil
|
||||
}
|
||||
|
||||
key, err := homedir.Expand("~/.ssh/id_rsa")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func sshport() string {
|
||||
port := os.Getenv("SSH_PORT")
|
||||
if port != "" {
|
||||
return port
|
||||
}
|
||||
return "22"
|
||||
}
|
||||
|
||||
func rewriteKubeconfig(kubeconfig string, ip string, context string) []byte {
|
||||
if context == "" {
|
||||
// nolint
|
||||
context = "default"
|
||||
}
|
||||
|
||||
kubeconfigReplacer := strings.NewReplacer(
|
||||
"127.0.0.1", ip,
|
||||
"localhost", ip,
|
||||
"default", context,
|
||||
)
|
||||
|
||||
return []byte(kubeconfigReplacer.Replace(kubeconfig))
|
||||
}
|
||||
|
||||
var (
|
||||
_ Noder = &Node{}
|
||||
)
|
||||
@@ -1,211 +0,0 @@
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/metrue/fx/infra"
|
||||
"github.com/metrue/go-ssh-client"
|
||||
sshMocks "github.com/metrue/go-ssh-client/mocks"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
func TestGetSSHKeyFile(t *testing.T) {
|
||||
t.Run("defaut", func(t *testing.T) {
|
||||
defau, err := sshkey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defaultPath, _ := homedir.Expand("~/.ssh/id_rsa")
|
||||
if defau != defaultPath {
|
||||
t.Fatalf("should get %s but got %s", defaultPath, defau)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("override from env", func(t *testing.T) {
|
||||
os.Setenv("SSH_KEY_FILE", "/tmp/id_rsa")
|
||||
keyFile, err := sshkey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if keyFile != "/tmp/id_rsa" {
|
||||
t.Fatalf("should get %s but got %s", "/tmp/id_rsa", keyFile)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetSSHPort(t *testing.T) {
|
||||
t.Run("defaut", func(t *testing.T) {
|
||||
defau := sshport()
|
||||
if defau != "22" {
|
||||
t.Fatalf("should get %s but got %s", "22", defau)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("override from env", func(t *testing.T) {
|
||||
os.Setenv("SSH_PORT", "2222")
|
||||
defau := sshport()
|
||||
if defau != "2222" {
|
||||
t.Fatalf("should get %s but got %s", "2222", defau)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestNode(t *testing.T) {
|
||||
t.Run("master node already has docker and k3s server", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
n, err := CreateNode("127.0.0.1", "fx", NodeTypeMaster, "master")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if n.sshClient == nil {
|
||||
t.Fatal("ssh client should not be nil")
|
||||
}
|
||||
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n.setsshClient(sshClient)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["docker_version"].(string), ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["check_k3s_server"].(string), ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(nil)
|
||||
if err := n.Provision(map[string]string{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
t.Run("master node no docker and k3s server", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
n, err := CreateNode("127.0.0.1", "fx", NodeTypeMaster, "master")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if n.sshClient == nil {
|
||||
t.Fatal("ssh client should not be nil")
|
||||
}
|
||||
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n.setsshClient(sshClient)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["docker_version"].(string), ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(fmt.Errorf("no such command"))
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["install_docker"].(string), ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["start_dockerd"].(string), ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["check_k3s_server"].(string), ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(fmt.Errorf("no such progress"))
|
||||
|
||||
cmd := infra.Scripts["setup_k3s_master"].(func(ip string) string)(n.IP)
|
||||
sshClient.EXPECT().RunCommand(cmd, ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(nil)
|
||||
if err := n.Provision(map[string]string{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("agent node already has docker and k3s agent", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
n, err := CreateNode("127.0.0.1", "fx", NodeTypeAgent, "agent")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if n.sshClient == nil {
|
||||
t.Fatal("ssh client should not be nil")
|
||||
}
|
||||
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n.setsshClient(sshClient)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["docker_version"].(string), ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["check_k3s_agent"].(string), ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(nil)
|
||||
if err := n.Provision(map[string]string{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
t.Run("agent node no docker and k3s agent", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
n, err := CreateNode("127.0.0.1", "fx", NodeTypeAgent, "agent")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if n.sshClient == nil {
|
||||
t.Fatal("ssh client should not be nil")
|
||||
}
|
||||
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n.setsshClient(sshClient)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["docker_version"].(string), ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(fmt.Errorf("no such command"))
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["install_docker"].(string), ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["start_dockerd"].(string), ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(infra.Scripts["check_k3s_agent"].(string), ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(fmt.Errorf("no such progress"))
|
||||
|
||||
url := "url-1"
|
||||
token := "token-1"
|
||||
cmd := infra.Scripts["setup_k3s_agent"].(func(url string, ip string) string)(url, token)
|
||||
sshClient.EXPECT().RunCommand(cmd, ssh.CommandOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stdin: os.Stdin,
|
||||
Stderr: os.Stderr,
|
||||
}).Return(nil)
|
||||
if err := n.Provision(map[string]string{"url": url, "token": token}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,313 +0,0 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: infra.go
|
||||
|
||||
// Package mock_infra is a generated GoMock package.
|
||||
package mock_infra
|
||||
|
||||
import (
|
||||
context "context"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
types "github.com/metrue/fx/types"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockProvisioner is a mock of Provisioner interface
|
||||
type MockProvisioner struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockProvisionerMockRecorder
|
||||
}
|
||||
|
||||
// MockProvisionerMockRecorder is the mock recorder for MockProvisioner
|
||||
type MockProvisionerMockRecorder struct {
|
||||
mock *MockProvisioner
|
||||
}
|
||||
|
||||
// NewMockProvisioner creates a new mock instance
|
||||
func NewMockProvisioner(ctrl *gomock.Controller) *MockProvisioner {
|
||||
mock := &MockProvisioner{ctrl: ctrl}
|
||||
mock.recorder = &MockProvisionerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockProvisioner) EXPECT() *MockProvisionerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Provision mocks base method
|
||||
func (m *MockProvisioner) Provision() ([]byte, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Provision")
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Provision indicates an expected call of Provision
|
||||
func (mr *MockProvisionerMockRecorder) Provision() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Provision", reflect.TypeOf((*MockProvisioner)(nil).Provision))
|
||||
}
|
||||
|
||||
// HealthCheck mocks base method
|
||||
func (m *MockProvisioner) HealthCheck() (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "HealthCheck")
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// HealthCheck indicates an expected call of HealthCheck
|
||||
func (mr *MockProvisionerMockRecorder) HealthCheck() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockProvisioner)(nil).HealthCheck))
|
||||
}
|
||||
|
||||
// MockDeployer is a mock of Deployer interface
|
||||
type MockDeployer struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockDeployerMockRecorder
|
||||
}
|
||||
|
||||
// MockDeployerMockRecorder is the mock recorder for MockDeployer
|
||||
type MockDeployerMockRecorder struct {
|
||||
mock *MockDeployer
|
||||
}
|
||||
|
||||
// NewMockDeployer creates a new mock instance
|
||||
func NewMockDeployer(ctrl *gomock.Controller) *MockDeployer {
|
||||
mock := &MockDeployer{ctrl: ctrl}
|
||||
mock.recorder = &MockDeployerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockDeployer) EXPECT() *MockDeployerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Deploy mocks base method
|
||||
func (m *MockDeployer) Deploy(ctx context.Context, fn, name, image string, bindings []types.PortBinding) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Deploy", ctx, fn, name, image, bindings)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Deploy indicates an expected call of Deploy
|
||||
func (mr *MockDeployerMockRecorder) Deploy(ctx, fn, name, image, bindings interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deploy", reflect.TypeOf((*MockDeployer)(nil).Deploy), ctx, fn, name, image, bindings)
|
||||
}
|
||||
|
||||
// Destroy mocks base method
|
||||
func (m *MockDeployer) Destroy(ctx context.Context, name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Destroy", ctx, name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Destroy indicates an expected call of Destroy
|
||||
func (mr *MockDeployerMockRecorder) Destroy(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Destroy", reflect.TypeOf((*MockDeployer)(nil).Destroy), ctx, name)
|
||||
}
|
||||
|
||||
// Update mocks base method
|
||||
func (m *MockDeployer) Update(ctx context.Context, name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Update", ctx, name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Update indicates an expected call of Update
|
||||
func (mr *MockDeployerMockRecorder) Update(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockDeployer)(nil).Update), ctx, name)
|
||||
}
|
||||
|
||||
// GetStatus mocks base method
|
||||
func (m *MockDeployer) GetStatus(ctx context.Context, name string) (types.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetStatus", ctx, name)
|
||||
ret0, _ := ret[0].(types.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetStatus indicates an expected call of GetStatus
|
||||
func (mr *MockDeployerMockRecorder) GetStatus(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatus", reflect.TypeOf((*MockDeployer)(nil).GetStatus), ctx, name)
|
||||
}
|
||||
|
||||
// List mocks base method
|
||||
func (m *MockDeployer) List(ctx context.Context, name string) ([]types.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "List", ctx, name)
|
||||
ret0, _ := ret[0].([]types.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// List indicates an expected call of List
|
||||
func (mr *MockDeployerMockRecorder) List(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockDeployer)(nil).List), ctx, name)
|
||||
}
|
||||
|
||||
// Ping mocks base method
|
||||
func (m *MockDeployer) Ping(ctx context.Context) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Ping", ctx)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Ping indicates an expected call of Ping
|
||||
func (mr *MockDeployerMockRecorder) Ping(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockDeployer)(nil).Ping), ctx)
|
||||
}
|
||||
|
||||
// MockInfra is a mock of Infra interface
|
||||
type MockInfra struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockInfraMockRecorder
|
||||
}
|
||||
|
||||
// MockInfraMockRecorder is the mock recorder for MockInfra
|
||||
type MockInfraMockRecorder struct {
|
||||
mock *MockInfra
|
||||
}
|
||||
|
||||
// NewMockInfra creates a new mock instance
|
||||
func NewMockInfra(ctrl *gomock.Controller) *MockInfra {
|
||||
mock := &MockInfra{ctrl: ctrl}
|
||||
mock.recorder = &MockInfraMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockInfra) EXPECT() *MockInfraMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Provision mocks base method
|
||||
func (m *MockInfra) Provision() ([]byte, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Provision")
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Provision indicates an expected call of Provision
|
||||
func (mr *MockInfraMockRecorder) Provision() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Provision", reflect.TypeOf((*MockInfra)(nil).Provision))
|
||||
}
|
||||
|
||||
// HealthCheck mocks base method
|
||||
func (m *MockInfra) HealthCheck() (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "HealthCheck")
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// HealthCheck indicates an expected call of HealthCheck
|
||||
func (mr *MockInfraMockRecorder) HealthCheck() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockInfra)(nil).HealthCheck))
|
||||
}
|
||||
|
||||
// Deploy mocks base method
|
||||
func (m *MockInfra) Deploy(ctx context.Context, fn, name, image string, bindings []types.PortBinding) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Deploy", ctx, fn, name, image, bindings)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Deploy indicates an expected call of Deploy
|
||||
func (mr *MockInfraMockRecorder) Deploy(ctx, fn, name, image, bindings interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deploy", reflect.TypeOf((*MockInfra)(nil).Deploy), ctx, fn, name, image, bindings)
|
||||
}
|
||||
|
||||
// Destroy mocks base method
|
||||
func (m *MockInfra) Destroy(ctx context.Context, name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Destroy", ctx, name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Destroy indicates an expected call of Destroy
|
||||
func (mr *MockInfraMockRecorder) Destroy(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Destroy", reflect.TypeOf((*MockInfra)(nil).Destroy), ctx, name)
|
||||
}
|
||||
|
||||
// Update mocks base method
|
||||
func (m *MockInfra) Update(ctx context.Context, name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Update", ctx, name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Update indicates an expected call of Update
|
||||
func (mr *MockInfraMockRecorder) Update(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockInfra)(nil).Update), ctx, name)
|
||||
}
|
||||
|
||||
// GetStatus mocks base method
|
||||
func (m *MockInfra) GetStatus(ctx context.Context, name string) (types.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetStatus", ctx, name)
|
||||
ret0, _ := ret[0].(types.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetStatus indicates an expected call of GetStatus
|
||||
func (mr *MockInfraMockRecorder) GetStatus(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatus", reflect.TypeOf((*MockInfra)(nil).GetStatus), ctx, name)
|
||||
}
|
||||
|
||||
// List mocks base method
|
||||
func (m *MockInfra) List(ctx context.Context, name string) ([]types.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "List", ctx, name)
|
||||
ret0, _ := ret[0].([]types.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// List indicates an expected call of List
|
||||
func (mr *MockInfraMockRecorder) List(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockInfra)(nil).List), ctx, name)
|
||||
}
|
||||
|
||||
// Ping mocks base method
|
||||
func (m *MockInfra) Ping(ctx context.Context) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Ping", ctx)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Ping indicates an expected call of Ping
|
||||
func (mr *MockInfraMockRecorder) Ping(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockInfra)(nil).Ping), ctx)
|
||||
}
|
||||
@@ -6,12 +6,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/metrue/fx/bundle"
|
||||
containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||
"github.com/metrue/fx/constants"
|
||||
dockerHTTP "github.com/metrue/fx/container_runtimes/docker/http"
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/hook"
|
||||
"github.com/metrue/fx/packer"
|
||||
"github.com/metrue/fx/pkg/spinner"
|
||||
"github.com/metrue/fx/types"
|
||||
"github.com/metrue/fx/utils"
|
||||
)
|
||||
|
||||
@@ -42,6 +42,9 @@ func Build(ctx context.Contexter) (err error) {
|
||||
fn := ctx.Get("fn").(string)
|
||||
deps := ctx.Get("deps").([]string)
|
||||
language := ctx.Get("language").(string)
|
||||
host := ctx.Get("host").(string)
|
||||
kubeconf := ctx.Get("kubeconf").(string)
|
||||
name := ctx.Get("name").(string)
|
||||
|
||||
if err := bundle.Bundle(workdir, language, fn, deps...); err != nil {
|
||||
return err
|
||||
@@ -51,16 +54,12 @@ func Build(ctx context.Contexter) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
cloudType := ctx.Get("cloud_type").(string)
|
||||
name := ctx.Get("name").(string)
|
||||
if cloudType == types.CloudTypeK8S {
|
||||
data, err := packer.PackIntoK8SConfigMapFile(workdir)
|
||||
if host != "" {
|
||||
// TODO port should be configurable
|
||||
docker, err := dockerHTTP.Create(host, constants.AgentPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Set("data", data)
|
||||
} else {
|
||||
docker := ctx.Get("docker").(containerruntimes.ContainerRuntime)
|
||||
if err := docker.BuildImage(ctx.GetContext(), workdir, name); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -68,9 +67,16 @@ func Build(ctx context.Contexter) (err error) {
|
||||
if err := docker.TagImage(ctx.GetContext(), name, nameWithTag); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.Set("image", nameWithTag)
|
||||
}
|
||||
|
||||
if kubeconf != "" {
|
||||
data, err := packer.PackIntoK8SConfigMapFile(workdir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Set("data", data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
54
middlewares/driver.go
Normal file
54
middlewares/driver.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/metrue/fx/constants"
|
||||
dockerHTTP "github.com/metrue/fx/container_runtimes/docker/http"
|
||||
"github.com/metrue/fx/context"
|
||||
dockerDriver "github.com/metrue/fx/driver/docker"
|
||||
k8sInfra "github.com/metrue/fx/driver/k8s"
|
||||
"github.com/metrue/fx/provisioners"
|
||||
"github.com/metrue/go-ssh-client"
|
||||
)
|
||||
|
||||
// Driver initialize infrastructure driver
|
||||
func Driver(ctx context.Contexter) (err error) {
|
||||
host := ctx.Get("host").(string)
|
||||
sshClient := ctx.Get("ssh").(ssh.Clienter)
|
||||
kubeconf := ctx.Get("kubeconf").(string)
|
||||
if host != "" {
|
||||
// TODO port should be configurable
|
||||
docker := dockerHTTP.New(host, constants.AgentPort)
|
||||
driver := dockerDriver.New(dockerDriver.Options{
|
||||
DockerClient: docker,
|
||||
})
|
||||
|
||||
if err := driver.Ping(ctx.GetContext()); err != nil {
|
||||
log.Infof("provisioning %s ...", host)
|
||||
|
||||
provisioner := provisioners.New(sshClient)
|
||||
isRemote := (host != "127.0.0.1" && host != "localhost")
|
||||
if err := provisioner.Provision(ctx.GetContext(), isRemote); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
if err := docker.Initialize(); err != nil {
|
||||
return fmt.Errorf("initialize docker client failed: %s", err)
|
||||
}
|
||||
ctx.Set("docker_driver", driver)
|
||||
}
|
||||
|
||||
if kubeconf != "" {
|
||||
driver, err := k8sInfra.CreateDeployer(kubeconf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Set("k8s_driver", driver)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
72
middlewares/driver_test.go
Normal file
72
middlewares/driver_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
mockCtx "github.com/metrue/fx/context/mocks"
|
||||
"github.com/metrue/go-ssh-client"
|
||||
sshMocks "github.com/metrue/go-ssh-client/mocks"
|
||||
)
|
||||
|
||||
func TestDriver(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := mockCtx.NewMockContexter(ctrl)
|
||||
|
||||
kubeconf, err := ioutil.TempFile("", "*.kubeconf")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
config := `apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: DATA+OMITTED
|
||||
server: https://kubernetes.docker.internal:6443
|
||||
name: docker-desktop
|
||||
contexts:
|
||||
- context:
|
||||
cluster: docker-desktop
|
||||
user: docker-desktop
|
||||
name: docker-desktop
|
||||
- context:
|
||||
cluster: docker-desktop
|
||||
user: docker-desktop
|
||||
name: docker-for-desktop
|
||||
current-context: docker-desktop
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: docker-desktop
|
||||
user:
|
||||
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM5RENDQWR5Z0F3SUJBZ0lJZmd6Rml2L0lKVzR3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T1RFeU1UWXdPREU0TWpkYUZ3MHlNVEF6TURrd016RTNNamRhTURZeApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sc3dHUVlEVlFRREV4SmtiMk5yWlhJdFptOXlMV1JsCmMydDBiM0F3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzdtQk9tdHArMWU3TEsKYzNnWDVLcHU2aWRvSTd2V3lmeXRpdGxXU3d3cHc0d1pmN0d6QnhncTdXS1l5WERBSjVNR1JIeElkekVQMHcyUQo2WjlpV2d4Yk9YS2NMRCtvc1dPSlR2azB2NzBGUmg5QUNTWmNYTTQrakxsUG1VNXNZY0xBNi9RK24yQitxc3o3Ckhwb0FzZlpieVFmV0MvTG9uUmY5QVVONmVLcjFuZzVSeGdqMDFQNnN2bHBjUy9BTjhLNjZTcFJkczVocGVUU2IKMUYwUXlERFhXT0w0QTNlZUhPZGIzaC9tT3dtOEdkb1dSbGhJQjV5enlKVzF0Ny9pNVVmZm9BLzFZT0pOc2pEUgo2ZElYUTF4djJWQ0IzZnNZc0Z3NWFqb2s0aDNYSlB4N04yUWxxeWlPeXBXVjYzdDc1QmtHR083SHUxWlhUa2RRCndjc0dnN3ZUQWdNQkFBR2pKekFsTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFZbEE0OXdwY0p5eUExbmlWK25UMlI2bTBURXNjMkdYTgpad0RXZFVrRWgwRHN2dGp2NUhqU1BCMmZiOUZ2V3VpcTI3YU1aTmVDdEt4WGlNcnpTUmh2YmNqS3pFQ0M2VVBDCkJQdUw3NysrZk42Rlh5eG9haXExbjRVcGhSNDl0azI4eEJORm1DTWdyKzFLOWExTDgrQ2RtaXNFRHAwKzVPQmgKYWhIUytSYk5pUmhBQ1YrazJSZ01tK0swemNvUm41c0pGS012SitCZWhuTEdUN0VLVjRFMnpOZkZiQUI0b0k3eAo0NmxsSVBKKzN1Uy9oUlEzNkR5bzZ0OUc3K204NXBKTmFFdkFJdmVxaXJ5VlRJbjF5T1BURXVFREVTM1FZaWxYCmJOZFFBNUNXcWxKRnhhZkNlNzMwNE5sbVVhTW0xZTROVHB2cmQyT2FzUDRKT1JPY2dJK1dVUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdTVnVHByYWZ0WHV5eW5ONEYrU3FidW9uYUNPNzFzbjhyWXJaVmtzTUtjT01HWCt4CnN3Y1lLdTFpbU1sd3dDZVRCa1I4U0hjeEQ5TU5rT21mWWxvTVd6bHluQ3cvcUxGamlVNzVOTCs5QlVZZlFBa20KWEZ6T1BveTVUNWxPYkdIQ3dPdjBQcDlnZnFyTSt4NmFBTEgyVzhrSDFndnk2SjBYL1FGRGVuaXE5WjRPVWNZSQo5TlQrckw1YVhFdndEZkN1dWtxVVhiT1lhWGswbTlSZEVNZ3cxMWppK0FOM25oem5XOTRmNWpzSnZCbmFGa1pZClNBZWNzOGlWdGJlLzR1VkgzNkFQOVdEaVRiSXcwZW5TRjBOY2I5bFFnZDM3R0xCY09XbzZKT0lkMXlUOGV6ZGsKSmFzb2pzcVZsZXQ3ZStRWkJoanV4N3RXVjA1SFVNSExCb083MHdJREFRQUJBb0lCQVFDZHV3REs3RUxkRldULwpWSmRsZjU3T0k1Tit2SXp6ekdIb2lSYTB0K1ZDT0dsVUIwb2lmWlNVZzRTamNyeWExS3VLV1lzbVl4R2RmSmVyCmdNUENyblExUDloZDk5YU93Smd3bTNadUk4bUs1YXJnN05DVVdIUVJvOEVzYkhyRUptN2FSNHJXSEt2RjFWY0UKem5ZdW4zUEZPUUtkdHU1SEo4OURyQXhRcmFVUlhxRE5OUUtKTGYydFNIWEJqNUtDam5jSXRBRXBwM2V3eXo3WQpnVmtlU0NrY3BRL0lZanJ0QS83c3dGR3IydWIrek9Jd1dKc3c4V01WcGZrbHZDVmR4VEk5YkdBQVRYNWdXMzZGCjhDSGRkUzd4UU5obEprbjNUVUVjeFU5UUw2TWEwNWdxWkcxVXBGaHpKQ0gybHlmQWhqQkZSbFkvcEN4YmJ1QXYKOFFGUjUyb0JBb0dCQU1HSzQ5bGFWUGhiak5Dc3J5N2dxUWVaYWZWbWQ1V2pMUzVrRFpYb0lQZkgxZWFVS253Ywp0cjd2SjJrNHZQRVR4eGdzOUtDMk5HSUFiUUIySlBFK3daYVo1OTk3NFdiUTNIaXVwWTBxMit3aFNseWpyWkxPCjI2MyswUzBLUVFDYUk1SkNseUJxTjFUOUdTeEMwYzlPTE9YMnpMbkMzUEthRHpZeHRyWlZGNDc5QW9HQkFQZ2gKdzhrSWxXYUdZd2RvMHNaeHowTzJoL2lmQ1U5ZmFGaTFpNjBrN2tBR2hxVVB4TCtyN0F5TDc4N2FlcnoxKzN5bApqVzB4YnFPZi9VTFZlaFBNWU40b0pFZkJpWmtCNnZ2Ri9HMllKeWg0MnJvQ2VDUmNLRVBqSjBST3FyQVI3Nks1CkZuMitWL2Nqc29Dd2ZsTjI4VC9LcUl4bjU3TWd1Wll1Ykt4Uk1qY1BBb0dBVWdvUnN4eDdVQnRlZ1VYeHJDbEcKL1JXbXVJTUt4Yjg1YzZTdHJaR01CL3dKUzRnYXlpbFJ2WFdhZXh1MTIyckt4aENvVVVkcXhPL3hSSFRRREFMUwpCSWlRcFViWnNMOXY5U2Z5dlBnaDZPSGpwNGtxRmtUaEVjd2wxclcyQUE5V2JMVVZZb1FqbUQ4QTRLWWlVWUdOCnZwenpBdnI2dFV0Z2oxUmJZc2FIQ2ZFQ2dZRUE3eDd5NDZoKythZW1oWHh5SzBXQVhSdnBteUlBUWRxSzMzcE4KR2RYT09DdFIxSDRHdUVRQkhmSTVieG5EVUppbysrMDdCckN0azhmWnRHK3Z6cWFWNzJHMTNPVFpLbmZic1RpUwpWRGRkL1RYQ2E2RjNrR3F6YndEWVZZNk9GVkdqb3loRlVYWitwUzlrbFhvQXM0U2JaME53L0tZaGR0R2hwK1lqCldraUJZT2NDZ1lCVmZDZGpCWDJvUmZIWkNJWm9NRVpuckNOS04rWWp1dHBnQTg5V1BEMmhPNlJLTG1DeG5GZ0oKbnFBUnUrZDBzMkhzaUErUzBKRXVMTVcxRjdnaml3Zm1zc3lSWkttWlNWaTZJZnFKUlFnSktDR3VzM1lPc2RHZQovSm5hTUlaRG1DRzNyNXpwZVl4VEowM0NIR2pReDl4dDRrallFU1F5MHdlVUtwelRITEhmWEE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=`
|
||||
if err := ioutil.WriteFile(kubeconf.Name(), []byte(config), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer os.Remove(kubeconf.Name())
|
||||
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
sshClient.EXPECT().Connectable(10*time.Second).Return(true, nil).Times(2)
|
||||
sshClient.EXPECT().RunCommand("docker version", ssh.CommandOptions{
|
||||
Timeout: 10 * time.Second,
|
||||
}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand("docker inspect fx-agent", ssh.CommandOptions{
|
||||
Timeout: 10 * time.Second,
|
||||
}).Return(nil)
|
||||
|
||||
cntx := context.Background()
|
||||
ctx.EXPECT().GetContext().Return(cntx).Times(2)
|
||||
ctx.EXPECT().Get("ssh").Return(sshClient)
|
||||
ctx.EXPECT().Get("host").Return("1.2.3.4")
|
||||
ctx.EXPECT().Get("kubeconf").Return(kubeconf.Name())
|
||||
// TODO mock http call
|
||||
if err := Driver(ctx); err == nil {
|
||||
t.Fatal("should failed on initial docker client dude to /version api not ready")
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/metrue/fx/config"
|
||||
"github.com/metrue/fx/context"
|
||||
)
|
||||
|
||||
// LoadConfig load default config
|
||||
func LoadConfig(ctx context.Contexter) error {
|
||||
config, err := config.LoadDefault()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Set("config", config)
|
||||
return nil
|
||||
}
|
||||
@@ -2,12 +2,49 @@ package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/utils"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type argsField struct {
|
||||
Type string
|
||||
Name string
|
||||
Env string
|
||||
}
|
||||
|
||||
func set(ctx context.Contexter, cli *cli.Context, fields []argsField) error {
|
||||
for _, f := range fields {
|
||||
if f.Type == "string" {
|
||||
if f.Name == "host" {
|
||||
addr := strings.Split(cli.String(f.Name), "@")
|
||||
if len(addr) != 2 {
|
||||
return fmt.Errorf("invalid host information, should be format of <user>@<ip>")
|
||||
}
|
||||
user := addr[0]
|
||||
ip := addr[1]
|
||||
ctx.Set("host", ip)
|
||||
ctx.Set("user", user)
|
||||
} else {
|
||||
|
||||
ctx.Set(f.Name, cli.String(f.Name))
|
||||
}
|
||||
} else if f.Type == "int" {
|
||||
ctx.Set(f.Name, cli.Int(f.Name))
|
||||
} else if f.Type == "bool" {
|
||||
ctx.Set(f.Name, cli.Bool(f.Name))
|
||||
}
|
||||
|
||||
if f.Env != "" && os.Getenv(f.Env) != "" {
|
||||
ctx.Set(f.Name, os.Getenv(f.Env))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse parse input
|
||||
func Parse(action string) func(ctx context.Contexter) (err error) {
|
||||
return func(ctx context.Contexter) error {
|
||||
@@ -31,12 +68,18 @@ func Parse(action string) func(ctx context.Contexter) (err error) {
|
||||
}
|
||||
ctx.Set("deps", deps)
|
||||
|
||||
name := cli.String("name")
|
||||
ctx.Set("name", name)
|
||||
port := cli.Int("port")
|
||||
ctx.Set("port", port)
|
||||
force := cli.Bool("force")
|
||||
ctx.Set("force", force)
|
||||
if err := set(ctx, cli, []argsField{
|
||||
argsField{Name: "name", Type: "string"},
|
||||
argsField{Name: "port", Type: "int"},
|
||||
argsField{Name: "force", Type: "bool"},
|
||||
argsField{Name: "ssh_port", Type: "string", Env: "SSH_PORT"},
|
||||
argsField{Name: "ssh_key", Type: "string", Env: "SSH_KEY_FILE"},
|
||||
argsField{Name: "host", Type: "string", Env: "FX_HOST"},
|
||||
argsField{Name: "kubeconf", Type: "string", Env: "FX_KUBECONF"},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case "down":
|
||||
services := cli.Args()
|
||||
if len(services) == 0 {
|
||||
@@ -47,11 +90,30 @@ func Parse(action string) func(ctx context.Contexter) (err error) {
|
||||
svc = append(svc, service)
|
||||
}
|
||||
ctx.Set("services", svc)
|
||||
|
||||
if err := set(ctx, cli, []argsField{
|
||||
argsField{Name: "ssh_port", Type: "string", Env: "SSH_PORT"},
|
||||
argsField{Name: "ssh_key", Type: "string", Env: "SSH_KEY_FILE"},
|
||||
argsField{Name: "host", Type: "string", Env: "FX_HOST"},
|
||||
argsField{Name: "kubeconf", Type: "string", Env: "FX_KUBECONF"},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case "list":
|
||||
name := cli.Args().First()
|
||||
ctx.Set("filter", name)
|
||||
format := cli.String("format")
|
||||
ctx.Set("format", format)
|
||||
if err := set(ctx, cli, []argsField{
|
||||
argsField{Name: "ssh_port", Type: "string", Env: "SSH_PORT"},
|
||||
argsField{Name: "ssh_key", Type: "string", Env: "SSH_KEY_FILE"},
|
||||
argsField{Name: "host", Type: "string", Env: "FX_HOST"},
|
||||
argsField{Name: "kubeconf", Type: "string", Env: "FX_KUBECONF"},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case "image_build":
|
||||
if !cli.Args().Present() {
|
||||
return fmt.Errorf("no function given")
|
||||
@@ -69,12 +131,16 @@ func Parse(action string) func(ctx context.Contexter) (err error) {
|
||||
}
|
||||
}
|
||||
ctx.Set("deps", deps)
|
||||
|
||||
tag := cli.String("tag")
|
||||
if tag == "" {
|
||||
tag = uuid.New().String()
|
||||
if err := set(ctx, cli, []argsField{
|
||||
argsField{Name: "tag", Type: "string"},
|
||||
argsField{Name: "ssh_port", Type: "string", Env: "SSH_PORT"},
|
||||
argsField{Name: "ssh_key", Type: "string", Env: "SSH_KEY_FILE"},
|
||||
argsField{Name: "host", Type: "string", Env: "FX_HOST"},
|
||||
argsField{Name: "kubeconf", Type: "string", Env: "FX_KUBECONF"},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Set("tag", tag)
|
||||
|
||||
case "image_export":
|
||||
if !cli.Args().Present() {
|
||||
return fmt.Errorf("no function given")
|
||||
|
||||
@@ -12,8 +12,20 @@ import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
t.Run("source code not existed", func(t *testing.T) {
|
||||
type stringValue string
|
||||
|
||||
func (s stringValue) Set(v string) error {
|
||||
// nolint
|
||||
s = stringValue(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s stringValue) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func TestParseUp(t *testing.T) {
|
||||
t.Run("SourceCodeNotReady", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
@@ -26,12 +38,15 @@ func TestParse(t *testing.T) {
|
||||
t.Fatal("should got file or directory not existed error")
|
||||
}
|
||||
})
|
||||
t.Run("source code ready", func(t *testing.T) {
|
||||
t.Run("SourceCodeReady", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := mockCtx.NewMockContexter(ctrl)
|
||||
argset := flag.NewFlagSet("test", 0)
|
||||
host := "127.0.0.1"
|
||||
user := "root"
|
||||
argset.Var(stringValue(user+"@"+host), "host", "host info")
|
||||
cli := cli.NewContext(nil, argset, nil)
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
@@ -43,6 +58,11 @@ func TestParse(t *testing.T) {
|
||||
ctx.EXPECT().GetCliContext().Return(cli)
|
||||
ctx.EXPECT().Set("fn", fd.Name())
|
||||
ctx.EXPECT().Set("deps", []string{})
|
||||
ctx.EXPECT().Set("host", host)
|
||||
ctx.EXPECT().Set("user", user)
|
||||
ctx.EXPECT().Set("ssh_port", "")
|
||||
ctx.EXPECT().Set("ssh_key", "")
|
||||
ctx.EXPECT().Set("kubeconf", "")
|
||||
ctx.EXPECT().Set("name", "")
|
||||
ctx.EXPECT().Set("port", 0)
|
||||
ctx.EXPECT().Set("force", false)
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/metrue/fx/config"
|
||||
"github.com/metrue/fx/constants"
|
||||
dockerHTTP "github.com/metrue/fx/container_runtimes/docker/http"
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/fx/infra"
|
||||
dockerInfra "github.com/metrue/fx/infra/docker"
|
||||
k8sInfra "github.com/metrue/fx/infra/k8s"
|
||||
"github.com/metrue/fx/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Provision make sure infrastructure is healthy
|
||||
func Provision(ctx context.Contexter) (err error) {
|
||||
fxConfig := ctx.Get("config").(*config.Config)
|
||||
meta, err := fxConfig.GetCurrentCloud()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cloudType, err := fxConfig.GetCurrentCloudType()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.Set("cloud_type", cloudType)
|
||||
var cloud infra.Clouder
|
||||
switch cloudType {
|
||||
case types.CloudTypeK8S:
|
||||
cloud, err = k8sInfra.Load(meta)
|
||||
case types.CloudTypeDocker:
|
||||
cloud, err = dockerInfra.Load(meta)
|
||||
}
|
||||
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()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var deployer infra.Deployer
|
||||
if os.Getenv("KUBECONFIG") != "" {
|
||||
cloudType = types.CloudTypeK8S
|
||||
conf = os.Getenv("KUBECONFIG")
|
||||
ctx.Set("cloud_type", types.CloudTypeK8S)
|
||||
}
|
||||
|
||||
if cloudType == types.CloudTypeDocker {
|
||||
var meta map[string]string
|
||||
if err := json.Unmarshal([]byte(conf), &meta); err != nil {
|
||||
return err
|
||||
}
|
||||
docker, err := dockerHTTP.Create(meta["ip"], constants.AgentPort)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "please make sure docker is installed and running on your host")
|
||||
}
|
||||
|
||||
// TODO should clean up, but it needed in middlewares.Build
|
||||
ctx.Set("docker", docker)
|
||||
deployer, err = dockerInfra.CreateDeployer(docker)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if cloudType == types.CloudTypeK8S {
|
||||
deployer, err = k8sInfra.CreateDeployer(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("unsupport cloud type %s, please make sure you config is correct", cloud.GetType())
|
||||
}
|
||||
|
||||
ctx.Set("deployer", deployer)
|
||||
|
||||
return nil
|
||||
}
|
||||
17
middlewares/ssh.go
Normal file
17
middlewares/ssh.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/metrue/fx/context"
|
||||
"github.com/metrue/go-ssh-client"
|
||||
)
|
||||
|
||||
// SSH create a ssh client
|
||||
func SSH(ctx context.Contexter) error {
|
||||
host := ctx.Get("host").(string)
|
||||
user := ctx.Get("user").(string)
|
||||
port := ctx.Get("ssh_port").(string)
|
||||
keyfile := ctx.Get("ssh_key").(string)
|
||||
sshClient := ssh.New(host).WithUser(user).WithPort(port).WithKey(keyfile)
|
||||
ctx.Set("ssh", sshClient)
|
||||
return nil
|
||||
}
|
||||
90
provisioners/docker.go
Normal file
90
provisioners/docker.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package provisioners
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/metrue/go-ssh-client"
|
||||
)
|
||||
|
||||
const sshConnectionTimeout = 10 * time.Second
|
||||
|
||||
var scripts = map[string]interface{}{
|
||||
"docker_version": "docker version",
|
||||
"install_docker": "curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-18.06.3-ce.tgz -o docker.tgz && tar zxvf docker.tgz && mv docker/* /usr/bin && rm -rf docker docker.tgz",
|
||||
"start_dockerd": "dockerd >/dev/null 2>&1 & sleep 2",
|
||||
"check_fx_agent": "docker inspect fx-agent",
|
||||
"start_fx_agent": "docker run -d --name=fx-agent --rm -v /var/run/docker.sock:/var/run/docker.sock -p 0.0.0.0:8866:1234 bobrik/socat TCP-LISTEN:1234,fork UNIX-CONNECT:/var/run/docker.sock",
|
||||
}
|
||||
|
||||
// Docker define a fx docker host
|
||||
type Docker struct {
|
||||
sshClient ssh.Clienter
|
||||
}
|
||||
|
||||
// New a docker provioner
|
||||
func New(sshClient ssh.Clienter) *Docker {
|
||||
return &Docker{
|
||||
sshClient: sshClient,
|
||||
}
|
||||
}
|
||||
|
||||
// Provision a host
|
||||
func (d *Docker) Provision(ctx context.Context, isRemote bool) error {
|
||||
if err := d.runCmd(scripts["docker_version"].(string), isRemote); err != nil {
|
||||
if err := d.runCmd(scripts["install_docker"].(string), isRemote); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := d.runCmd(scripts["start_dockerd"].(string), isRemote); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := d.runCmd(scripts["check_fx_agent"].(string), isRemote); err != nil {
|
||||
if err := d.runCmd(scripts["start_fx_agent"].(string), isRemote); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Docker) runCmd(script string, isRemote bool, options ...ssh.CommandOptions) error {
|
||||
option := ssh.CommandOptions{
|
||||
Timeout: sshConnectionTimeout,
|
||||
}
|
||||
if len(options) >= 1 {
|
||||
option = options[0]
|
||||
}
|
||||
if !isRemote {
|
||||
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
|
||||
}
|
||||
ok, err := d.sshClient.Connectable(sshConnectionTimeout)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not connect via SSH: '%s'", err)
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("could not connect via SSH")
|
||||
}
|
||||
|
||||
return d.sshClient.RunCommand(script, option)
|
||||
}
|
||||
|
||||
var (
|
||||
_ Provisioner = &Docker{}
|
||||
)
|
||||
124
provisioners/docker_test.go
Normal file
124
provisioners/docker_test.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package provisioners
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/metrue/go-ssh-client"
|
||||
sshMocks "github.com/metrue/go-ssh-client/mocks"
|
||||
)
|
||||
|
||||
func TestDriverProvision(t *testing.T) {
|
||||
t.Run("SSHConnectError", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n := &Docker{sshClient: sshClient}
|
||||
err := errors.New("could not connect to host")
|
||||
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(false, err).AnyTimes()
|
||||
if err := n.Provision(context.Background(), true); err == nil {
|
||||
t.Fatalf("should get error when SSH connection not ok")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("SSHConnectionNotOK", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n := New(sshClient)
|
||||
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(false, nil).AnyTimes()
|
||||
if err := n.Provision(context.Background(), true); err == nil {
|
||||
t.Fatalf("should get error when SSH connection not ok")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DockerAndFxAgentOK", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n := New(sshClient)
|
||||
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(true, nil).AnyTimes()
|
||||
sshClient.EXPECT().RunCommand(scripts["docker_version"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(scripts["check_fx_agent"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
|
||||
if err := n.Provision(context.Background(), true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DockerNotReady", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n := New(sshClient)
|
||||
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(true, nil).AnyTimes()
|
||||
err := errors.New("docker command not found")
|
||||
sshClient.EXPECT().RunCommand(scripts["docker_version"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(err)
|
||||
sshClient.EXPECT().RunCommand(scripts["install_docker"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(scripts["start_dockerd"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(scripts["check_fx_agent"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
|
||||
if err := n.Provision(context.Background(), true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("FxAgentNotReady", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n := New(sshClient)
|
||||
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(true, nil).AnyTimes()
|
||||
err := errors.New("fx agent not found")
|
||||
sshClient.EXPECT().RunCommand(scripts["docker_version"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(scripts["check_fx_agent"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(err)
|
||||
sshClient.EXPECT().RunCommand(scripts["start_fx_agent"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
|
||||
if err := n.Provision(context.Background(), true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DockerAndFxAgentNotReady", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n := New(sshClient)
|
||||
|
||||
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(true, nil).AnyTimes()
|
||||
err2 := errors.New("fx agent not found")
|
||||
err1 := errors.New("docker command not found")
|
||||
sshClient.EXPECT().RunCommand(scripts["docker_version"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(err1)
|
||||
sshClient.EXPECT().RunCommand(scripts["install_docker"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(scripts["start_dockerd"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
|
||||
sshClient.EXPECT().RunCommand(scripts["check_fx_agent"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(err2)
|
||||
sshClient.EXPECT().RunCommand(scripts["start_fx_agent"].(string), ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
|
||||
if err := n.Provision(context.Background(), true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunCommand(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
sshClient := sshMocks.NewMockClienter(ctrl)
|
||||
n := &Docker{
|
||||
sshClient: sshClient,
|
||||
}
|
||||
script := "script"
|
||||
option := ssh.CommandOptions{
|
||||
Timeout: sshConnectionTimeout,
|
||||
}
|
||||
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(true, nil)
|
||||
sshClient.EXPECT().RunCommand(script, option).Return(nil)
|
||||
if err := n.runCmd(script, true, option); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
8
provisioners/provisioner.go
Normal file
8
provisioners/provisioner.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package provisioners
|
||||
|
||||
import "context"
|
||||
|
||||
// Provisioner define provisioner interface
|
||||
type Provisioner interface {
|
||||
Provision(ctx context.Context, isRemote bool) error
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
echo "mode: atomic\n" > coverage.txt
|
||||
|
||||
for d in `go list ./... | grep -v 'mocks\|images\|examples\|assets'`; do
|
||||
for d in `go list ./... | grep -v 'mocks\|images\|examples\|assets\|packrd'`; do
|
||||
echo $d
|
||||
go test -race -coverprofile=profile.out -covermode=atomic $d
|
||||
if [ $? -ne 0 ];then
|
||||
|
||||
Reference in New Issue
Block a user