Compare commits
26 Commits
0.8.2-alph
...
0.8.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35262de828 | ||
|
|
34a495984c | ||
|
|
d7130c4e28 | ||
|
|
c9630a53c3 | ||
|
|
0522690472 | ||
|
|
a8a0fbed32 | ||
|
|
26ae9585f6 | ||
|
|
b69bd699c8 | ||
|
|
650ee5f63a | ||
|
|
e3c60cbb77 | ||
|
|
0daca43d10 | ||
|
|
d3c239dc54 | ||
|
|
05ac2441da | ||
|
|
c0009b1b64 | ||
|
|
82960824ef | ||
|
|
64b63cbd0f | ||
|
|
05771fb07f | ||
|
|
d1f680dacd | ||
|
|
14c9397b70 | ||
|
|
eb5e724899 | ||
|
|
80619bd800 | ||
|
|
8e2cdfc607 | ||
|
|
58f416b7b2 | ||
|
|
b6cf39e3e5 | ||
|
|
41bc98ab64 | ||
|
|
b007ac315a |
46
.github/workflows/ci.yml
vendored
46
.github/workflows/ci.yml
vendored
@@ -13,36 +13,22 @@ jobs:
|
|||||||
- name: check out
|
- name: check out
|
||||||
uses: actions/checkout@master
|
uses: actions/checkout@master
|
||||||
|
|
||||||
- name: setup docker
|
- name: kind create a k8s cluster
|
||||||
run: |
|
run: |
|
||||||
./scripts/provision.sh
|
kind create cluster
|
||||||
|
|
||||||
- name: lint
|
- name: lint
|
||||||
run: |
|
run: |
|
||||||
docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint \
|
docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint \
|
||||||
golangci-lint run -v
|
golangci-lint run -v
|
||||||
|
|
||||||
- name: setup k8s and kind
|
|
||||||
run: |
|
|
||||||
export GOBIN=$(go env GOPATH)/bin
|
|
||||||
export PATH=$PATH:$GOBIN
|
|
||||||
mkdir -p $GOBIN
|
|
||||||
curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
|
|
||||||
chmod +x kubectl && mv kubectl $GOBIN
|
|
||||||
wget https://github.com/kubernetes-sigs/kind/releases/download/v0.5.0/kind-linux-amd64 && chmod +x kind-linux-amd64 && mv kind-linux-amd64 $GOBIN/kind
|
|
||||||
./scripts/setup_kind.sh
|
|
||||||
|
|
||||||
- name: unit test
|
- name: unit test
|
||||||
env:
|
env:
|
||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||||
run: |
|
|
||||||
export KUBECONFIG=/home/runner/.kube/kind-config-fx-test
|
|
||||||
DEBUG=true go test -v ./...
|
|
||||||
- name: code cov
|
|
||||||
env:
|
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
|
export KUBECONFIG="$(kind get kubeconfig-path)"
|
||||||
./scripts/coverage.sh
|
./scripts/coverage.sh
|
||||||
bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN}
|
bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN}
|
||||||
|
|
||||||
@@ -57,6 +43,11 @@ jobs:
|
|||||||
make docker-build
|
make docker-build
|
||||||
make test
|
make test
|
||||||
# make docker-publish #TODO in release workflow
|
# 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
|
- name: test fx cli
|
||||||
env:
|
env:
|
||||||
@@ -66,21 +57,22 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo $KUBECONFIG
|
echo $KUBECONFIG
|
||||||
unset KUBECONFIG
|
unset KUBECONFIG
|
||||||
make cli-test
|
make cli-test-ci
|
||||||
|
|
||||||
- name: test AKS
|
- name: test AKS
|
||||||
env:
|
env:
|
||||||
AKS_KUBECONFIG: ${{ secrets.AKS_KUBECONFIG }}
|
AKS_KUBECONFIG: ${{ secrets.AKS_KUBECONFIG }}
|
||||||
run: |
|
run: |
|
||||||
export KUBECONFIG=${HOME}/.kube/aks
|
echo "skip since aks environment not ready yet"
|
||||||
echo ${AKS_KUBECONFIG} | base64 -d > $KUBECONFIG
|
# export KUBECONFIG=${HOME}/.kube/aks
|
||||||
if [[ -z "$AKS_KUBECONFIG" ]];then
|
# echo ${AKS_KUBECONFIG} | base64 -d > $KUBECONFIG
|
||||||
echo "skip deploy test since no valid KUBECONFIG"
|
# if [[ -z "$AKS_KUBECONFIG" ]];then
|
||||||
else
|
# echo "skip deploy test since no valid KUBECONFIG"
|
||||||
DEBUG=true ./build/fx up -n hello -p 12345 examples/functions/JavaScript/func.js
|
# else
|
||||||
./build/fx down hello
|
# DEBUG=true ./build/fx up -n hello -p 12345 examples/functions/JavaScript/func.js
|
||||||
rm ${KUBECONFIG}
|
# ./build/fx down hello
|
||||||
fi
|
# rm ${KUBECONFIG}
|
||||||
|
# fi
|
||||||
|
|
||||||
Installation:
|
Installation:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|||||||
29
.github/workflows/release.yml
vendored
29
.github/workflows/release.yml
vendored
@@ -18,32 +18,22 @@ jobs:
|
|||||||
- name: check out
|
- name: check out
|
||||||
uses: actions/checkout@master
|
uses: actions/checkout@master
|
||||||
|
|
||||||
- name: setup docker
|
- name: kind create a k8s cluster
|
||||||
run: |
|
run: |
|
||||||
./scripts/provision.sh
|
kind create cluster
|
||||||
|
|
||||||
- name: lint
|
- name: lint
|
||||||
run: |
|
run: |
|
||||||
docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint \
|
docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint \
|
||||||
golangci-lint run -v
|
golangci-lint run -v
|
||||||
|
|
||||||
- name: setup k8s and kind
|
|
||||||
run: |
|
|
||||||
export GOBIN=$(go env GOPATH)/bin
|
|
||||||
export PATH=$PATH:$GOBIN
|
|
||||||
mkdir -p $GOBIN
|
|
||||||
curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
|
|
||||||
chmod +x kubectl && mv kubectl $GOBIN
|
|
||||||
wget https://github.com/kubernetes-sigs/kind/releases/download/v0.5.0/kind-linux-amd64 && chmod +x kind-linux-amd64 && mv kind-linux-amd64 $GOBIN/kind
|
|
||||||
./scripts/setup_kind.sh
|
|
||||||
|
|
||||||
- name: unit test
|
- name: unit test
|
||||||
env:
|
env:
|
||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||||
run: |
|
run: |
|
||||||
export KUBECONFIG=/home/runner/.kube/kind-config-fx-test
|
export KUBECONFIG="$(kind get kubeconfig-path)"
|
||||||
DEBUG=true go test -v ./container_runtimes/... ./deploy/...
|
DEBUG=true go test -v ./...
|
||||||
|
|
||||||
- name: build fx
|
- name: build fx
|
||||||
run: |
|
run: |
|
||||||
@@ -61,11 +51,12 @@ jobs:
|
|||||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
run: |
|
run: |
|
||||||
export KUBECONFIG=${HOME}/.kube/aks
|
echo "skip since aks environment not ready yet"
|
||||||
echo ${AKS_KUBECONFIG} | base64 -d > $KUBECONFIG
|
# export KUBECONFIG=${HOME}/.kube/aks
|
||||||
DEBUG=true ./build/fx up -n hello -p 12345 examples/functions/JavaScript/func.js
|
# echo ${AKS_KUBECONFIG} | base64 -d > $KUBECONFIG
|
||||||
./build/fx down hello
|
# DEBUG=true ./build/fx up -n hello -p 12345 examples/functions/JavaScript/func.js
|
||||||
rm ${KUBECONFIG}
|
# ./build/fx down hello
|
||||||
|
# rm ${KUBECONFIG}
|
||||||
Release:
|
Release:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: [Test]
|
needs: [Test]
|
||||||
|
|||||||
39
Makefile
39
Makefile
@@ -1,5 +1,7 @@
|
|||||||
OUTPUT_DIR=./build
|
OUTPUT_DIR ?=./build
|
||||||
DIST_DIR=./dist
|
DIST_DIR ?=./dist
|
||||||
|
DOCKER_REMOTE_HOST_ADDR ?= "127.0.0.1"
|
||||||
|
DOCKER_REMOTE_HOST_USER ?= $(whoami)
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
golangci-lint run
|
golangci-lint run
|
||||||
@@ -26,12 +28,11 @@ clean:
|
|||||||
unit-test:
|
unit-test:
|
||||||
./scripts/coverage.sh
|
./scripts/coverage.sh
|
||||||
|
|
||||||
|
cli-test-ci:
|
||||||
|
./scripts/test_cli.sh 'js'
|
||||||
|
|
||||||
cli-test:
|
cli-test:
|
||||||
echo 'run testing on localhost'
|
./scripts/test_cli.sh 'js rb py go php java d'
|
||||||
./scripts/test_cli.sh
|
|
||||||
# TODO enable remote test
|
|
||||||
echo 'run testing on remote host'
|
|
||||||
DOCKER_REMOTE_HOST_ADDR=${REMOTE_HOST_ADDR} DOCKER_REMOTE_HOST_USER=${REMOTE_HOST_USER} DOCKER_REMOTE_HOST_PASSWORD=${REMOTE_HOST_PASSWORD} ./scripts/test_cli.sh
|
|
||||||
|
|
||||||
http-test:
|
http-test:
|
||||||
./scripts/http_test.sh
|
./scripts/http_test.sh
|
||||||
@@ -39,3 +40,27 @@ http-test:
|
|||||||
zip:
|
zip:
|
||||||
zip -r images.zip images/
|
zip -r images.zip images/
|
||||||
.PHONY: test build start list clean generate
|
.PHONY: test build start list clean generate
|
||||||
|
|
||||||
|
start_docker_infra:
|
||||||
|
docker build -t fx-docker-infra -f test/Dockerfile ./test
|
||||||
|
docker run --rm --name fx-docker-infra -p 2222:22 -v /var/run/docker.sock:/var/run/docker.sock -d fx-docker-infra
|
||||||
|
|
||||||
|
test_docker_infra:
|
||||||
|
CICD=true SSH_PORT=2222 SSH_KEY_FILE=./test/id_rsa ./build/fx infra create --name docker-local -t docker --host root@127.0.0.1
|
||||||
|
|
||||||
|
stop_docker_infra:
|
||||||
|
docker stop fx-docker-infra
|
||||||
|
|
||||||
|
start_k3s_infra:
|
||||||
|
multipass launch --name k3s-master --cpus 1 --mem 512M --disk 3G --cloud-init ./test/k3s/ssh-cloud-init.yaml
|
||||||
|
multipass launch --name k3s-worker1 --cpus 1 --mem 512M --disk 3G --cloud-init ./test/k3s/ssh-cloud-init.yaml
|
||||||
|
multipass launch --name k3s-worker2 --cpus 1 --mem 512M --disk 3G --cloud-init ./test/k3s/ssh-cloud-init.yaml
|
||||||
|
|
||||||
|
test_k3s_infra:
|
||||||
|
./scripts/test_k3s_infra.sh
|
||||||
|
|
||||||
|
stop_k3s_infra:
|
||||||
|
multipass delete k3s-master
|
||||||
|
multipass delete k3s-worker1
|
||||||
|
multipass delete k3s-worker2
|
||||||
|
multipass purge
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -79,10 +79,10 @@ USAGE:
|
|||||||
fx [global options] command [command options] [arguments...]
|
fx [global options] command [command options] [arguments...]
|
||||||
|
|
||||||
VERSION:
|
VERSION:
|
||||||
0.8.1
|
0.8.4
|
||||||
|
|
||||||
COMMANDS:
|
COMMANDS:
|
||||||
init start fx agent on host
|
infra manage infrastructure
|
||||||
up deploy a function
|
up deploy a function
|
||||||
down destroy a service
|
down destroy a service
|
||||||
list, ls list deployed services
|
list, ls list deployed services
|
||||||
@@ -222,6 +222,14 @@ But we would suggest you run `kubectl config current-context` to check if the cu
|
|||||||
* Google Kubernetes Engine (GKET)
|
* Google Kubernetes Engine (GKET)
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
|
* Setup your own Kubernetes cluster
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```shell
|
||||||
|
fx infra create --type k3s --name fx-cluster-1 --master root@123.11.2.3 --agents 'root@1.1.1.1,root@2.2.2.2'
|
||||||
|
```
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
fx uses [Project](https://github.com/metrue/fx/projects/4) to manage the development.
|
fx uses [Project](https://github.com/metrue/fx/projects/4) to manage the development.
|
||||||
|
|||||||
195
config/config.go
Normal file
195
config/config.go
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/metrue/fx/utils"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Items data of config file
|
||||||
|
type Items struct {
|
||||||
|
Clouds map[string]map[string]string `json:"clouds"`
|
||||||
|
CurrentCloud string `json:"current_cloud"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config config of fx
|
||||||
|
type Config struct {
|
||||||
|
mux sync.Mutex
|
||||||
|
configFile string
|
||||||
|
Items
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadDefault load default config
|
||||||
|
func LoadDefault() (*Config, error) {
|
||||||
|
configFile, err := homedir.Expand("~/.fx/config.yml")
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if err := writeDefaultConfig(configFile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return load(configFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
if err := writeDefaultConfig(configFile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return load(configFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCloud add a cloud
|
||||||
|
func (c *Config) addCloud(name string, cloud map[string]string) error {
|
||||||
|
c.Items.Clouds[name] = cloud
|
||||||
|
return save(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDockerCloud add docker cloud
|
||||||
|
func (c *Config) AddDockerCloud(name string, config []byte) error {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
var conf map[string]string
|
||||||
|
err := json.Unmarshal(config, &conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cloud := map[string]string{
|
||||||
|
"type": "docker",
|
||||||
|
"host": conf["ip"],
|
||||||
|
"user": conf["user"],
|
||||||
|
}
|
||||||
|
return c.addCloud(name, cloud)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddK8SCloud add k8s cloud
|
||||||
|
func (c *Config) AddK8SCloud(name string, kubeconfig []byte) error {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
dir := path.Dir(c.configFile)
|
||||||
|
kubecfg := path.Join(dir, name+".kubeconfig")
|
||||||
|
if err := utils.EnsureFile(kubecfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(kubecfg, kubeconfig, 0666); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cloud := map[string]string{
|
||||||
|
"type": "k8s",
|
||||||
|
"kubeConfig": kubecfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.addCloud(name, cloud)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use set cloud instance with name as current context
|
||||||
|
func (c *Config) Use(name string) error {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
has := false
|
||||||
|
for n := range c.Clouds {
|
||||||
|
if n == name {
|
||||||
|
has = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
return fmt.Errorf("no cloud with name = %s", name)
|
||||||
|
}
|
||||||
|
c.Items.CurrentCloud = name
|
||||||
|
return save(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// View view current config
|
||||||
|
func (c *Config) View() ([]byte, error) {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
return ioutil.ReadFile(c.configFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func load(configFile string) (*Config, error) {
|
||||||
|
conf, err := ioutil.ReadFile(configFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var items Items
|
||||||
|
if err := yaml.Unmarshal(conf, &items); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var c = Config{
|
||||||
|
configFile: configFile,
|
||||||
|
Items: items,
|
||||||
|
}
|
||||||
|
return &c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func save(c *Config) error {
|
||||||
|
conf, err := yaml.Marshal(c.Items)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(c.configFile, conf, 0666); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeDefaultConfig(configFile string) error {
|
||||||
|
me, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
items := Items{
|
||||||
|
Clouds: map[string]map[string]string{
|
||||||
|
"default": map[string]string{
|
||||||
|
"type": "docker",
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": me.Username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CurrentCloud: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := yaml.Marshal(items)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(configFile, body, 0666); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
65
config/config_test.go
Normal file
65
config/config_test.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfig(t *testing.T) {
|
||||||
|
configPath := "./tmp/config.yml"
|
||||||
|
defer func() {
|
||||||
|
if err := os.RemoveAll("./tmp"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
c, err := Load(configPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Clouds) != 1 {
|
||||||
|
t.Fatal("should contain default cloud")
|
||||||
|
}
|
||||||
|
|
||||||
|
name := "fx_cluster_1"
|
||||||
|
if err := c.Use(name); err == nil {
|
||||||
|
t.Fatal("should get no such cloud error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.AddK8SCloud(name, []byte("sampe kubeconfg")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := map[string]string{
|
||||||
|
"ip": "127.0.0.1",
|
||||||
|
"user": "use1",
|
||||||
|
}
|
||||||
|
configData, _ := json.Marshal(config)
|
||||||
|
if err := c.AddDockerCloud("docker-1", configData); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Use(name); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.CurrentCloud != name {
|
||||||
|
t.Fatalf("should get %s but got %s", name, c.CurrentCloud)
|
||||||
|
}
|
||||||
|
|
||||||
|
conf, err := Load(configPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if conf.CurrentCloud != name {
|
||||||
|
t.Fatalf("should get %s but got %s", name, c.CurrentCloud)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := c.View()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(body))
|
||||||
|
}
|
||||||
7
config/types.go
Normal file
7
config/types.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
// CloudTypeDocker docker type
|
||||||
|
const CloudTypeDocker = "docker"
|
||||||
|
|
||||||
|
// CloudTypeK8S k8s type
|
||||||
|
const CloudTypeK8S = "k8s"
|
||||||
@@ -129,6 +129,41 @@ func (api *API) post(path string, body []byte, expectStatus int, v interface{})
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Version get version of docker engine
|
||||||
|
func (api *API) Version(ctx context.Context) (string, error) {
|
||||||
|
path := api.endpoint + "/version"
|
||||||
|
if !strings.HasPrefix(path, "http") {
|
||||||
|
path = "http://" + path
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
client := &http.Client{Timeout: 20 * time.Second}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return "", fmt.Errorf("request %s failed: %d - %s", path, resp.StatusCode, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res dockerTypes.Version
|
||||||
|
err = json.Unmarshal(body, &res)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return res.APIVersion, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ListContainer list service
|
// ListContainer list service
|
||||||
func (api *API) ListContainer(ctx context.Context, name string) ([]types.Service, error) {
|
func (api *API) ListContainer(ctx context.Context, name string) ([]types.Service, error) {
|
||||||
if name != "" {
|
if name != "" {
|
||||||
@@ -377,8 +412,6 @@ func (api *API) StartContainer(ctx context.Context, name string, image string, b
|
|||||||
return fmt.Errorf("container id is missing")
|
return fmt.Errorf("container id is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("container %s created", name)
|
|
||||||
|
|
||||||
// start container
|
// start container
|
||||||
path = fmt.Sprintf("/containers/%s/start", createRes.ID)
|
path = fmt.Sprintf("/containers/%s/start", createRes.ID)
|
||||||
url := fmt.Sprintf("%s%s", api.endpoint, path)
|
url := fmt.Sprintf("%s%s", api.endpoint, path)
|
||||||
@@ -402,7 +435,6 @@ func (api *API) StartContainer(ctx context.Context, name string, image string, b
|
|||||||
msg := fmt.Sprintf("start container met issue: %s", string(b))
|
msg := fmt.Sprintf("start container met issue: %s", string(b))
|
||||||
return errors.New(msg)
|
return errors.New(msg)
|
||||||
}
|
}
|
||||||
log.Infof("container %s started", name)
|
|
||||||
|
|
||||||
if _, err = api.inspect(createRes.ID); err != nil {
|
if _, err = api.inspect(createRes.ID); err != nil {
|
||||||
msg := fmt.Sprintf("inspect container %s error", name)
|
msg := fmt.Sprintf("inspect container %s error", name)
|
||||||
@@ -419,6 +451,10 @@ func (api *API) StopContainer(ctx context.Context, name string) error {
|
|||||||
|
|
||||||
// InspectContainer inspect container
|
// InspectContainer inspect container
|
||||||
func (api *API) InspectContainer(ctx context.Context, name string, container interface{}) error {
|
func (api *API) InspectContainer(ctx context.Context, name string, container interface{}) error {
|
||||||
|
path := fmt.Sprintf("/containers/%s/json", name)
|
||||||
|
if err := api.get(path, "", &container); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,111 +1,31 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
// func TestDockerHTTP(t *testing.T) {
|
import (
|
||||||
// const addr = "127.0.0.1"
|
"context"
|
||||||
// const user = ""
|
"os"
|
||||||
// const passord = ""
|
"testing"
|
||||||
// provisioner := provision.NewWithHost(addr, user, passord)
|
|
||||||
// if err := utils.RunWithRetry(func() error {
|
"github.com/docker/docker/api/types"
|
||||||
// if !provisioner.IsFxAgentRunning() {
|
)
|
||||||
// if err := provisioner.StartFxAgent(); err != nil {
|
|
||||||
// log.Infof("could not start fx agent on host: %s", err)
|
func TestDockerHTTP(t *testing.T) {
|
||||||
// return err
|
host := os.Getenv("DOCKER_ENGINE_HOST")
|
||||||
// }
|
port := os.Getenv("DOCKER_ENGINE_PORT")
|
||||||
// log.Infof("fx agent started")
|
if host == "" ||
|
||||||
// } else {
|
port == "" {
|
||||||
// log.Infof("fx agent is running")
|
t.Skip("DOCKER_ENGINE_HOST and DOCKER_ENGINE_PORT required")
|
||||||
// }
|
}
|
||||||
// return nil
|
|
||||||
// }, 2*time.Second, 10); err != nil {
|
api, err := Create(host, port)
|
||||||
// t.Fatal(err)
|
if err != nil {
|
||||||
// } else {
|
t.Fatal(err)
|
||||||
// defer provisioner.StopFxAgent()
|
}
|
||||||
// }
|
name := "fx-agent"
|
||||||
//
|
var container types.ContainerJSON
|
||||||
// host := config.Host{Host: "127.0.0.1"}
|
if err := api.InspectContainer(context.Background(), name, &container); err != nil {
|
||||||
// api, err := Create(host.Host, constants.AgentPort)
|
t.Fatal(err)
|
||||||
// if err != nil {
|
}
|
||||||
// t.Fatal(err)
|
if container.Name != "/"+name {
|
||||||
// }
|
t.Fatalf("should get %s but got %s", name, container.Name)
|
||||||
//
|
}
|
||||||
// serviceName := "a-test-service"
|
}
|
||||||
// project := types.Project{
|
|
||||||
// Name: serviceName,
|
|
||||||
// Language: "node",
|
|
||||||
// Files: []types.ProjectSourceFile{
|
|
||||||
// types.ProjectSourceFile{
|
|
||||||
// Path: "Dockerfile",
|
|
||||||
// Body: `
|
|
||||||
// FROM metrue/fx-node-base
|
|
||||||
//
|
|
||||||
// COPY . .
|
|
||||||
// EXPOSE 3000
|
|
||||||
// CMD ["node", "app.js"]`,
|
|
||||||
// IsHandler: false,
|
|
||||||
// },
|
|
||||||
// types.ProjectSourceFile{
|
|
||||||
// Path: "app.js",
|
|
||||||
// Body: `
|
|
||||||
// const Koa = require('koa');
|
|
||||||
// const bodyParser = require('koa-bodyparser');
|
|
||||||
// const func = require('./fx');
|
|
||||||
//
|
|
||||||
// const app = new Koa();
|
|
||||||
// app.use(bodyParser());
|
|
||||||
// app.use(ctx => {
|
|
||||||
// const msg = func(ctx.request.body);
|
|
||||||
// ctx.body = msg;
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// app.listen(3000);`,
|
|
||||||
// IsHandler: false,
|
|
||||||
// },
|
|
||||||
// types.ProjectSourceFile{
|
|
||||||
// Path: "fx.js",
|
|
||||||
// Body: `
|
|
||||||
// module.exports = (input) => {
|
|
||||||
// return input.a + input.b
|
|
||||||
// }
|
|
||||||
// `,
|
|
||||||
// IsHandler: true,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// service, err := api.Build(project)
|
|
||||||
// if err != nil {
|
|
||||||
// t.Fatal(err)
|
|
||||||
// }
|
|
||||||
// if service.Name != serviceName {
|
|
||||||
// t.Fatalf("should get %s but got %s", serviceName, service.Name)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if err := api.Run(9999, &service); err != nil {
|
|
||||||
// t.Fatal(err)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// services, err := api.ListContainer(serviceName)
|
|
||||||
// if err != nil {
|
|
||||||
// t.Fatal(err)
|
|
||||||
// }
|
|
||||||
// if len(services) != 1 {
|
|
||||||
// t.Fatal("service number should be 1")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if err := api.Stop(serviceName); err != nil {
|
|
||||||
// t.Fatal(err)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// const network = "fx-net"
|
|
||||||
// if err := api.CreateNetwork(network); err != nil {
|
|
||||||
// t.Fatal(err)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// nws, err := api.GetNetwork(network)
|
|
||||||
// if err != nil {
|
|
||||||
// t.Fatal(err)
|
|
||||||
// }
|
|
||||||
// if nws[0].Name != network {
|
|
||||||
// t.Fatalf("should get %s but got %s", network, nws[0].Name)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ func (d *Docker) BuildImage(ctx context.Context, workdir string, name string) er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info(string(body))
|
if os.Getenv("DEBUG") != "" {
|
||||||
|
log.Info(string(body))
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -187,7 +189,16 @@ func (d *Docker) StopContainer(ctx context.Context, name string) error {
|
|||||||
|
|
||||||
// InspectContainer inspect a container
|
// InspectContainer inspect a container
|
||||||
func (d *Docker) InspectContainer(ctx context.Context, name string, container interface{}) error {
|
func (d *Docker) InspectContainer(ctx context.Context, name string, container interface{}) error {
|
||||||
return nil
|
res, err := d.ContainerInspect(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := json.Marshal(res)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.Unmarshal(body, &container)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListContainer list containers
|
// ListContainer list containers
|
||||||
@@ -224,6 +235,15 @@ func (d *Docker) ListContainer(ctx context.Context, name string) ([]types.Servic
|
|||||||
return services, nil
|
return services, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Version get version of docker engine
|
||||||
|
func (d *Docker) Version(ctx context.Context) (string, error) {
|
||||||
|
ping, err := d.Ping(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return ping.APIVersion, nil
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ containerruntimes.ContainerRuntime = &Docker{}
|
_ containerruntimes.ContainerRuntime = &Docker{}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
dockerTypes "github.com/docker/docker/api/types"
|
||||||
|
"github.com/metrue/fx/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDocker(t *testing.T) {
|
func TestDocker(t *testing.T) {
|
||||||
@@ -42,6 +43,23 @@ func TestDocker(t *testing.T) {
|
|||||||
t.Fatalf("should have built image with tag %s", name)
|
t.Fatalf("should have built image with tag %s", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := cli.StartContainer(ctx, name, name, []types.PortBinding{
|
||||||
|
types.PortBinding{
|
||||||
|
ServiceBindingPort: 9000,
|
||||||
|
ContainerExposePort: 3000,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var container dockerTypes.ContainerJSON
|
||||||
|
if err := cli.InspectContainer(ctx, name, &container); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if container.Name != "/"+name {
|
||||||
|
t.Fatalf("should get %s but got %s", "/"+name, container.Name)
|
||||||
|
}
|
||||||
|
|
||||||
username := os.Getenv("DOCKER_USERNAME")
|
username := os.Getenv("DOCKER_USERNAME")
|
||||||
password := os.Getenv("DOCKER_PASSWORD")
|
password := os.Getenv("DOCKER_PASSWORD")
|
||||||
if username == "" || password == "" {
|
if username == "" || password == "" {
|
||||||
|
|||||||
@@ -16,4 +16,5 @@ type ContainerRuntime interface {
|
|||||||
StopContainer(ctx context.Context, name string) error
|
StopContainer(ctx context.Context, name string) error
|
||||||
InspectContainer(ctx context.Context, name string, container interface{}) error
|
InspectContainer(ctx context.Context, name string, container interface{}) error
|
||||||
ListContainer(ctx context.Context, filter string) ([]types.Service, error)
|
ListContainer(ctx context.Context, filter string) ([]types.Service, error)
|
||||||
|
Version(ctx context.Context) (string, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,15 @@ const (
|
|||||||
keyCliCtx = key("cmd_cli")
|
keyCliCtx = key("cmd_cli")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Contexter ctx interface
|
||||||
|
type Contexter interface {
|
||||||
|
Get(k string) interface{}
|
||||||
|
Set(k string, v interface{})
|
||||||
|
Use(fn func(ctx *Context) error) error
|
||||||
|
GetContext() context.Context
|
||||||
|
GetCliContext() *cli.Context
|
||||||
|
}
|
||||||
|
|
||||||
// Context fx context
|
// Context fx context
|
||||||
type Context struct {
|
type Context struct {
|
||||||
context.Context
|
context.Context
|
||||||
@@ -56,3 +65,12 @@ func (ctx *Context) Get(name string) interface{} {
|
|||||||
func (ctx *Context) Use(fn func(ctx *Context) error) error {
|
func (ctx *Context) Use(fn func(ctx *Context) error) error {
|
||||||
return fn(ctx)
|
return fn(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetContext get context
|
||||||
|
func (ctx *Context) GetContext() context.Context {
|
||||||
|
return ctx.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ Contexter = &Context{}
|
||||||
|
)
|
||||||
|
|||||||
@@ -22,4 +22,8 @@ func TestContext(t *testing.T) {
|
|||||||
if v != value {
|
if v != value {
|
||||||
t.Fatalf("should get %v but %v", value, v)
|
t.Fatalf("should get %v but %v", value, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.GetContext() == nil {
|
||||||
|
t.Fatalf("should get context")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
104
context/mocks/context.go
Normal file
104
context/mocks/context.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: context.go
|
||||||
|
|
||||||
|
// Package mock_context is a generated GoMock package.
|
||||||
|
package mock_context
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
context0 "github.com/metrue/fx/context"
|
||||||
|
cli "github.com/urfave/cli"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockContexter is a mock of Contexter interface
|
||||||
|
type MockContexter struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockContexterMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockContexterMockRecorder is the mock recorder for MockContexter
|
||||||
|
type MockContexterMockRecorder struct {
|
||||||
|
mock *MockContexter
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockContexter creates a new mock instance
|
||||||
|
func NewMockContexter(ctrl *gomock.Controller) *MockContexter {
|
||||||
|
mock := &MockContexter{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockContexterMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockContexter) EXPECT() *MockContexterMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get mocks base method
|
||||||
|
func (m *MockContexter) Get(k string) interface{} {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Get", k)
|
||||||
|
ret0, _ := ret[0].(interface{})
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get indicates an expected call of Get
|
||||||
|
func (mr *MockContexterMockRecorder) Get(k interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockContexter)(nil).Get), k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set mocks base method
|
||||||
|
func (m *MockContexter) Set(k string, v interface{}) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Set", k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set indicates an expected call of Set
|
||||||
|
func (mr *MockContexterMockRecorder) Set(k, v interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockContexter)(nil).Set), k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use mocks base method
|
||||||
|
func (m *MockContexter) Use(fn func(*context0.Context) error) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Use", fn)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use indicates an expected call of Use
|
||||||
|
func (mr *MockContexterMockRecorder) Use(fn interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Use", reflect.TypeOf((*MockContexter)(nil).Use), fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContext mocks base method
|
||||||
|
func (m *MockContexter) GetContext() context.Context {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetContext")
|
||||||
|
ret0, _ := ret[0].(context.Context)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContext indicates an expected call of GetContext
|
||||||
|
func (mr *MockContexterMockRecorder) GetContext() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContext", reflect.TypeOf((*MockContexter)(nil).GetContext))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCliContext mocks base method
|
||||||
|
func (m *MockContexter) GetCliContext() *cli.Context {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetCliContext")
|
||||||
|
ret0, _ := ret[0].(*cli.Context)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCliContext indicates an expected call of GetCliContext
|
||||||
|
func (mr *MockContexterMockRecorder) GetCliContext() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCliContext", reflect.TypeOf((*MockContexter)(nil).GetCliContext))
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package deploy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
types "github.com/metrue/fx/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Deployer make a image a service
|
|
||||||
type Deployer interface {
|
|
||||||
Deploy(ctx context.Context, fn types.Func, name string, bindings []types.PortBinding) error
|
|
||||||
Destroy(ctx context.Context, name string) error
|
|
||||||
Update(ctx context.Context, name string) error
|
|
||||||
GetStatus(ctx context.Context, name string) error
|
|
||||||
List(ctx context.Context, name string) ([]types.Service, error)
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
|
||||||
"github.com/metrue/fx/constants"
|
|
||||||
containerruntimes "github.com/metrue/fx/container_runtimes"
|
|
||||||
dockerHTTP "github.com/metrue/fx/container_runtimes/docker/http"
|
|
||||||
dockerSDK "github.com/metrue/fx/container_runtimes/docker/sdk"
|
|
||||||
"github.com/metrue/fx/deploy"
|
|
||||||
"github.com/metrue/fx/packer"
|
|
||||||
"github.com/metrue/fx/types"
|
|
||||||
"github.com/metrue/fx/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Docker manage container
|
|
||||||
type Docker struct {
|
|
||||||
cli containerruntimes.ContainerRuntime
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateClient create a docker instance
|
|
||||||
func CreateClient(ctx context.Context) (d *Docker, err error) {
|
|
||||||
var cli containerruntimes.ContainerRuntime
|
|
||||||
host := os.Getenv("DOCKER_REMOTE_HOST_ADDR")
|
|
||||||
user := os.Getenv("DOCKER_REMOTE_HOST_USER")
|
|
||||||
if host != "" && user != "" {
|
|
||||||
cli, err = dockerHTTP.Create(host, constants.AgentPort)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cli, err = dockerSDK.CreateClient(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Docker{cli: cli}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deploy create a Docker container from given image, and bind the constants.FxContainerExposePort to given port
|
|
||||||
func (d *Docker) Deploy(ctx context.Context, fn types.Func, name string, ports []types.PortBinding) error {
|
|
||||||
workdir := fmt.Sprintf("/tmp/fx-%d", time.Now().Unix())
|
|
||||||
defer os.RemoveAll(workdir)
|
|
||||||
|
|
||||||
if err := packer.PackIntoDir(fn, workdir); err != nil {
|
|
||||||
log.Fatalf("could not pack function %v: %v", fn, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := d.cli.BuildImage(ctx, workdir, name); err != nil {
|
|
||||||
log.Fatalf("could not build image: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
nameWithTag := name + ":latest"
|
|
||||||
if err := d.cli.TagImage(ctx, name, nameWithTag); err != nil {
|
|
||||||
log.Fatalf("could not tag image: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// when deploy a function on a bare Docker running without Kubernetes,
|
|
||||||
// image would be built on-demand on host locally, so there is no need to
|
|
||||||
// pull image from remote.
|
|
||||||
// But it takes some times waiting image ready after image built, we retry to make sure it ready here
|
|
||||||
var imgInfo dockerTypes.ImageInspect
|
|
||||||
if err := utils.RunWithRetry(func() error {
|
|
||||||
return d.cli.InspectImage(ctx, name, &imgInfo)
|
|
||||||
}, time.Second*1, 5); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.cli.StartContainer(ctx, name, name, ports)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update a container
|
|
||||||
func (d *Docker) Update(ctx context.Context, name string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy stop and remove container
|
|
||||||
func (d *Docker) Destroy(ctx context.Context, name string) error {
|
|
||||||
return d.cli.StopContainer(ctx, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStatus get status of container
|
|
||||||
func (d *Docker) GetStatus(ctx context.Context, name string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List services
|
|
||||||
func (d *Docker) List(ctx context.Context, name string) ([]types.Service, error) {
|
|
||||||
// FIXME support remote host
|
|
||||||
return d.cli.ListContainer(ctx, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ deploy.Deployer = &Docker{}
|
|
||||||
)
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/metrue/fx/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDocker(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
cli, err := CreateClient(ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
name := "helloworld"
|
|
||||||
bindings := []types.PortBinding{
|
|
||||||
types.PortBinding{
|
|
||||||
ServiceBindingPort: 80,
|
|
||||||
ContainerExposePort: 3000,
|
|
||||||
},
|
|
||||||
types.PortBinding{
|
|
||||||
ServiceBindingPort: 443,
|
|
||||||
ContainerExposePort: 3000,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
fn := types.Func{
|
|
||||||
Language: "node",
|
|
||||||
Source: `
|
|
||||||
module.exports = (ctx) => {
|
|
||||||
ctx.body = 'hello world'
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
}
|
|
||||||
if err := cli.Deploy(ctx, fn, name, bindings); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
|
|
||||||
if err := cli.Destroy(ctx, name); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BIN
docs/fx-init-cluster.png
Normal file
BIN
docs/fx-init-cluster.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 294 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 78 KiB |
155
fx.go
155
fx.go
@@ -9,18 +9,31 @@ import (
|
|||||||
|
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
aurora "github.com/logrusorgru/aurora"
|
||||||
"github.com/metrue/fx/context"
|
"github.com/metrue/fx/context"
|
||||||
"github.com/metrue/fx/handlers"
|
"github.com/metrue/fx/handlers"
|
||||||
"github.com/metrue/fx/middlewares"
|
"github.com/metrue/fx/middlewares"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "0.8.2"
|
const version = "0.8.7"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
go checkForUpdate()
|
go checkForUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handle(fns ...func(ctx context.Contexter) error) func(ctx *cli.Context) error {
|
||||||
|
return func(c *cli.Context) error {
|
||||||
|
ctx := context.FromCliContext(c)
|
||||||
|
for _, fn := range fns {
|
||||||
|
if err := fn(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func checkForUpdate() {
|
func checkForUpdate() {
|
||||||
const releaseURL = "https://api.github.com/repos/metrue/fx/releases/latest"
|
const releaseURL = "https://api.github.com/repos/metrue/fx/releases/latest"
|
||||||
resp, err := http.Get(releaseURL)
|
resp, err := http.Get(releaseURL)
|
||||||
@@ -56,12 +69,66 @@ func main() {
|
|||||||
app.Usage = "makes function as a service"
|
app.Usage = "makes function as a service"
|
||||||
app.Version = version
|
app.Version = version
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Println(aurora.Red("*****************"))
|
||||||
|
fmt.Println(r)
|
||||||
|
fmt.Println(aurora.Red("*****************"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "init",
|
Name: "infra",
|
||||||
Usage: "start fx agent on host",
|
Usage: "manage infrastructure",
|
||||||
Action: func(c *cli.Context) error {
|
Subcommands: []cli.Command{
|
||||||
return handlers.Init()(context.FromCliContext(c))
|
{
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -87,40 +154,36 @@ func main() {
|
|||||||
Usage: "force deploy a function or functions",
|
Usage: "force deploy a function or functions",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: func(c *cli.Context) error {
|
Action: handle(
|
||||||
ctx := context.FromCliContext(c)
|
middlewares.LoadConfig,
|
||||||
if err := ctx.Use(middlewares.Setup); err != nil {
|
middlewares.Provision,
|
||||||
log.Fatalf("%v", err)
|
middlewares.Parse("up"),
|
||||||
}
|
middlewares.Binding,
|
||||||
if err := ctx.Use(middlewares.Binding); err != nil {
|
middlewares.Build,
|
||||||
log.Fatalf("%v", err)
|
handlers.Up,
|
||||||
}
|
),
|
||||||
return handlers.Up()(ctx)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "down",
|
Name: "down",
|
||||||
Usage: "destroy a service",
|
Usage: "destroy a service",
|
||||||
ArgsUsage: "[service 1, service 2, ....]",
|
ArgsUsage: "[service 1, service 2, ....]",
|
||||||
Action: func(c *cli.Context) error {
|
Action: handle(
|
||||||
ctx := context.FromCliContext(c)
|
middlewares.Parse("down"),
|
||||||
if err := ctx.Use(middlewares.Setup); err != nil {
|
middlewares.LoadConfig,
|
||||||
log.Fatalf("%v", err)
|
middlewares.Provision,
|
||||||
}
|
handlers.Down,
|
||||||
return handlers.Down()(ctx)
|
),
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Aliases: []string{"ls"},
|
Aliases: []string{"ls"},
|
||||||
Usage: "list deployed services",
|
Usage: "list deployed services",
|
||||||
Action: func(c *cli.Context) error {
|
Action: handle(
|
||||||
ctx := context.FromCliContext(c)
|
middlewares.Parse("list"),
|
||||||
if err := ctx.Use(middlewares.Setup); err != nil {
|
middlewares.LoadConfig,
|
||||||
log.Fatalf("%v", err)
|
middlewares.Provision,
|
||||||
}
|
handlers.List,
|
||||||
return handlers.List()(ctx)
|
),
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "call",
|
Name: "call",
|
||||||
@@ -131,9 +194,7 @@ func main() {
|
|||||||
Usage: "fx server host, default is localhost",
|
Usage: "fx server host, default is localhost",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: func(c *cli.Context) error {
|
Action: handle(handlers.Call),
|
||||||
return handlers.Call()(context.FromCliContext(c))
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "image",
|
Name: "image",
|
||||||
@@ -148,13 +209,11 @@ func main() {
|
|||||||
Usage: "image tag",
|
Usage: "image tag",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: func(c *cli.Context) error {
|
Action: handle(
|
||||||
ctx := context.FromCliContext(c)
|
middlewares.LoadConfig,
|
||||||
if err := ctx.Use(middlewares.Setup); err != nil {
|
middlewares.Provision,
|
||||||
log.Fatalf("%v", err)
|
handlers.BuildImage,
|
||||||
}
|
),
|
||||||
return handlers.BuildImage()(ctx)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "export",
|
Name: "export",
|
||||||
@@ -165,22 +224,22 @@ func main() {
|
|||||||
Usage: "output directory",
|
Usage: "output directory",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: func(c *cli.Context) error {
|
Action: handle(
|
||||||
return handlers.ExportImage()(context.FromCliContext(c))
|
middlewares.LoadConfig,
|
||||||
},
|
middlewares.Provision,
|
||||||
|
handlers.ExportImage,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "doctor",
|
Name: "doctor",
|
||||||
Usage: "health check for fx",
|
Usage: "health check for fx",
|
||||||
Action: func(c *cli.Context) error {
|
Action: handle(handlers.Doctor),
|
||||||
return handlers.Doctor()(context.FromCliContext(c))
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
if err := app.Run(os.Args); err != nil {
|
||||||
log.Fatalf("fx startup with fatal: %v", err)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
go.mod
16
go.mod
@@ -21,24 +21,28 @@ require (
|
|||||||
github.com/googleapis/gnostic v0.3.1 // indirect
|
github.com/googleapis/gnostic v0.3.1 // indirect
|
||||||
github.com/gorilla/mux v1.7.3 // indirect
|
github.com/gorilla/mux v1.7.3 // indirect
|
||||||
github.com/imdario/mergo v0.3.7 // indirect
|
github.com/imdario/mergo v0.3.7 // indirect
|
||||||
github.com/metrue/go-ssh-client v0.0.0-20190810064746-98a7a27048f3
|
github.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434
|
||||||
|
github.com/metrue/go-ssh-client v0.0.0-20191125030649-4ac058ee958b
|
||||||
github.com/mholt/archiver v3.1.1+incompatible
|
github.com/mholt/archiver v3.1.1+incompatible
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/nwaples/rardecode v1.0.0 // indirect
|
github.com/nwaples/rardecode v1.0.0 // indirect
|
||||||
|
github.com/olekukonko/tablewriter v0.0.4
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||||
github.com/pelletier/go-toml v1.4.0 // indirect
|
github.com/otiai10/copy v1.0.2
|
||||||
|
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2
|
||||||
github.com/pierrec/lz4 v0.0.0-20190222153722-062282ea0dcf // indirect
|
github.com/pierrec/lz4 v0.0.0-20190222153722-062282ea0dcf // indirect
|
||||||
github.com/pkg/errors v0.8.1
|
github.com/pkg/errors v0.8.1
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
|
||||||
github.com/spf13/viper v1.5.0
|
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
github.com/ugorji/go v1.1.7 // indirect
|
github.com/ugorji/go v1.1.7 // indirect
|
||||||
github.com/urfave/cli v1.22.1
|
github.com/urfave/cli v1.22.2
|
||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 // indirect
|
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 // indirect
|
||||||
gopkg.in/h2non/gock.v1 v1.0.15
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
|
||||||
|
google.golang.org/grpc v1.21.0 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.2.7
|
||||||
gotest.tools v2.2.0+incompatible // indirect
|
gotest.tools v2.2.0+incompatible // indirect
|
||||||
k8s.io/api v0.0.0-20190925180651-d58b53da08f5
|
k8s.io/api v0.0.0-20190925180651-d58b53da08f5
|
||||||
k8s.io/apimachinery v0.0.0-20190925235427-62598f38f24e
|
k8s.io/apimachinery v0.0.0-20190925235427-62598f38f24e
|
||||||
|
|||||||
105
go.sum
105
go.sum
@@ -15,11 +15,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
|||||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
|
||||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
|
||||||
github.com/apex/log v1.1.1 h1:BwhRZ0qbjYtTob0I+2M+smavV0kOC8XgcnGZcyL9liA=
|
github.com/apex/log v1.1.1 h1:BwhRZ0qbjYtTob0I+2M+smavV0kOC8XgcnGZcyL9liA=
|
||||||
github.com/apex/log v1.1.1/go.mod h1:Ls949n1HFtXfbDcjiTTFQqkVUrte0puoIBfO3SVgwOA=
|
github.com/apex/log v1.1.1/go.mod h1:Ls949n1HFtXfbDcjiTTFQqkVUrte0puoIBfO3SVgwOA=
|
||||||
github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
|
github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
|
||||||
@@ -27,18 +24,12 @@ github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3st
|
|||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
|
||||||
github.com/briandowns/spinner v1.7.0 h1:aan1hBBOoscry2TXAkgtxkJiq7Se0+9pt+TUWaPrB4g=
|
github.com/briandowns/spinner v1.7.0 h1:aan1hBBOoscry2TXAkgtxkJiq7Se0+9pt+TUWaPrB4g=
|
||||||
github.com/briandowns/spinner v1.7.0/go.mod h1://Zf9tMcxfRUA36V23M6YGEAv+kECGfvpnLTnb8n4XQ=
|
github.com/briandowns/spinner v1.7.0/go.mod h1://Zf9tMcxfRUA36V23M6YGEAv+kECGfvpnLTnb8n4XQ=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
|
||||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
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 h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||||
@@ -48,7 +39,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
|
||||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v0.0.0-20190313072916-46036c230805 h1:Imk7y5LY4ljn+DhwaPVj9d8kvAxiZw8DQGwNmivIom0=
|
github.com/docker/docker v0.0.0-20190313072916-46036c230805 h1:Imk7y5LY4ljn+DhwaPVj9d8kvAxiZw8DQGwNmivIom0=
|
||||||
@@ -71,20 +61,16 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
|
|||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
|
||||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
|
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
|
||||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||||
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
|
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
|
||||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
|
||||||
github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
|
github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
|
||||||
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||||
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||||
@@ -93,14 +79,10 @@ github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b
|
|||||||
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
|
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
|
||||||
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
||||||
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
|
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
|
||||||
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=
|
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
|
||||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
|
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
@@ -114,7 +96,6 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
|||||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
@@ -134,13 +115,7 @@ github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1a
|
|||||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
|
||||||
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
|
||||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
|
||||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
@@ -153,17 +128,14 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
|
|||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
|
||||||
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
|
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
|
||||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
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 h1:BqUm+LuJcXjGv1d2mj3gBiQyrQ57a0rYoAmhvJQ7RDU=
|
||||||
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||||
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/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=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
@@ -177,9 +149,9 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
|||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434 h1:im9kkmH0WWwxzegiv18gSUJbuXR9y028rXrWuPp6Jug=
|
||||||
|
github.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
|
||||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
github.com/mattn/go-colorable v0.1.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 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||||
@@ -189,12 +161,16 @@ github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc
|
|||||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k=
|
||||||
github.com/metrue/go-ssh-client v0.0.0-20190810064746-98a7a27048f3 h1:IzZATG6TKa6amM5pr8HK7w/Ae4l0VBjmTwTmVbszWFw=
|
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/metrue/go-ssh-client v0.0.0-20190810064746-98a7a27048f3/go.mod h1:ERHOEBrDy6+8vfoJjjmhdmBpOzdvvP7bLtwYTTK6LOs=
|
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||||
|
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
|
github.com/metrue/go-ssh-client v0.0.0-20191125030649-4ac058ee958b h1:JGD0sJ44XzhsT1voOg00zji4ubuMNcVNK3m7d9GI88k=
|
||||||
|
github.com/metrue/go-ssh-client v0.0.0-20191125030649-4ac058ee958b/go.mod h1:ERHOEBrDy6+8vfoJjjmhdmBpOzdvvP7bLtwYTTK6LOs=
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
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 h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU=
|
||||||
github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=
|
github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
@@ -208,13 +184,13 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
|||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
|
||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
|
||||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
|
||||||
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
|
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
|
||||||
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/olekukonko/tablewriter v0.0.3 h1:i0LBnzgiChAWHJYTQAZJDOgf8MNxAVYZJ2m63SIDimI=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.3/go.mod h1:YZeBtGzYYEsCHp2LST/u/0NDwGkRoBtmn1cIWCJiS6M=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@@ -224,29 +200,22 @@ github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2i
|
|||||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
|
github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc=
|
||||||
|
github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY=
|
||||||
|
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||||
|
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
|
|
||||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||||
|
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
|
||||||
|
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||||
github.com/pierrec/lz4 v0.0.0-20190222153722-062282ea0dcf h1:0d7SseXGaeqFXfRTLbiCkuLhSGEHZyKpz1XD3e5lbSo=
|
github.com/pierrec/lz4 v0.0.0-20190222153722-062282ea0dcf h1:0d7SseXGaeqFXfRTLbiCkuLhSGEHZyKpz1XD3e5lbSo=
|
||||||
github.com/pierrec/lz4 v0.0.0-20190222153722-062282ea0dcf/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
github.com/pierrec/lz4 v0.0.0-20190222153722-062282ea0dcf/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
|
||||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
|
||||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
|
||||||
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
||||||
@@ -258,15 +227,12 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
|||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
||||||
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
|
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
|
||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
@@ -275,16 +241,10 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
|
|||||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
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.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
|
||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
|
||||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
|
|
||||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
|
||||||
github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4=
|
|
||||||
github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@@ -294,13 +254,10 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
|
|||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
|
||||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
||||||
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
|
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
|
||||||
github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
|
github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
|
||||||
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
|
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
@@ -311,16 +268,12 @@ github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
|
|||||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||||
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
|
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
|
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
|
||||||
|
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
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/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
@@ -338,15 +291,11 @@ golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/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=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
|
||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
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 h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68=
|
||||||
@@ -366,8 +315,6 @@ golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -387,7 +334,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -396,6 +342,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
|||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
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-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
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.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
@@ -409,7 +356,6 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn
|
|||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
|
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
|
||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -419,23 +365,22 @@ gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXa
|
|||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||||
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
|
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
|
||||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||||
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
|
|
||||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
|
||||||
gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
|
gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
|
||||||
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||||
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a h1:/8zB6iBfHCl1qAnEAWwGPNrUvapuy6CPla1VM0k8hQw=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
k8s.io/api v0.0.0-20190925180651-d58b53da08f5 h1:PEYuamj4laOODrvrh/KIKxihqE8kAnxFRZ6kKtrAS8c=
|
k8s.io/api v0.0.0-20190925180651-d58b53da08f5 h1:PEYuamj4laOODrvrh/KIKxihqE8kAnxFRZ6kKtrAS8c=
|
||||||
k8s.io/api v0.0.0-20190925180651-d58b53da08f5/go.mod h1:blPYY5r6fKug8SVOnjDtFAlzZzInCRL9NNls66SFhFI=
|
k8s.io/api v0.0.0-20190925180651-d58b53da08f5/go.mod h1:blPYY5r6fKug8SVOnjDtFAlzZzInCRL9NNls66SFhFI=
|
||||||
|
|||||||
@@ -1,45 +1,11 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/apex/log"
|
|
||||||
"github.com/metrue/fx/context"
|
"github.com/metrue/fx/context"
|
||||||
"github.com/metrue/fx/packer"
|
|
||||||
"github.com/metrue/fx/types"
|
|
||||||
"github.com/metrue/fx/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Call command handle
|
// Call command handle
|
||||||
func Call() HandleFunc {
|
func Call(ctx context.Contexter) error {
|
||||||
return func(ctx *context.Context) error {
|
// TODO not supported
|
||||||
cli := ctx.GetCliContext()
|
return nil
|
||||||
_ = strings.Join(cli.Args()[1:], " ")
|
|
||||||
|
|
||||||
file := cli.Args().First()
|
|
||||||
src, err := ioutil.ReadFile(file)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Read Source: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info("Read Source: \u2713")
|
|
||||||
|
|
||||||
lang := utils.GetLangFromFileName(file)
|
|
||||||
fn := types.Func{
|
|
||||||
Language: lang,
|
|
||||||
Source: string(src),
|
|
||||||
}
|
|
||||||
if _, err := packer.Pack(file, fn); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO not supported
|
|
||||||
// if err := api.MustCreate(host.Host, constants.AgentPort).
|
|
||||||
// Call(file, params, project); err != nil {
|
|
||||||
// log.Fatalf("call functions on machine %s with %v failed: %v", name, params, err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,19 +10,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Doctor command handle
|
// Doctor command handle
|
||||||
func Doctor() HandleFunc {
|
func Doctor(ctx context.Contexter) error {
|
||||||
return func(ctx *context.Context) error {
|
host := os.Getenv("DOCKER_REMOTE_HOST_ADDR")
|
||||||
host := os.Getenv("DOCKER_REMOTE_HOST_ADDR")
|
user := os.Getenv("DOCKER_REMOTE_HOST_USER")
|
||||||
user := os.Getenv("DOCKER_REMOTE_HOST_USER")
|
password := os.Getenv("DOCKER_REMOTE_HOST_PASSWORD")
|
||||||
password := os.Getenv("DOCKER_REMOTE_HOST_PASSWORD")
|
if host == "" {
|
||||||
if host == "" {
|
host = "localhost"
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
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,24 +2,17 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/metrue/fx/context"
|
"github.com/metrue/fx/context"
|
||||||
"github.com/metrue/fx/deploy"
|
"github.com/metrue/fx/infra"
|
||||||
"github.com/metrue/fx/pkg/spinner"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Down command handle
|
// Down command handle
|
||||||
func Down() HandleFunc {
|
func Down(ctx context.Contexter) (err error) {
|
||||||
return func(ctx *context.Context) (err error) {
|
services := ctx.Get("services").([]string)
|
||||||
spinner.Start("deploying")
|
runner := ctx.Get("deployer").(infra.Deployer)
|
||||||
defer spinner.Stop()
|
for _, svc := range services {
|
||||||
|
if err := runner.Destroy(ctx.GetContext(), svc); err != nil {
|
||||||
cli := ctx.GetCliContext()
|
return err
|
||||||
services := cli.Args()
|
|
||||||
runner := ctx.Get("deployer").(deploy.Deployer)
|
|
||||||
for _, svc := range services {
|
|
||||||
if err := runner.Destroy(ctx.Context, svc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
27
handlers/down_test.go
Normal file
27
handlers/down_test.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
mockCtx "github.com/metrue/fx/context/mocks"
|
||||||
|
mockDeployer "github.com/metrue/fx/infra/mocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDown(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
ctx := mockCtx.NewMockContexter(ctrl)
|
||||||
|
deployer := mockDeployer.NewMockDeployer(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)
|
||||||
|
if err := Down(ctx); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/metrue/fx/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HandleFunc command handle function
|
|
||||||
type HandleFunc func(ctx *context.Context) error
|
|
||||||
@@ -18,68 +18,64 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// BuildImage build image
|
// BuildImage build image
|
||||||
func BuildImage() HandleFunc {
|
func BuildImage(ctx context.Contexter) error {
|
||||||
return func(ctx *context.Context) error {
|
cli := ctx.GetCliContext()
|
||||||
cli := ctx.GetCliContext()
|
funcFile := cli.Args().First()
|
||||||
funcFile := cli.Args().First()
|
tag := cli.String("tag")
|
||||||
tag := cli.String("tag")
|
if tag == "" {
|
||||||
if tag == "" {
|
tag = uuid.New().String()
|
||||||
tag = uuid.New().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
workdir := fmt.Sprintf("/tmp/fx-%d", time.Now().Unix())
|
|
||||||
defer os.RemoveAll(workdir)
|
|
||||||
|
|
||||||
body, err := ioutil.ReadFile(funcFile)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("function code load failed: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Infof("function code loaded: %v", constants.CheckedSymbol)
|
|
||||||
lang := utils.GetLangFromFileName(funcFile)
|
|
||||||
|
|
||||||
fn := types.Func{Language: lang, Source: string(body)}
|
|
||||||
|
|
||||||
if err := packer.PackIntoDir(fn, workdir); err != nil {
|
|
||||||
log.Fatalf("could not pack function %v: %v", fn, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
docker, ok := ctx.Get("docker").(containerruntimes.ContainerRuntime)
|
|
||||||
if ok {
|
|
||||||
nameWithTag := tag + ":latest"
|
|
||||||
if err := docker.BuildImage(ctx.Context, workdir, nameWithTag); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Infof("image built: %v", constants.CheckedSymbol)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("no available docker cli")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
workdir := fmt.Sprintf("/tmp/fx-%d", time.Now().Unix())
|
||||||
|
defer os.RemoveAll(workdir)
|
||||||
|
|
||||||
|
body, err := ioutil.ReadFile(funcFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("function code load failed: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Infof("function code loaded: %v", constants.CheckedSymbol)
|
||||||
|
lang := utils.GetLangFromFileName(funcFile)
|
||||||
|
|
||||||
|
fn := types.Func{Language: lang, Source: string(body)}
|
||||||
|
|
||||||
|
if err := packer.PackIntoDir(fn, workdir); err != nil {
|
||||||
|
log.Fatalf("could not pack function %v: %v", fn, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
docker, ok := ctx.Get("docker").(containerruntimes.ContainerRuntime)
|
||||||
|
if ok {
|
||||||
|
nameWithTag := tag + ":latest"
|
||||||
|
if err := docker.BuildImage(ctx.GetContext(), workdir, nameWithTag); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Infof("image built: %v", constants.CheckedSymbol)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("no available docker cli")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportImage export service's code into a directory
|
// ExportImage export service's code into a directory
|
||||||
func ExportImage() HandleFunc {
|
func ExportImage(ctx context.Contexter) (err error) {
|
||||||
return func(ctx *context.Context) (err error) {
|
cli := ctx.GetCliContext()
|
||||||
cli := ctx.GetCliContext()
|
funcFile := cli.Args().First()
|
||||||
funcFile := cli.Args().First()
|
outputDir := cli.String("output")
|
||||||
outputDir := cli.String("output")
|
if outputDir == "" {
|
||||||
if outputDir == "" {
|
log.Fatalf("output directory required")
|
||||||
log.Fatalf("output directory required")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadFile(funcFile)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "read source failed")
|
|
||||||
}
|
|
||||||
lang := utils.GetLangFromFileName(funcFile)
|
|
||||||
|
|
||||||
if err := packer.PackIntoDir(types.Func{Language: lang, Source: string(body)}, outputDir); err != nil {
|
|
||||||
log.Fatalf("write source code to file failed: %v", constants.UncheckedSymbol)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Infof("exported to %v: %v", outputDir, constants.CheckedSymbol)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadFile(funcFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "read source failed")
|
||||||
|
}
|
||||||
|
lang := utils.GetLangFromFileName(funcFile)
|
||||||
|
|
||||||
|
if err := packer.PackIntoDir(types.Func{Language: lang, Source: string(body)}, outputDir); err != nil {
|
||||||
|
log.Fatalf("write source code to file failed: %v", constants.UncheckedSymbol)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Infof("exported to %v: %v", outputDir, constants.CheckedSymbol)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
97
handlers/infra.go
Normal file
97
handlers/infra.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/metrue/fx/config"
|
||||||
|
"github.com/metrue/fx/context"
|
||||||
|
dockerInfra "github.com/metrue/fx/infra/docker"
|
||||||
|
"github.com/metrue/fx/infra/k8s"
|
||||||
|
"github.com/metrue/fx/pkg/spinner"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupK8S(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 := k8s.MasterNode{
|
||||||
|
User: info[0],
|
||||||
|
IP: info[1],
|
||||||
|
}
|
||||||
|
agents := []k8s.AgentNode{}
|
||||||
|
if agentsInfo != "" {
|
||||||
|
agentsInfoList := strings.Split(agentsInfo, ",")
|
||||||
|
for _, agent := range agentsInfoList {
|
||||||
|
info := strings.Split(agent, "@")
|
||||||
|
if len(info) != 2 {
|
||||||
|
return nil, fmt.Errorf("incorrect agent info, should be <user>@<ip> format")
|
||||||
|
}
|
||||||
|
agents = append(agents, k8s.AgentNode{
|
||||||
|
User: info[0],
|
||||||
|
IP: info[1],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(master, agents, len(agents))
|
||||||
|
k8sOperator := k8s.New(master, agents)
|
||||||
|
return k8sOperator.Provision()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupDocker(hostInfo 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[1]
|
||||||
|
host := info[0]
|
||||||
|
dockr := dockerInfra.CreateProvisioner(user, host)
|
||||||
|
return dockr.Provision()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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":
|
||||||
|
kubeconf, err := setupK8S(cli.String("master"), cli.String("agents"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fxConfig.AddK8SCloud(name, kubeconf)
|
||||||
|
case "docker":
|
||||||
|
config, err := setupDocker(cli.String("host"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fxConfig.AddDockerCloud(name, config)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/apex/log"
|
|
||||||
"github.com/metrue/fx/context"
|
|
||||||
"github.com/metrue/fx/provision"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Init start fx-agent
|
|
||||||
func Init() HandleFunc {
|
|
||||||
return func(ctx *context.Context) error {
|
|
||||||
host := os.Getenv("DOCKER_REMOTE_HOST_ADDR")
|
|
||||||
user := os.Getenv("DOCKER_REMOTE_HOST_USER")
|
|
||||||
passord := os.Getenv("DOCKER_REMOTE_HOST_PASSWORD")
|
|
||||||
if host == "" {
|
|
||||||
host = "127.0.0.1"
|
|
||||||
}
|
|
||||||
provisioner := provision.NewWithHost(host, user, passord)
|
|
||||||
if !provisioner.IsFxAgentRunning() {
|
|
||||||
if err := provisioner.StartFxAgent(); err != nil {
|
|
||||||
log.Fatalf("could not start fx agent on host: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info("fx agent started")
|
|
||||||
}
|
|
||||||
log.Info("fx agent already started")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,31 +2,20 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/metrue/fx/context"
|
"github.com/metrue/fx/context"
|
||||||
"github.com/metrue/fx/deploy"
|
"github.com/metrue/fx/infra"
|
||||||
"github.com/metrue/fx/pkg/spinner"
|
"github.com/metrue/fx/pkg/render"
|
||||||
"github.com/metrue/fx/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// List command handle
|
// List command handle
|
||||||
func List() HandleFunc {
|
func List(ctx context.Contexter) (err error) {
|
||||||
return func(ctx *context.Context) error {
|
cli := ctx.GetCliContext()
|
||||||
spinner.Start("deploying")
|
deployer := ctx.Get("deployer").(infra.Deployer)
|
||||||
defer spinner.Stop()
|
|
||||||
|
|
||||||
cli := ctx.GetCliContext()
|
services, err := deployer.List(ctx.GetContext(), cli.Args().First())
|
||||||
deployer := ctx.Get("deployer").(deploy.Deployer)
|
if err != nil {
|
||||||
|
return err
|
||||||
services, err := deployer.List(ctx.Context, cli.Args().First())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, service := range services {
|
|
||||||
if err := utils.OutputJSON(service); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render.Table(services)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
19
handlers/list_infra.go
Normal file
19
handlers/list_infra.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -1,65 +1,34 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/apex/log"
|
|
||||||
"github.com/metrue/fx/context"
|
"github.com/metrue/fx/context"
|
||||||
"github.com/metrue/fx/deploy"
|
"github.com/metrue/fx/infra"
|
||||||
"github.com/metrue/fx/pkg/spinner"
|
"github.com/metrue/fx/pkg/render"
|
||||||
"github.com/metrue/fx/types"
|
"github.com/metrue/fx/types"
|
||||||
"github.com/metrue/fx/utils"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// PortRange usable port range https: //en.wikipedia.org/wiki/Ephemeral_port
|
|
||||||
var PortRange = struct {
|
|
||||||
min int
|
|
||||||
max int
|
|
||||||
}{
|
|
||||||
min: 1023,
|
|
||||||
max: 65535,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Up command handle
|
// Up command handle
|
||||||
func Up() HandleFunc {
|
func Up(ctx context.Contexter) (err error) {
|
||||||
return func(ctx *context.Context) (err error) {
|
fn := ctx.Get("data").(string)
|
||||||
spinner.Start("deploying")
|
image := ctx.Get("image").(string)
|
||||||
defer spinner.Stop()
|
name := ctx.Get("name").(string)
|
||||||
|
deployer := ctx.Get("deployer").(infra.Deployer)
|
||||||
|
bindings := ctx.Get("bindings").([]types.PortBinding)
|
||||||
|
|
||||||
cli := ctx.GetCliContext()
|
if err := deployer.Deploy(
|
||||||
funcFile := cli.Args().First()
|
ctx.GetContext(),
|
||||||
name := cli.String("name")
|
fn,
|
||||||
port := cli.Int("port")
|
name,
|
||||||
|
image,
|
||||||
defer func() {
|
bindings,
|
||||||
if r := recover(); r != nil {
|
); err != nil {
|
||||||
log.Fatalf("fatal error happened: %v", r)
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("deploy function %s (%s) failed: %v", err)
|
|
||||||
}
|
|
||||||
log.Infof("function %s (%s) deployed successfully", name, funcFile)
|
|
||||||
}()
|
|
||||||
|
|
||||||
if port < PortRange.min || port > PortRange.max {
|
|
||||||
return fmt.Errorf("invalid port number: %d, port number should in range of %d - %d", port, PortRange.min, PortRange.max)
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadFile(funcFile)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "read source failed")
|
|
||||||
}
|
|
||||||
lang := utils.GetLangFromFileName(funcFile)
|
|
||||||
deployer := ctx.Get("deployer").(deploy.Deployer)
|
|
||||||
bindings := ctx.Get("bindings").([]types.PortBinding)
|
|
||||||
return deployer.Deploy(
|
|
||||||
ctx.Context,
|
|
||||||
types.Func{Language: lang, Source: string(body)},
|
|
||||||
name,
|
|
||||||
bindings,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
service, err := deployer.GetStatus(ctx.GetContext(), name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
render.Table([]types.Service{service})
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,40 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
mockCtx "github.com/metrue/fx/context/mocks"
|
||||||
|
mockDeployer "github.com/metrue/fx/infra/mocks"
|
||||||
|
"github.com/metrue/fx/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUp(t *testing.T) {
|
func TestUp(t *testing.T) {
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
ctx := mockCtx.NewMockContexter(ctrl)
|
||||||
|
deployer := mockDeployer.NewMockDeployer(ctrl)
|
||||||
|
|
||||||
|
bindings := []types.PortBinding{}
|
||||||
|
name := "sample-name"
|
||||||
|
image := "sample-image"
|
||||||
|
data := "sample-data"
|
||||||
|
ctx.EXPECT().Get("name").Return(name)
|
||||||
|
ctx.EXPECT().Get("image").Return(image)
|
||||||
|
ctx.EXPECT().Get("deployer").Return(deployer)
|
||||||
|
ctx.EXPECT().Get("bindings").Return(bindings)
|
||||||
|
ctx.EXPECT().Get("data").Return(data)
|
||||||
|
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{
|
||||||
|
ID: "id-1",
|
||||||
|
Name: name,
|
||||||
|
Host: "127.0.0.1",
|
||||||
|
Port: 2100,
|
||||||
|
}, nil)
|
||||||
|
if err := Up(ctx); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
handlers/use_infra.go
Normal file
13
handlers/use_infra.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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.Use(cli.Args().First())
|
||||||
|
}
|
||||||
101
infra/docker/deployer.go
Normal file
101
infra/docker/deployer.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
dockerTypes "github.com/docker/docker/api/types"
|
||||||
|
containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||||
|
"github.com/metrue/fx/infra"
|
||||||
|
"github.com/metrue/fx/pkg/spinner"
|
||||||
|
"github.com/metrue/fx/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deployer manage container
|
||||||
|
type Deployer struct {
|
||||||
|
cli containerruntimes.ContainerRuntime
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateClient create a docker instance
|
||||||
|
func CreateClient(client containerruntimes.ContainerRuntime) (d *Deployer, err error) {
|
||||||
|
return &Deployer{cli: client}, 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) {
|
||||||
|
spinner.Start("deploying " + name)
|
||||||
|
defer func() {
|
||||||
|
spinner.Stop("deploying "+name, err)
|
||||||
|
}()
|
||||||
|
return d.cli.StartContainer(ctx, name, image, ports)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update a container
|
||||||
|
func (d *Deployer) 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) {
|
||||||
|
spinner.Start("destroying " + name)
|
||||||
|
defer func() {
|
||||||
|
spinner.Stop("destroying "+name, err)
|
||||||
|
}()
|
||||||
|
return d.cli.StopContainer(ctx, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStatus get a service status
|
||||||
|
func (d *Deployer) GetStatus(ctx context.Context, name string) (types.Service, error) {
|
||||||
|
var container dockerTypes.ContainerJSON
|
||||||
|
if err := d.cli.InspectContainer(ctx, name, &container); err != nil {
|
||||||
|
return types.Service{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
service := types.Service{
|
||||||
|
ID: container.ID,
|
||||||
|
Name: container.Name,
|
||||||
|
}
|
||||||
|
for _, bindings := range container.NetworkSettings.Ports {
|
||||||
|
if len(bindings) > 0 {
|
||||||
|
binding := bindings[0]
|
||||||
|
port, err := strconv.Atoi(binding.HostPort)
|
||||||
|
if err != nil {
|
||||||
|
return service, err
|
||||||
|
}
|
||||||
|
service.Port = port
|
||||||
|
service.Host = binding.HostIP
|
||||||
|
service.State = container.State.Status
|
||||||
|
service.Image = container.Image
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if service.Port != 0 && service.Host != "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
const task = "listing"
|
||||||
|
spinner.Start(task)
|
||||||
|
defer func() {
|
||||||
|
spinner.Stop(task, err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// FIXME support remote host
|
||||||
|
return d.cli.ListContainer(ctx, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ infra.Deployer = &Deployer{}
|
||||||
|
)
|
||||||
43
infra/docker/deployer_test.go
Normal file
43
infra/docker/deployer_test.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
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)
|
||||||
|
// }
|
||||||
|
}
|
||||||
13
infra/docker/docker.go
Normal file
13
infra/docker/docker.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package docker
|
||||||
|
|
||||||
|
import containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||||
|
|
||||||
|
// CreateProvisioner create a provisioner
|
||||||
|
func CreateProvisioner(ip string, user string) *Provisioner {
|
||||||
|
return NewProvisioner(ip, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDeployer create a deployer
|
||||||
|
func CreateDeployer(client containerruntimes.ContainerRuntime) (*Deployer, error) {
|
||||||
|
return &Deployer{cli: client}, nil
|
||||||
|
}
|
||||||
217
infra/docker/provisioner.go
Normal file
217
infra/docker/provisioner.go
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/metrue/fx/constants"
|
||||||
|
"github.com/metrue/fx/infra"
|
||||||
|
"github.com/metrue/fx/pkg/spinner"
|
||||||
|
sshOperator "github.com/metrue/go-ssh-client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provisioner docker host
|
||||||
|
type Provisioner struct {
|
||||||
|
IP string
|
||||||
|
User string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProvisioner new a docker object
|
||||||
|
func NewProvisioner(ip string, user string) *Provisioner {
|
||||||
|
return &Provisioner{
|
||||||
|
IP: ip,
|
||||||
|
User: user,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provision provision a host, install docker and start dockerd
|
||||||
|
func (d *Provisioner) Provision() (config []byte, err error) {
|
||||||
|
spinner.Start("provisioning")
|
||||||
|
defer func() {
|
||||||
|
spinner.Stop("provisioning", err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// TODO clean up, skip check localhost or not if in CICD env
|
||||||
|
if os.Getenv("CICD") != "" {
|
||||||
|
if err := d.Install(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := d.StartDockerd(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := d.StartFxAgent(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config, _ := json.Marshal(map[string]string{
|
||||||
|
"ip": d.IP,
|
||||||
|
"user": d.User,
|
||||||
|
})
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.isLocalHost() {
|
||||||
|
if !d.hasDocker() {
|
||||||
|
return nil, fmt.Errorf("please make sure docker installed and running")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.StartFxAgentLocally(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
config, _ := json.Marshal(map[string]string{
|
||||||
|
"ip": d.IP,
|
||||||
|
"user": d.User,
|
||||||
|
})
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Install(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := d.StartDockerd(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := d.StartFxAgent(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.Marshal(map[string]string{
|
||||||
|
"ip": d.IP,
|
||||||
|
"user": d.User,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Provisioner) isLocalHost() bool {
|
||||||
|
return strings.ToLower(d.IP) == "localhost" || d.IP == "127.0.0.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Provisioner) hasDocker() bool {
|
||||||
|
cmd := exec.Command("docker", "version")
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthCheck check healthy status of host
|
||||||
|
func (d *Provisioner) HealthCheck() (bool, error) {
|
||||||
|
if d.isLocalHost() {
|
||||||
|
return d.IfFxAgentRunningLocally(), nil
|
||||||
|
}
|
||||||
|
return d.IfFxAgentRunning(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install docker on host
|
||||||
|
func (d *Provisioner) Install() error {
|
||||||
|
sudo := ""
|
||||||
|
if d.User != "root" {
|
||||||
|
sudo = "sudo"
|
||||||
|
}
|
||||||
|
installCmd := fmt.Sprintf("curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-18.06.3-ce.tgz -o docker.tgz && tar zxvf docker.tgz && %s mv docker/* /usr/bin && rm -rf docker docker.tgz", sudo)
|
||||||
|
sshKeyFile, _ := infra.GetSSHKeyFile()
|
||||||
|
sshPort := infra.GetSSHPort()
|
||||||
|
ssh := sshOperator.New(d.IP).WithUser(d.User).WithKey(sshKeyFile).WithPort(sshPort)
|
||||||
|
if err := ssh.RunCommand(installCmd, sshOperator.CommandOptions{
|
||||||
|
Stdout: os.Stdout,
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println("install docker failed \n================")
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Println("================")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartDockerd start dockerd
|
||||||
|
func (d *Provisioner) StartDockerd() error {
|
||||||
|
sudo := ""
|
||||||
|
if d.User != "root" {
|
||||||
|
sudo = "sudo"
|
||||||
|
}
|
||||||
|
installCmd := fmt.Sprintf("%s dockerd >/dev/null 2>&1 & sleep 2", sudo)
|
||||||
|
sshKeyFile, _ := infra.GetSSHKeyFile()
|
||||||
|
sshPort := infra.GetSSHPort()
|
||||||
|
ssh := sshOperator.New(d.IP).WithUser(d.User).WithKey(sshKeyFile).WithPort(sshPort)
|
||||||
|
if err := ssh.RunCommand(installCmd, sshOperator.CommandOptions{
|
||||||
|
Stdout: os.Stdout,
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println("start dockerd failed \n================")
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Println("================")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartFxAgent start fx agent
|
||||||
|
func (d *Provisioner) StartFxAgent() error {
|
||||||
|
startCmd := fmt.Sprintf("sleep 3 && docker stop %s || true && docker run -d --name=%s --rm -v /var/run/docker.sock:/var/run/docker.sock -p 0.0.0.0:%s:1234 bobrik/socat TCP-LISTEN:1234,fork UNIX-CONNECT:/var/run/docker.sock", constants.AgentContainerName, constants.AgentContainerName, constants.AgentPort)
|
||||||
|
sshKeyFile, _ := infra.GetSSHKeyFile()
|
||||||
|
sshPort := infra.GetSSHPort()
|
||||||
|
ssh := sshOperator.New(d.IP).WithUser(d.User).WithKey(sshKeyFile).WithPort(sshPort)
|
||||||
|
if err := ssh.RunCommand(startCmd, sshOperator.CommandOptions{
|
||||||
|
Stdout: os.Stdout,
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println("start fx agent failed \n================")
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Println("================")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartFxAgentLocally start fx agent
|
||||||
|
func (d *Provisioner) StartFxAgentLocally() error {
|
||||||
|
startCmd := fmt.Sprintf("docker run -d --name=%s --rm -v /var/run/docker.sock:/var/run/docker.sock -p 0.0.0.0:%s:1234 bobrik/socat TCP-LISTEN:1234,fork UNIX-CONNECT:/var/run/docker.sock", constants.AgentContainerName, constants.AgentPort)
|
||||||
|
params := strings.Split(startCmd, " ")
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
if len(params) > 1 {
|
||||||
|
// nolint: gosec
|
||||||
|
cmd = exec.Command(params[0], params[1:]...)
|
||||||
|
} else {
|
||||||
|
// nolint: gosec
|
||||||
|
cmd = exec.Command(params[0])
|
||||||
|
}
|
||||||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
fmt.Println(string(out))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfFxAgentRunningLocally check if fx agent is running
|
||||||
|
func (d *Provisioner) IfFxAgentRunningLocally() bool {
|
||||||
|
cmd := exec.Command("docker", "inspect", "fx-agent")
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfFxAgentRunning check if fx agent is running
|
||||||
|
func (d *Provisioner) IfFxAgentRunning() bool {
|
||||||
|
inspectCmd := infra.Sudo("docker inspect fx-agent", d.User)
|
||||||
|
sshKeyFile, _ := infra.GetSSHKeyFile()
|
||||||
|
sshPort := infra.GetSSHPort()
|
||||||
|
ssh := sshOperator.New(d.IP).WithUser(d.User).WithKey(sshKeyFile).WithPort(sshPort)
|
||||||
|
if err := ssh.RunCommand(inspectCmd, sshOperator.CommandOptions{
|
||||||
|
Stdout: os.Stdout,
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
}); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ infra.Provisioner = &Provisioner{}
|
||||||
23
infra/docker/provisioner_test.go
Normal file
23
infra/docker/provisioner_test.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProvisioner(t *testing.T) {
|
||||||
|
if os.Getenv("DOCKER_HOST") == "" ||
|
||||||
|
os.Getenv("DOCKER_USER") == "" {
|
||||||
|
t.Skip("skip test since DOCKER_HOST and DOCKER_USER not ready")
|
||||||
|
}
|
||||||
|
d := NewProvisioner(os.Getenv("DOCKER_HOST"), os.Getenv("DOCKER_USER"))
|
||||||
|
if err := d.Install(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := d.StartDockerd(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := d.StartFxAgent(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
29
infra/infra.go
Normal file
29
infra/infra.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package infra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/metrue/fx/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provisioner provision interface
|
||||||
|
type Provisioner interface {
|
||||||
|
Provision() (config []byte, err error)
|
||||||
|
HealthCheck() (bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deployer deploy interface
|
||||||
|
type Deployer 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
|
||||||
|
GetStatus(ctx context.Context, name string) (types.Service, error)
|
||||||
|
List(ctx context.Context, name string) ([]types.Service, error)
|
||||||
|
Ping(ctx context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infra infrastructure provision interface
|
||||||
|
type Infra interface {
|
||||||
|
Provisioner
|
||||||
|
Deployer
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package kubernetes
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
apiv1 "k8s.io/api/core/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package kubernetes
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
@@ -11,7 +11,7 @@ func TestConfigMap(t *testing.T) {
|
|||||||
t.Skip("skip test since no KUBECONFIG given in environment variable")
|
t.Skip("skip test since no KUBECONFIG given in environment variable")
|
||||||
}
|
}
|
||||||
|
|
||||||
k8s, err := Create()
|
k8s, err := Create("")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package kubernetes
|
package k8s
|
||||||
|
|
||||||
// ConfigMap is the key to function docker project source code in configmap
|
// ConfigMap is the key to function docker project source code in configmap
|
||||||
var ConfigMap = struct {
|
var ConfigMap = struct {
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
package kubernetes
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/metrue/fx/packer"
|
||||||
"github.com/metrue/fx/types"
|
"github.com/metrue/fx/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,21 +27,17 @@ func TestK8SDeployer(t *testing.T) {
|
|||||||
if kubeconfig == "" || username == "" || password == "" {
|
if kubeconfig == "" || username == "" || password == "" {
|
||||||
t.Skip("skip test since no KUBECONFIG, DOCKER_USERNAME and DOCKER_PASSWORD given in environment variable")
|
t.Skip("skip test since no KUBECONFIG, DOCKER_USERNAME and DOCKER_PASSWORD given in environment variable")
|
||||||
}
|
}
|
||||||
k8s, err := Create()
|
k8s, err := Create("")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn := types.Func{
|
data, err := packer.PackIntoK8SConfigMapFile("./fixture")
|
||||||
Language: "node",
|
if err != nil {
|
||||||
Source: `
|
t.Fatal(err)
|
||||||
module.exports = (ctx) => {
|
|
||||||
ctx.body = 'hello world'
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
}
|
}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
if err := k8s.Deploy(ctx, fn, name, bindings); err != nil {
|
if err := k8s.Deploy(ctx, data, name, name, bindings); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
package kubernetes
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/metrue/fx/deploy"
|
"github.com/metrue/fx/infra"
|
||||||
"github.com/metrue/fx/packer"
|
"github.com/metrue/fx/pkg/spinner"
|
||||||
"github.com/metrue/fx/types"
|
"github.com/metrue/fx/types"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
@@ -18,8 +20,12 @@ type K8S struct {
|
|||||||
const namespace = "default"
|
const namespace = "default"
|
||||||
|
|
||||||
// Create a k8s cluster client
|
// Create a k8s cluster client
|
||||||
func Create() (*K8S, error) {
|
func Create(kubeconfig string) (*K8S, error) {
|
||||||
config, err := clientcmd.BuildConfigFromKubeconfigGetter("", clientcmd.NewDefaultClientConfigLoadingRules().Load)
|
if os.Getenv("KUBECONFIG") != "" {
|
||||||
|
kubeconfig = os.Getenv("KUBECONFIG")
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -34,17 +40,13 @@ func Create() (*K8S, error) {
|
|||||||
// Deploy a image to be a service
|
// Deploy a image to be a service
|
||||||
func (k *K8S) Deploy(
|
func (k *K8S) Deploy(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
fn types.Func,
|
fn string,
|
||||||
name string,
|
name string,
|
||||||
|
image string,
|
||||||
ports []types.PortBinding,
|
ports []types.PortBinding,
|
||||||
) error {
|
) error {
|
||||||
// put source code of function docker project into k8s config map
|
|
||||||
tree, err := packer.PackIntoK8SConfigMapFile(fn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data := map[string]string{}
|
data := map[string]string{}
|
||||||
data[ConfigMap.AppMetaEnvName] = tree
|
data[ConfigMap.AppMetaEnvName] = fn
|
||||||
if _, err := k.CreateOrUpdateConfigMap(namespace, name, data); err != nil {
|
if _, err := k.CreateOrUpdateConfigMap(namespace, name, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -56,14 +58,28 @@ func (k *K8S) Deploy(
|
|||||||
const replicas = int32(3)
|
const replicas = int32(3)
|
||||||
if _, err := k.GetDeployment(namespace, name); err != nil {
|
if _, err := k.GetDeployment(namespace, name); err != nil {
|
||||||
// TODO enable passing replica from fx CLI
|
// TODO enable passing replica from fx CLI
|
||||||
if _, err := k.CreateDeploymentWithInitContainer(
|
if os.Getenv("K3S") != "" {
|
||||||
namespace,
|
// NOTE Doing docker build in initial container will fail when cluster is created by K3S
|
||||||
name,
|
if _, err := k.CreateDeployment(
|
||||||
ports,
|
namespace,
|
||||||
replicas,
|
name,
|
||||||
selector,
|
image,
|
||||||
); err != nil {
|
ports,
|
||||||
return err
|
replicas,
|
||||||
|
selector,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := k.CreateDeploymentWithInitContainer(
|
||||||
|
namespace,
|
||||||
|
name,
|
||||||
|
ports,
|
||||||
|
replicas,
|
||||||
|
selector,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if _, err := k.UpdateDeployment(
|
if _, err := k.UpdateDeployment(
|
||||||
@@ -80,10 +96,9 @@ func (k *K8S) Deploy(
|
|||||||
|
|
||||||
// TODO fx should be able to know what's the target Kubernetes service platform
|
// TODO fx should be able to know what's the target Kubernetes service platform
|
||||||
// it's going to deploy to
|
// it's going to deploy to
|
||||||
const isOnPublicCloud = true
|
|
||||||
typ := "LoadBalancer"
|
typ := "LoadBalancer"
|
||||||
if !isOnPublicCloud {
|
if os.Getenv("SERVICE_TYPE") != "" {
|
||||||
typ = "NodePort"
|
typ = os.Getenv("SERVICE_TYPE")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := k.GetService(namespace, name); err != nil {
|
if _, err := k.GetService(namespace, name); err != nil {
|
||||||
@@ -127,15 +142,49 @@ func (k *K8S) Destroy(ctx context.Context, name string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetStatus get status of a service
|
// GetStatus get status of a service
|
||||||
func (k *K8S) GetStatus(ctx context.Context, name string) error {
|
func (k *K8S) GetStatus(ctx context.Context, name string) (types.Service, error) {
|
||||||
return nil
|
svc, err := k.GetService(namespace, name)
|
||||||
|
service := types.Service{}
|
||||||
|
if err != nil {
|
||||||
|
return service, err
|
||||||
|
}
|
||||||
|
|
||||||
|
service.Host = svc.Spec.ClusterIP
|
||||||
|
if len(svc.Spec.ExternalIPs) > 0 {
|
||||||
|
service.Host = svc.Spec.ExternalIPs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, port := range svc.Spec.Ports {
|
||||||
|
// TODO should clearify which port (target port, node port) should use
|
||||||
|
service.Port = int(port.Port)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return service, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// List services
|
// List services
|
||||||
func (k *K8S) List(ctx context.Context, name string) ([]types.Service, error) {
|
func (k *K8S) List(ctx context.Context, name string) (svcs []types.Service, err error) {
|
||||||
|
const task = "listing"
|
||||||
|
spinner.Start(task)
|
||||||
|
defer func() {
|
||||||
|
spinner.Stop(task, err)
|
||||||
|
}()
|
||||||
return []types.Service{}, nil
|
return []types.Service{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ping health check of infra
|
||||||
|
func (k *K8S) Ping(ctx context.Context) error {
|
||||||
|
// Does not find any ping method for k8s
|
||||||
|
nodes, err := k.ListNodes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(nodes.Items) <= 0 {
|
||||||
|
return fmt.Errorf("no available nodes")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ deploy.Deployer = &K8S{}
|
_ infra.Deployer = &K8S{}
|
||||||
)
|
)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package kubernetes
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -29,7 +29,7 @@ func generateDeploymentSpec(
|
|||||||
Name: "fx-placeholder-container-name",
|
Name: "fx-placeholder-container-name",
|
||||||
Image: image,
|
Image: image,
|
||||||
Ports: ports,
|
Ports: ports,
|
||||||
ImagePullPolicy: v1.PullNever,
|
ImagePullPolicy: v1.PullIfNotPresent,
|
||||||
}
|
}
|
||||||
return &appsv1.Deployment{
|
return &appsv1.Deployment{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -98,6 +98,5 @@ func (k *K8S) CreateDeploymentWithInitContainer(
|
|||||||
) (*appsv1.Deployment, error) {
|
) (*appsv1.Deployment, error) {
|
||||||
deployment := generateDeploymentSpec(name, name, ports, replicas, selector)
|
deployment := generateDeploymentSpec(name, name, ports, replicas, selector)
|
||||||
updatedDeployment := injectInitContainer(name, deployment)
|
updatedDeployment := injectInitContainer(name, deployment)
|
||||||
fmt.Println(updatedDeployment)
|
|
||||||
return k.AppsV1().Deployments(namespace).Create(updatedDeployment)
|
return k.AppsV1().Deployments(namespace).Create(updatedDeployment)
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package kubernetes
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
@@ -20,7 +20,7 @@ func TestDeployment(t *testing.T) {
|
|||||||
t.Skip("skip test since no KUBECONFIG given in environment variable")
|
t.Skip("skip test since no KUBECONFIG given in environment variable")
|
||||||
}
|
}
|
||||||
|
|
||||||
k8s, err := Create()
|
k8s, err := Create("")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package kubernetes
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
11
infra/k8s/k8s.go
Normal file
11
infra/k8s/k8s.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package k8s
|
||||||
|
|
||||||
|
// CreateProvisioner create a provisioner
|
||||||
|
func CreateProvisioner(master MasterNode, agents []AgentNode) *Provisioner {
|
||||||
|
return New(master, agents)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDeployer create a deployer
|
||||||
|
func CreateDeployer(kubeconfig string) (*K8S, error) {
|
||||||
|
return Create(kubeconfig)
|
||||||
|
}
|
||||||
15
infra/k8s/node.go
Normal file
15
infra/k8s/node.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package k8s
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListNodes list node
|
||||||
|
func (k *K8S) ListNodes() (*v1.NodeList, error) {
|
||||||
|
nodes, err := k.CoreV1().Nodes().List(metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package kubernetes
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/metrue/fx/constants"
|
"github.com/metrue/fx/constants"
|
||||||
154
infra/k8s/provisioner.go
Normal file
154
infra/k8s/provisioner.go
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
package k8s
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/metrue/fx/infra"
|
||||||
|
sshOperator "github.com/metrue/go-ssh-client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MasterNode master node instance
|
||||||
|
type MasterNode struct {
|
||||||
|
IP string
|
||||||
|
User string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentNode agent node instance
|
||||||
|
type AgentNode struct {
|
||||||
|
IP string
|
||||||
|
User string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provisioner k3s operator
|
||||||
|
type Provisioner struct {
|
||||||
|
master MasterNode
|
||||||
|
agents []AgentNode
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO upgrade to latest when k3s fix the tls scan issue
|
||||||
|
// https://github.com/rancher/k3s/issues/556
|
||||||
|
const version = "v0.9.1"
|
||||||
|
|
||||||
|
// New new a operator
|
||||||
|
func New(master MasterNode, agents []AgentNode) *Provisioner {
|
||||||
|
return &Provisioner{
|
||||||
|
master: master,
|
||||||
|
agents: agents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provision provision k3s cluster
|
||||||
|
func (k *Provisioner) Provision() ([]byte, error) {
|
||||||
|
if err := k.SetupMaster(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := k.SetupAgent(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return k.GetKubeConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthCheck check healthy status of host
|
||||||
|
func (k *Provisioner) HealthCheck() (bool, error) {
|
||||||
|
// TODO
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupMaster setup master node
|
||||||
|
func (k *Provisioner) SetupMaster() error {
|
||||||
|
sshKeyFile, _ := infra.GetSSHKeyFile()
|
||||||
|
ssh := sshOperator.New(k.master.IP).WithUser(k.master.User).WithKey(sshKeyFile)
|
||||||
|
installCmd := fmt.Sprintf("curl -sLS https://get.k3s.io | INSTALL_K3S_EXEC='server --tls-san %s' INSTALL_K3S_VERSION='%s' sh -", k.master.IP, version)
|
||||||
|
if err := ssh.RunCommand(infra.Sudo(installCmd, k.master.User), sshOperator.CommandOptions{
|
||||||
|
Stdout: os.Stdout,
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println("setup master failed \n ===========")
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Println("===========")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Provisioner) getToken() (string, error) {
|
||||||
|
sshKeyFile, _ := infra.GetSSHKeyFile()
|
||||||
|
ssh := sshOperator.New(k.master.IP).WithUser(k.master.User).WithKey(sshKeyFile)
|
||||||
|
script := "cat /var/lib/rancher/k3s/server/node-token"
|
||||||
|
var outPipe bytes.Buffer
|
||||||
|
if err := ssh.RunCommand(infra.Sudo(script, k.master.User), sshOperator.CommandOptions{
|
||||||
|
Stdout: bufio.NewWriter(&outPipe),
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
}); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return outPipe.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupAgent set agent node
|
||||||
|
func (k *Provisioner) SetupAgent() error {
|
||||||
|
sshKeyFile, _ := infra.GetSSHKeyFile()
|
||||||
|
tok, err := k.getToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
const k3sExtraArgs = ""
|
||||||
|
joinCmd := fmt.Sprintf("curl -fL https://get.k3s.io/ | K3S_URL='https://%s:6443' K3S_TOKEN='%s' INSTALL_K3S_VERSION='%s' sh -s - %s", k.master.IP, tok, version, k3sExtraArgs)
|
||||||
|
for _, agent := range k.agents {
|
||||||
|
ssh := sshOperator.New(agent.IP).WithUser(agent.User).WithKey(sshKeyFile)
|
||||||
|
if err := ssh.RunCommand(joinCmd, sshOperator.CommandOptions{
|
||||||
|
Stdout: os.Stdout,
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println("setup agent failed \n================")
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Println("================")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKubeConfig get kubeconfig of k3s cluster
|
||||||
|
func (k *Provisioner) GetKubeConfig() ([]byte, error) {
|
||||||
|
sshKeyFile, _ := infra.GetSSHKeyFile()
|
||||||
|
var config []byte
|
||||||
|
getConfigCmd := "cat /etc/rancher/k3s/k3s.yaml\n"
|
||||||
|
ssh := sshOperator.New(k.master.IP).WithUser(k.master.User).WithKey(sshKeyFile)
|
||||||
|
var outPipe bytes.Buffer
|
||||||
|
if err := ssh.RunCommand(infra.Sudo(getConfigCmd, k.master.User), sshOperator.CommandOptions{
|
||||||
|
Stdout: bufio.NewWriter(&outPipe),
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println("setup agent failed \n================")
|
||||||
|
fmt.Println("================")
|
||||||
|
fmt.Println(err)
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
return rewriteKubeconfig(outPipe.String(), k.master.IP, "default"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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 _ infra.Provisioner = &Provisioner{}
|
||||||
45
infra/k8s/provisioner_test.go
Normal file
45
infra/k8s/provisioner_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package k8s
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProvisioner(t *testing.T) {
|
||||||
|
if os.Getenv("K3S_MASTER_IP") == "" ||
|
||||||
|
os.Getenv("K3S_MASTER_USER") == "" ||
|
||||||
|
os.Getenv("K3S_AGENT_IP") == "" ||
|
||||||
|
os.Getenv("K3S_AGENT_USER") == "" {
|
||||||
|
t.Skip("skip k3s test since K3S_MASTER_IP, K3S_MASTER_USER and K3S_AGENT_IP, K3S_AGENT_USER not ready")
|
||||||
|
}
|
||||||
|
|
||||||
|
master := MasterNode{
|
||||||
|
IP: os.Getenv("K3S_MASTER_IP"),
|
||||||
|
User: os.Getenv("K3S_MASTER_USER"),
|
||||||
|
}
|
||||||
|
agents := []AgentNode{
|
||||||
|
AgentNode{
|
||||||
|
IP: os.Getenv("K3S_AGENT_IP"),
|
||||||
|
User: os.Getenv("K3S_AGENT_USER"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
k3s := New(master, agents)
|
||||||
|
if err := k3s.SetupMaster(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
kubeconfig, err := k3s.GetKubeConfig()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(kubeconfig))
|
||||||
|
|
||||||
|
if _, err := k3s.getToken(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := k3s.SetupAgent(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package kubernetes
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package kubernetes
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
@@ -27,7 +27,7 @@ func TestK8S(t *testing.T) {
|
|||||||
if kubeconfig == "" {
|
if kubeconfig == "" {
|
||||||
t.Skip("skip test since no KUBECONFIG given in environment variable")
|
t.Skip("skip test since no KUBECONFIG given in environment variable")
|
||||||
}
|
}
|
||||||
k8s, err := Create()
|
k8s, err := Create("")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
313
infra/mocks/infra.go
Normal file
313
infra/mocks/infra.go
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
// 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)
|
||||||
|
}
|
||||||
35
infra/ssh.go
Normal file
35
infra/ssh.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package infra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetSSHKeyFile get ssh private key file
|
||||||
|
func GetSSHKeyFile() (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
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSSHPort get ssh port
|
||||||
|
func GetSSHPort() string {
|
||||||
|
port := os.Getenv("SSH_PORT")
|
||||||
|
if port != "" {
|
||||||
|
return port
|
||||||
|
}
|
||||||
|
return "22"
|
||||||
|
}
|
||||||
50
infra/ssh_test.go
Normal file
50
infra/ssh_test.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package infra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetSSHKeyFile(t *testing.T) {
|
||||||
|
t.Run("defaut", func(t *testing.T) {
|
||||||
|
defau, err := GetSSHKeyFile()
|
||||||
|
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 := GetSSHKeyFile()
|
||||||
|
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 := GetSSHPort()
|
||||||
|
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 := GetSSHPort()
|
||||||
|
if defau != "2222" {
|
||||||
|
t.Fatalf("should get %s but got %s", "2222", defau)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
9
infra/sudo.go
Normal file
9
infra/sudo.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package infra
|
||||||
|
|
||||||
|
// Sudo append sudo when user is not root
|
||||||
|
func Sudo(cmd string, user string) string {
|
||||||
|
if user == "root" {
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
return "sudo " + cmd
|
||||||
|
}
|
||||||
5
k3s.cluster.json
Normal file
5
k3s.cluster.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"master": "52.78.196.250",
|
||||||
|
"agents": [
|
||||||
|
"13.125.243.192",
|
||||||
|
],
|
||||||
@@ -1,17 +1,36 @@
|
|||||||
package middlewares
|
package middlewares
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/metrue/fx/constants"
|
"github.com/metrue/fx/constants"
|
||||||
"github.com/metrue/fx/context"
|
"github.com/metrue/fx/context"
|
||||||
"github.com/metrue/fx/types"
|
"github.com/metrue/fx/types"
|
||||||
|
"github.com/phayes/freeport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PortRange usable port range https: //en.wikipedia.org/wiki/Ephemeral_port
|
||||||
|
var PortRange = struct {
|
||||||
|
min int
|
||||||
|
max int
|
||||||
|
}{
|
||||||
|
min: 1023,
|
||||||
|
max: 65535,
|
||||||
|
}
|
||||||
|
|
||||||
// Binding create bindings
|
// Binding create bindings
|
||||||
func Binding(ctx *context.Context) error {
|
func Binding(ctx context.Contexter) (err error) {
|
||||||
cli := ctx.GetCliContext()
|
port := ctx.Get("port").(int)
|
||||||
port := cli.Int("port")
|
if port == 0 {
|
||||||
|
port, err = freeport.GetFreePort()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if port < PortRange.min || port > PortRange.max {
|
||||||
|
return fmt.Errorf("invalid port number: %d, port number should in range of %d - %d", port, PortRange.min, PortRange.max)
|
||||||
|
}
|
||||||
|
|
||||||
var bindings []types.PortBinding
|
var bindings []types.PortBinding
|
||||||
if os.Getenv("KUBECONFIG") != "" {
|
if os.Getenv("KUBECONFIG") != "" {
|
||||||
@@ -24,6 +43,10 @@ func Binding(ctx *context.Context) error {
|
|||||||
ServiceBindingPort: 443,
|
ServiceBindingPort: 443,
|
||||||
ContainerExposePort: constants.FxContainerExposePort,
|
ContainerExposePort: constants.FxContainerExposePort,
|
||||||
},
|
},
|
||||||
|
types.PortBinding{
|
||||||
|
ServiceBindingPort: int32(port),
|
||||||
|
ContainerExposePort: constants.FxContainerExposePort,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bindings = []types.PortBinding{
|
bindings = []types.PortBinding{
|
||||||
|
|||||||
20
middlewares/binding_test.go
Normal file
20
middlewares/binding_test.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
mockCtx "github.com/metrue/fx/context/mocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBinding(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
ctx := mockCtx.NewMockContexter(ctrl)
|
||||||
|
ctx.EXPECT().Get("port").Return(0)
|
||||||
|
ctx.EXPECT().Set("bindings", gomock.Any())
|
||||||
|
if err := Binding(ctx); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
58
middlewares/build.go
Normal file
58
middlewares/build.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||||
|
"github.com/metrue/fx/context"
|
||||||
|
"github.com/metrue/fx/packer"
|
||||||
|
"github.com/metrue/fx/pkg/spinner"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Build image
|
||||||
|
func Build(ctx context.Contexter) (err error) {
|
||||||
|
const task = "building"
|
||||||
|
spinner.Start(task)
|
||||||
|
defer func() {
|
||||||
|
spinner.Stop(task, err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
name := ctx.Get("name").(string)
|
||||||
|
docker := ctx.Get("docker").(containerruntimes.ContainerRuntime)
|
||||||
|
workdir := fmt.Sprintf("/tmp/fx-%d", time.Now().Unix())
|
||||||
|
defer os.RemoveAll(workdir)
|
||||||
|
|
||||||
|
if err := packer.Pack(workdir, ctx.Get("sources").([]string)...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := packer.PackIntoK8SConfigMapFile(workdir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx.Set("data", data)
|
||||||
|
|
||||||
|
if err := docker.BuildImage(ctx.GetContext(), workdir, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nameWithTag := name + ":latest"
|
||||||
|
if err := docker.TagImage(ctx.GetContext(), name, nameWithTag); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx.Set("image", nameWithTag)
|
||||||
|
|
||||||
|
if os.Getenv("K3S") != "" {
|
||||||
|
username := os.Getenv("DOCKER_USERNAME")
|
||||||
|
password := os.Getenv("DOCKER_PASSWORD")
|
||||||
|
if username != "" && password != "" {
|
||||||
|
if _, err := docker.PushImage(ctx.GetContext(), name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx.Set("image", username+"/"+name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
16
middlewares/load_config.go
Normal file
16
middlewares/load_config.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
49
middlewares/parse.go
Normal file
49
middlewares/parse.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/metrue/fx/context"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse parse input
|
||||||
|
func Parse(action string) func(ctx context.Contexter) (err error) {
|
||||||
|
return func(ctx context.Contexter) error {
|
||||||
|
cli := ctx.GetCliContext()
|
||||||
|
switch action {
|
||||||
|
case "up":
|
||||||
|
sources := []string{}
|
||||||
|
for _, s := range cli.Args() {
|
||||||
|
sources = append(sources, s)
|
||||||
|
}
|
||||||
|
if len(sources) == 0 {
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sources = append(sources, pwd)
|
||||||
|
}
|
||||||
|
ctx.Set("sources", sources)
|
||||||
|
name := cli.String("name")
|
||||||
|
ctx.Set("name", name)
|
||||||
|
port := cli.Int("port")
|
||||||
|
ctx.Set("port", port)
|
||||||
|
case "down":
|
||||||
|
services := cli.Args()
|
||||||
|
if len(services) == 0 {
|
||||||
|
return errors.New("service name required")
|
||||||
|
}
|
||||||
|
svc := []string{}
|
||||||
|
for _, service := range services {
|
||||||
|
svc = append(svc, service)
|
||||||
|
}
|
||||||
|
ctx.Set("services", svc)
|
||||||
|
case "list":
|
||||||
|
name := cli.Args().First()
|
||||||
|
ctx.Set("filter", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
60
middlewares/provision.go
Normal file
60
middlewares/provision.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provision make sure infrastructure is healthy
|
||||||
|
func Provision(ctx context.Contexter) (err error) {
|
||||||
|
fxConfig := ctx.Get("config").(*config.Config)
|
||||||
|
cloud := fxConfig.Clouds[fxConfig.CurrentCloud]
|
||||||
|
|
||||||
|
var deployer infra.Deployer
|
||||||
|
if cloud["type"] == config.CloudTypeDocker {
|
||||||
|
provisioner := dockerInfra.CreateProvisioner(cloud["host"], cloud["user"])
|
||||||
|
ok, err := provisioner.HealthCheck()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
if _, err := provisioner.Provision(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
docker, err := dockerHTTP.Create(cloud["host"], 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 cloud["type"] == config.CloudTypeK8S {
|
||||||
|
if os.Getenv("KUBECONFIG") != "" {
|
||||||
|
deployer, err = k8sInfra.CreateDeployer(cloud["kubeconfig"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("unsupport cloud type %s, please make sure you config is correct", cloud["type"])
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Set("deployer", deployer)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
package middlewares
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/apex/log"
|
|
||||||
"github.com/metrue/fx/constants"
|
|
||||||
containerruntimes "github.com/metrue/fx/container_runtimes"
|
|
||||||
dockerHTTP "github.com/metrue/fx/container_runtimes/docker/http"
|
|
||||||
dockerSDK "github.com/metrue/fx/container_runtimes/docker/sdk"
|
|
||||||
"github.com/metrue/fx/context"
|
|
||||||
"github.com/metrue/fx/deploy"
|
|
||||||
dockerDeployer "github.com/metrue/fx/deploy/docker"
|
|
||||||
k8sDeployer "github.com/metrue/fx/deploy/kubernetes"
|
|
||||||
"github.com/metrue/fx/provision"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Setup create k8s or docker cli
|
|
||||||
func Setup(ctx *context.Context) (err error) {
|
|
||||||
host := os.Getenv("DOCKER_REMOTE_HOST_ADDR")
|
|
||||||
user := os.Getenv("DOCKER_REMOTE_HOST_USER")
|
|
||||||
passord := os.Getenv("DOCKER_REMOTE_HOST_PASSWORD")
|
|
||||||
var docker containerruntimes.ContainerRuntime
|
|
||||||
if host != "" && user != "" {
|
|
||||||
provisioner := provision.NewWithHost(host, user, passord)
|
|
||||||
if !provisioner.IsFxAgentRunning() {
|
|
||||||
if err := provisioner.StartFxAgent(); err != nil {
|
|
||||||
log.Fatalf("could not start fx agent on host: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info("fx agent started")
|
|
||||||
}
|
|
||||||
|
|
||||||
docker, err = dockerHTTP.Create(host, constants.AgentPort)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
docker, err = dockerSDK.CreateClient(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.Set("docker", docker)
|
|
||||||
|
|
||||||
var deployer deploy.Deployer
|
|
||||||
if os.Getenv("KUBECONFIG") != "" {
|
|
||||||
deployer, err = k8sDeployer.Create()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
deployer, err = dockerDeployer.CreateClient(ctx.Context)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.Set("deployer", deployer)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/metrue/fx/types"
|
"github.com/metrue/fx/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPacker(t *testing.T) {
|
func TestDockerPacker(t *testing.T) {
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
|||||||
5
packer/fixture/p1/Dockerfile
Normal file
5
packer/fixture/p1/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FROM metrue/fx-node-base
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["node", "app.js"]
|
||||||
9
packer/fixture/p1/app.js
Normal file
9
packer/fixture/p1/app.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const Koa = require('koa');
|
||||||
|
const bodyParser = require('koa-bodyparser');
|
||||||
|
const fx = require('./fx');
|
||||||
|
|
||||||
|
const app = new Koa();
|
||||||
|
app.use(bodyParser());
|
||||||
|
app.use(fx);
|
||||||
|
|
||||||
|
app.listen(3000);
|
||||||
3
packer/fixture/p1/fx.js
Normal file
3
packer/fixture/p1/fx.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = (ctx) => {
|
||||||
|
ctx.body = 'hello world'
|
||||||
|
}
|
||||||
3
packer/fixture/p2/fx.js
Normal file
3
packer/fixture/p2/fx.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = (ctx) => {
|
||||||
|
ctx.body = 'hello world'
|
||||||
|
}
|
||||||
6
packer/fixture/p3/fx.js
Normal file
6
packer/fixture/p3/fx.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
const say = require('./helper')
|
||||||
|
|
||||||
|
module.exports = (ctx) => {
|
||||||
|
say("hi")
|
||||||
|
ctx.body = 'hello world'
|
||||||
|
}
|
||||||
5
packer/fixture/p3/helper.js
Normal file
5
packer/fixture/p3/helper.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const say = (msg) => {
|
||||||
|
console.log(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = say
|
||||||
128
packer/packer.go
128
packer/packer.go
@@ -1,9 +1,12 @@
|
|||||||
package packer
|
package packer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -11,15 +14,106 @@ import (
|
|||||||
"github.com/gobuffalo/packr"
|
"github.com/gobuffalo/packr"
|
||||||
"github.com/metrue/fx/types"
|
"github.com/metrue/fx/types"
|
||||||
"github.com/metrue/fx/utils"
|
"github.com/metrue/fx/utils"
|
||||||
|
"github.com/otiai10/copy"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Packer interface
|
// Pack pack a file or directory into a Docker project
|
||||||
type Packer interface {
|
func Pack(output string, input ...string) error {
|
||||||
Pack(serviceName string, fn types.Func) (types.Project, error)
|
if len(input) == 0 {
|
||||||
|
return fmt.Errorf("source file or directory required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(input) == 1 {
|
||||||
|
file := input[0]
|
||||||
|
stat, err := os.Stat(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !stat.IsDir() {
|
||||||
|
lang := utils.GetLangFromFileName(file)
|
||||||
|
body, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "read source failed")
|
||||||
|
}
|
||||||
|
fn := types.Func{
|
||||||
|
Language: lang,
|
||||||
|
Source: string(body),
|
||||||
|
}
|
||||||
|
if err := PackIntoDir(fn, output); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workdir := fmt.Sprintf("./fx-%d", time.Now().Unix())
|
||||||
|
defer os.RemoveAll(workdir)
|
||||||
|
|
||||||
|
for _, f := range input {
|
||||||
|
if err := copy.Copy(f, filepath.Join(workdir, f)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dockerfile, has := hasDockerfileInDir(workdir); has {
|
||||||
|
return copy.Copy(filepath.Dir(dockerfile), output)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f, has := hasFxHandleFileInDir(workdir); has {
|
||||||
|
lang := utils.GetLangFromFileName(f)
|
||||||
|
body, err := ioutil.ReadFile(f)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "read source failed")
|
||||||
|
}
|
||||||
|
fn := types.Func{
|
||||||
|
Language: lang,
|
||||||
|
Source: string(body),
|
||||||
|
}
|
||||||
|
if err := PackIntoDir(fn, output); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return copy.Copy(filepath.Dir(f), output)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("input directories or files has no Dockerfile or file with fx as name, e.g. fx.js")
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasDockerfileInDir(dir string) (string, bool) {
|
||||||
|
var dockerfile string
|
||||||
|
if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
// nolint
|
||||||
|
if !info.IsDir() && info.Name() == "Dockerfile" {
|
||||||
|
dockerfile = path
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if dockerfile == "" {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return dockerfile, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasFxHandleFileInDir(dir string) (string, bool) {
|
||||||
|
var handleFile string
|
||||||
|
if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if !info.IsDir() && isHandler(info.Name()) {
|
||||||
|
handleFile = path
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if handleFile == "" {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return handleFile, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pack a function to be a docker project which is web service, handle the imcome request with given function
|
// Pack a function to be a docker project which is web service, handle the imcome request with given function
|
||||||
func Pack(svcName string, fn types.Func) (types.Project, error) {
|
func pack(svcName string, fn types.Func) (types.Project, error) {
|
||||||
box := packr.NewBox("./images")
|
box := packr.NewBox("./images")
|
||||||
pkr := NewDockerPacker(box)
|
pkr := NewDockerPacker(box)
|
||||||
return pkr.Pack(svcName, fn)
|
return pkr.Pack(svcName, fn)
|
||||||
@@ -27,7 +121,7 @@ func Pack(svcName string, fn types.Func) (types.Project, error) {
|
|||||||
|
|
||||||
// PackIntoDir pack service code into directory
|
// PackIntoDir pack service code into directory
|
||||||
func PackIntoDir(fn types.Func, outputDir string) error {
|
func PackIntoDir(fn types.Func, outputDir string) error {
|
||||||
project, err := Pack("", fn)
|
project, err := pack("", fn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -44,16 +138,24 @@ func PackIntoDir(fn types.Func, outputDir string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PackIntoK8SConfigMapFile pack function a K8S config map file
|
// PackIntoK8SConfigMapFile pack function a K8S config map file
|
||||||
func PackIntoK8SConfigMapFile(fn types.Func) (string, error) {
|
func PackIntoK8SConfigMapFile(dir string) (string, error) {
|
||||||
project, err := Pack("", fn)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
tree := map[string]string{}
|
tree := map[string]string{}
|
||||||
for _, file := range project.Files {
|
if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
tree[file.Path] = file.Body
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
relpath := strings.Replace(path, dir, "", 1)
|
||||||
|
body, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tree[relpath] = string(body)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(tree)
|
data, err := json.Marshal(tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|||||||
@@ -1,79 +1,122 @@
|
|||||||
package packer
|
package packer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metrue/fx/types"
|
"github.com/metrue/fx/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPack(t *testing.T) {
|
func TestPacker(t *testing.T) {
|
||||||
mockSource := `
|
t.Run("Pack directory with Dockerfile in it", func(t *testing.T) {
|
||||||
|
input := "./fixture/p1"
|
||||||
|
output := "output-1"
|
||||||
|
defer func() {
|
||||||
|
os.RemoveAll(output)
|
||||||
|
}()
|
||||||
|
if err := Pack(output, input); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Pack directory only fx.js in it", func(t *testing.T) {
|
||||||
|
input := "./fixture/p2"
|
||||||
|
output := "output-2"
|
||||||
|
defer func() {
|
||||||
|
os.RemoveAll(output)
|
||||||
|
}()
|
||||||
|
if err := Pack(output, input); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Pack directory with fx.js and helper in it", func(t *testing.T) {
|
||||||
|
input := "./fixture/p3"
|
||||||
|
output := "output-3"
|
||||||
|
defer func() {
|
||||||
|
os.RemoveAll(output)
|
||||||
|
}()
|
||||||
|
if err := Pack(output, input); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Pack files list with fx.js in it", func(t *testing.T) {
|
||||||
|
handleFile := "./fixture/p3/fx.js"
|
||||||
|
helperFile := "./fixture/p3/helper.js"
|
||||||
|
output := "output-4"
|
||||||
|
defer func() {
|
||||||
|
os.RemoveAll(output)
|
||||||
|
}()
|
||||||
|
if err := Pack(output, handleFile, helperFile); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Pack files list without fx.js in it", func(t *testing.T) {
|
||||||
|
f1 := "./fixture/p3/helper.js"
|
||||||
|
f2 := "./fixture/p3/helper.js"
|
||||||
|
output := "output-5"
|
||||||
|
defer func() {
|
||||||
|
os.RemoveAll(output)
|
||||||
|
}()
|
||||||
|
if err := Pack(output, f1, f2); err == nil {
|
||||||
|
t.Fatalf("should report error when there is not Dockerfile or fx.[ext] in it")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("pack", func(t *testing.T) {
|
||||||
|
mockSource := `
|
||||||
module.exports = ({a, b}) => {
|
module.exports = ({a, b}) => {
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
fn := types.Func{
|
fn := types.Func{
|
||||||
Language: "node",
|
Language: "node",
|
||||||
Source: mockSource,
|
Source: mockSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceName := "service-mock"
|
serviceName := "service-mock"
|
||||||
project, err := Pack(serviceName, fn)
|
project, err := pack(serviceName, fn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if project.Name != serviceName {
|
if project.Name != serviceName {
|
||||||
t.Fatalf("should get %s but got %s", serviceName, project.Name)
|
t.Fatalf("should get %s but got %s", serviceName, project.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if project.Language != "node" {
|
if project.Language != "node" {
|
||||||
t.Fatal("incorrect Language")
|
t.Fatal("incorrect Language")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(project.Files) != 3 {
|
if len(project.Files) != 3 {
|
||||||
t.Fatal("node project should have 3 files")
|
t.Fatal("node project should have 3 files")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, file := range project.Files {
|
for _, file := range project.Files {
|
||||||
if file.Path == "fx.js" {
|
if file.Path == "fx.js" {
|
||||||
if file.IsHandler == false {
|
if file.IsHandler == false {
|
||||||
t.Fatal("fx.js should be handler")
|
t.Fatal("fx.js should be handler")
|
||||||
}
|
}
|
||||||
if file.Body != mockSource {
|
if file.Body != mockSource {
|
||||||
t.Fatalf("should get %s but got %v", mockSource, file.Body)
|
t.Fatalf("should get %s but got %v", mockSource, file.Body)
|
||||||
}
|
}
|
||||||
} else if file.Path == "Dockerfile" {
|
} else if file.Path == "Dockerfile" {
|
||||||
if file.IsHandler == true {
|
if file.IsHandler == true {
|
||||||
t.Fatalf("should get %v but got %v", false, file.IsHandler)
|
t.Fatalf("should get %v but got %v", false, file.IsHandler)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if file.IsHandler == true {
|
if file.IsHandler == true {
|
||||||
t.Fatalf("should get %v but %v", false, file.IsHandler)
|
t.Fatalf("should get %v but %v", false, file.IsHandler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTreeAndUnTree(t *testing.T) {
|
func TestTreeAndUnTree(t *testing.T) {
|
||||||
mockSource := `
|
_, err := PackIntoK8SConfigMapFile("./fixture/p1")
|
||||||
package fx;
|
|
||||||
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
public class Fx {
|
|
||||||
public int handle(JSONObject input) {
|
|
||||||
String a = input.get("a").toString();
|
|
||||||
String b = input.get("b").toString();
|
|
||||||
return Integer.parseInt(a) + Integer.parseInt(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
fn := types.Func{
|
|
||||||
Language: "java",
|
|
||||||
Source: mockSource,
|
|
||||||
}
|
|
||||||
_, err := PackIntoK8SConfigMapFile(fn)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -24,8 +26,13 @@ func NewRemoteRunner(sshClient ssh.Client) *RemoteRunner {
|
|||||||
|
|
||||||
// Run script on remote host
|
// Run script on remote host
|
||||||
func (r *RemoteRunner) Run(script string) ([]byte, error) {
|
func (r *RemoteRunner) Run(script string) ([]byte, error) {
|
||||||
stdout, stderr, err := r.sshClient.RunCommand(script)
|
var outPipe bytes.Buffer
|
||||||
output := string(stdout) + string(stderr)
|
var errPipe bytes.Buffer
|
||||||
|
err := r.sshClient.RunCommand(script, ssh.CommandOptions{
|
||||||
|
Stdout: bufio.NewWriter(&outPipe),
|
||||||
|
Stderr: bufio.NewWriter(&errPipe),
|
||||||
|
})
|
||||||
|
output := outPipe.String() + errPipe.String()
|
||||||
return []byte(output), err
|
return []byte(output), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
27
pkg/render/render.go
Normal file
27
pkg/render/render.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/metrue/fx/types"
|
||||||
|
"github.com/olekukonko/tablewriter"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Table output services as table format
|
||||||
|
func Table(services []types.Service) {
|
||||||
|
data := [][]string{}
|
||||||
|
for _, s := range services {
|
||||||
|
col := []string{
|
||||||
|
s.ID,
|
||||||
|
s.Name,
|
||||||
|
fmt.Sprintf("%s:%d", s.Host, +s.Port),
|
||||||
|
}
|
||||||
|
data = append(data, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
table.SetHeader([]string{"ID", "Name", "Endpoint"})
|
||||||
|
table.AppendBulk(data)
|
||||||
|
table.Render()
|
||||||
|
}
|
||||||
19
pkg/render/render_test.go
Normal file
19
pkg/render/render_test.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/metrue/fx/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTable(t *testing.T) {
|
||||||
|
services := []types.Service{
|
||||||
|
types.Service{
|
||||||
|
ID: "id-1",
|
||||||
|
Name: "name-1",
|
||||||
|
Host: "127.0.0.1",
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Table(services)
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
package spinner
|
package spinner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/briandowns/spinner"
|
"github.com/briandowns/spinner"
|
||||||
|
aurora "github.com/logrusorgru/aurora"
|
||||||
)
|
)
|
||||||
|
|
||||||
var s *spinner.Spinner
|
var s *spinner.Spinner
|
||||||
@@ -31,10 +33,17 @@ func Start(task string) {
|
|||||||
// nolint
|
// nolint
|
||||||
s.Color(colors[rand.Intn(len(colors))])
|
s.Color(colors[rand.Intn(len(colors))])
|
||||||
s.Prefix = task + " "
|
s.Prefix = task + " "
|
||||||
s.Start()
|
if s.Active() {
|
||||||
|
s.Restart()
|
||||||
|
} else {
|
||||||
|
s.Start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop spinner
|
// Stop spinner
|
||||||
func Stop() {
|
func Stop(task string, err error) {
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(aurora.Red("\u2717"))
|
||||||
|
}
|
||||||
s.Stop()
|
s.Stop()
|
||||||
}
|
}
|
||||||
|
|||||||
20
pkg/spinner/spiner_test.go
Normal file
20
pkg/spinner/spiner_test.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package spinner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSpinner(t *testing.T) {
|
||||||
|
t.Run("failure", func(t *testing.T) {
|
||||||
|
Start("task 2")
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
Stop("task 2", fmt.Errorf("error happened"))
|
||||||
|
})
|
||||||
|
t.Run("success", func(t *testing.T) {
|
||||||
|
Start("task 1")
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
Stop("task 1", nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
package provision
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/apex/log"
|
|
||||||
"github.com/metrue/fx/constants"
|
|
||||||
"github.com/metrue/fx/pkg/command"
|
|
||||||
ssh "github.com/metrue/go-ssh-client"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Provisioner provision
|
|
||||||
type Provisioner interface {
|
|
||||||
Start() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provisionor provision-or
|
|
||||||
type Provisionor struct {
|
|
||||||
sshClient ssh.Client
|
|
||||||
host string
|
|
||||||
}
|
|
||||||
|
|
||||||
func isLocal(host string) bool {
|
|
||||||
if host == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return host == "127.0.0.1" || host == "localhost" || host == "0.0.0.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWithHost create a provisionor with host, user, and password
|
|
||||||
func NewWithHost(host string, user string, password string) *Provisionor {
|
|
||||||
p := &Provisionor{
|
|
||||||
host: host,
|
|
||||||
}
|
|
||||||
if !isLocal(host) {
|
|
||||||
p.sshClient = ssh.New(host).
|
|
||||||
WithUser(user).
|
|
||||||
WithPassword(password)
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFxAgentRunning check if fx-agent is running on host
|
|
||||||
func (p *Provisionor) IsFxAgentRunning() bool {
|
|
||||||
script := fmt.Sprintf("docker inspect %s", constants.AgentContainerName)
|
|
||||||
var cmd *command.Command
|
|
||||||
if !isLocal(p.host) {
|
|
||||||
cmd = command.New("inspect fx-agent", script, command.NewRemoteRunner(p.sshClient))
|
|
||||||
} else {
|
|
||||||
cmd = command.New("inspect fx-agent", script, command.NewLocalRunner())
|
|
||||||
}
|
|
||||||
output, err := cmd.Exec()
|
|
||||||
if os.Getenv("DEBUG") != "" {
|
|
||||||
log.Infof(string(output))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartFxAgent start fx agent
|
|
||||||
func (p *Provisionor) StartFxAgent() error {
|
|
||||||
script := fmt.Sprintf("docker run -d --name=%s --rm -v /var/run/docker.sock:/var/run/docker.sock -p 0.0.0.0:%s:1234 bobrik/socat TCP-LISTEN:1234,fork UNIX-CONNECT:/var/run/docker.sock", constants.AgentContainerName, constants.AgentPort)
|
|
||||||
var cmd *command.Command
|
|
||||||
if !isLocal(p.host) {
|
|
||||||
cmd = command.New("start fx-agent", script, command.NewRemoteRunner(p.sshClient))
|
|
||||||
} else {
|
|
||||||
cmd = command.New("start fx-agent", script, command.NewLocalRunner())
|
|
||||||
}
|
|
||||||
if output, err := cmd.Exec(); err != nil {
|
|
||||||
log.Info(string(output))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// StopFxAgent stop fx agent
|
|
||||||
func (p *Provisionor) StopFxAgent() error {
|
|
||||||
script := fmt.Sprintf("docker stop %s", constants.AgentContainerName)
|
|
||||||
var cmd *command.Command
|
|
||||||
if !isLocal(p.host) {
|
|
||||||
cmd = command.New("stop fx agent", script, command.NewRemoteRunner(p.sshClient))
|
|
||||||
} else {
|
|
||||||
cmd = command.New("stop fx agent", script, command.NewLocalRunner())
|
|
||||||
}
|
|
||||||
if output, err := cmd.Exec(); err != nil {
|
|
||||||
log.Infof(string(output))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start start provision progress
|
|
||||||
func (p *Provisionor) Start() error {
|
|
||||||
scripts := map[string]string{
|
|
||||||
"pull java Docker base image": "docker pull metrue/fx-java-base",
|
|
||||||
"pull julia Docker base image": "docker pull metrue/fx-julia-base",
|
|
||||||
"pull python Docker base iamge": "docker pull metrue/fx-python-base",
|
|
||||||
"pull node Docker base image": "docker pull metrue/fx-node-base",
|
|
||||||
"pull d Docker base image": "docker pull metrue/fx-d-base",
|
|
||||||
"pull go Docker base image": "docker pull metrue/fx-go-base",
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
for n, s := range scripts {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(name, script string) {
|
|
||||||
var cmd *command.Command
|
|
||||||
if !isLocal(p.host) {
|
|
||||||
cmd = command.New(name, script, command.NewRemoteRunner(p.sshClient))
|
|
||||||
} else {
|
|
||||||
cmd = command.New(name, script, command.NewLocalRunner())
|
|
||||||
}
|
|
||||||
if _, err := cmd.Exec(); err != nil {
|
|
||||||
log.Fatalf("Provision:%s: %s", cmd.Name, err)
|
|
||||||
} else {
|
|
||||||
log.Infof("Provision:%s: \u2713", cmd.Name)
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}(n, s)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package provision
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestProvisionWorkflow(t *testing.T) {
|
|
||||||
provisionor := NewWithHost("127.0.0.1", "", "")
|
|
||||||
|
|
||||||
_ = provisionor.StopFxAgent()
|
|
||||||
// TODO wait too long here to make test pass
|
|
||||||
time.Sleep(40 * time.Second)
|
|
||||||
|
|
||||||
running := provisionor.IsFxAgentRunning()
|
|
||||||
if running {
|
|
||||||
t.Fatalf("fx-agent should not be running")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := provisionor.StartFxAgent(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
running = provisionor.IsFxAgentRunning()
|
|
||||||
if !running {
|
|
||||||
t.Fatalf("fx-agent should be running")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := provisionor.Start(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := provisionor.StopFxAgent(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,9 @@ 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'`; do
|
||||||
echo $d
|
echo $d
|
||||||
go test -race -coverprofile=profile.out -covermode=atomic $d
|
go test -race -coverprofile=profile.out -covermode=atomic $d
|
||||||
|
if [ $? -ne 0 ];then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
if [ -f profile.out ]; then
|
if [ -f profile.out ]; then
|
||||||
cat profile.out | grep -v "mode: atomic" >> coverage.txt
|
cat profile.out | grep -v "mode: atomic" >> coverage.txt
|
||||||
rm profile.out
|
rm profile.out
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ sudo apt-get remove -y docker docker-engine docker.io containerd runc
|
|||||||
apt-get update -y
|
apt-get update -y
|
||||||
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common lsb-core curl
|
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common lsb-core curl
|
||||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
|
||||||
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu \$(lsb_release -cs) stable"
|
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7EA0A9C3F273FCD8
|
||||||
|
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
||||||
sudo apt-get update -y
|
sudo apt-get update -y
|
||||||
sudo apt-get install -y docker-ce
|
sudo apt-get install -y docker-ce
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,14 @@ export_image() {
|
|||||||
# main
|
# main
|
||||||
# clean up
|
# clean up
|
||||||
# docker stop fx-agent || true && docker rm fx-agent || true
|
# docker stop fx-agent || true && docker rm fx-agent || true
|
||||||
|
if [[ "$DOCKER_REMOTE_HOST_ADDR" != "" ]];then
|
||||||
|
cloud_name='fx-remote-docker-host'
|
||||||
|
$fx infra create --name ${cloud_name} --type docker --host ${DOCKER_REMOTE_HOST_USER}@${DOCKER_REMOTE_HOST_ADDR}
|
||||||
|
$fx use ${cloud_name}
|
||||||
|
fi
|
||||||
|
|
||||||
port=20000
|
port=20000
|
||||||
for lang in 'js' 'rb' 'py' 'go' 'php' 'java' 'd'; do
|
for lang in ${1}; do
|
||||||
run $lang $port
|
run $lang $port
|
||||||
((port++))
|
((port++))
|
||||||
|
|
||||||
|
|||||||
9
scripts/test_k3s_infra.sh
Executable file
9
scripts/test_k3s_infra.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
master_ip=$(multipass list | tail -3 | grep k3s-master | awk -F' ' '{print $3}')
|
||||||
|
agent_1_ip=$(multipass list | tail -3 | grep k3s-worker1 | awk -F' ' '{print $3}')
|
||||||
|
agent_2_ip=$(multipass list | tail -3 | grep k3s-worker2 | awk -F' ' '{print $3}')
|
||||||
|
user="multipass"
|
||||||
|
echo SSH_KEY_FILE=./test/id_rsa ./build/fx infra create -name k3s-test-cloud -t k3s --master ${user}@${master_ip} #--agents ${user}@${agent_1_ip},${user}@${agent_2_ip}
|
||||||
|
SSH_KEY_FILE=./test/id_rsa ./build/fx infra create -name k3s-test-cloud -t k3s --master ${user}@${master_ip} #--agents ${user}@${agent_1_ip},${user}@${agent_2_ip}
|
||||||
17
test/Dockerfile
Normal file
17
test/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
FROM ubuntu:16.04
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y openssh-server curl
|
||||||
|
RUN mkdir -p ~/.ssh
|
||||||
|
RUN echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDPVROluD9aW8YEsHiMefr0Yk70TzMJ+yRXkTN0DSDQje6fycffZaxI4vb5JO/tfXkTQCg+uo3t9YVQU3ceFAPpnznCnCr3jnOo7s2BqV5zDRjIW/fG3MLuVyZKvecA5RDIj2WLfvlsev+J6LI/Q/kMr9i8dI9BHp5B3u8Nv3sePEzKU9YRnTd/UTbSdAHKqfpGhgwZEI00q3iiP6f5DKVXZ4b7ZVEsV3cPVrRskurYClSMd32/yaJ+68mFlpwTKI/aq7tZBd5lLsAsd2IxshGE23g4bU04GeeJ76tFT7BvDyL8woshECisRHSdEsdlY9MXIcC/a4hIV4baHXJDkFrf minghe@oldmac.local' >> ~/.ssh/authorized_keys
|
||||||
|
RUN mkdir /var/run/sshd
|
||||||
|
RUN echo 'root:THEPASSWORDYOUCREATED' | chpasswd
|
||||||
|
RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
|
||||||
|
|
||||||
|
# SSH login fix. Otherwise user is kicked off after login
|
||||||
|
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
|
||||||
|
|
||||||
|
ENV NOTVISIBLE "in users profile"
|
||||||
|
RUN echo "export VISIBLE=now" >> /etc/profile
|
||||||
|
|
||||||
|
EXPOSE 22
|
||||||
|
CMD ["/usr/sbin/sshd", "-D"]
|
||||||
27
test/id_rsa
Normal file
27
test/id_rsa
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||||
|
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
|
||||||
|
NhAAAAAwEAAQAAAQEAz1UTpbg/WlvGBLB4jHn69GJO9E8zCfskV5EzdA0g0I3un8nH32Ws
|
||||||
|
SOL2+STv7X15E0AoPrqN7fWFUFN3HhQD6Z85wpwq945zqO7Ngalecw0YyFv3xtzC7lcmSr
|
||||||
|
3nAOUQyI9li375bHr/ieiyP0P5DK/YvHSPQR6eQd7vDb97HjxMylPWEZ03f1E20nQByqn6
|
||||||
|
RoYMGRCNNKt4oj+n+QylV2eG+2VRLFd3D1a0bJLq2ApUjHd9v8mifuvJhZacEyiP2qu7WQ
|
||||||
|
XeZS7ALHdiMbIRhNt4OG1NOBnnie+rRU+wbw8i/MKLIRAorER0nRLHZWPTFyHAv2uISFeG
|
||||||
|
2h1yQ5Ba3wAAA9AgPfD9ID3w/QAAAAdzc2gtcnNhAAABAQDPVROluD9aW8YEsHiMefr0Yk
|
||||||
|
70TzMJ+yRXkTN0DSDQje6fycffZaxI4vb5JO/tfXkTQCg+uo3t9YVQU3ceFAPpnznCnCr3
|
||||||
|
jnOo7s2BqV5zDRjIW/fG3MLuVyZKvecA5RDIj2WLfvlsev+J6LI/Q/kMr9i8dI9BHp5B3u
|
||||||
|
8Nv3sePEzKU9YRnTd/UTbSdAHKqfpGhgwZEI00q3iiP6f5DKVXZ4b7ZVEsV3cPVrRskurY
|
||||||
|
ClSMd32/yaJ+68mFlpwTKI/aq7tZBd5lLsAsd2IxshGE23g4bU04GeeJ76tFT7BvDyL8wo
|
||||||
|
shECisRHSdEsdlY9MXIcC/a4hIV4baHXJDkFrfAAAAAwEAAQAAAQBngaOzYg5Ov+5VvPwR
|
||||||
|
tXvxsXqVQUzLuNNl3BmB4GP3ekQdBZGBF7MxGA4QR754I+HkGG1/E4dzutT5SxH2tCtX4K
|
||||||
|
PnYiuZN0bKmZ2DE9kROwKaVD+YyfGPJ3b4bWH78l+0oNIjGBrRa35TjRYfu4GEMe3T96Tk
|
||||||
|
77I2VGOny2ZdqvpM98tzbmtOkaZm+ixRfYJ2Yx01clYENh2PMKexsJ2YNwwQKyEv7GCZjm
|
||||||
|
xa2WOuzBs7r/WUdPTHRbGCxfvpWEreeTJqMXcSFNW6uf//oXVbQp4BFeePEdSZSVC8aBDE
|
||||||
|
lILoaBsVhWaQFIWU50EQaqbtWGfq/DhxzU610M2iV5LhAAAAgQDzkzz3Jw2goPZVKYjWyg
|
||||||
|
NfzLx+CV2EP0J6IK+Ot3c7RFadWNY0mnMY6bfar6j3aU2A9g/n7ili2sx0cvruYp3DXnRF
|
||||||
|
srVd3tMDb6/PBAlJp0UHrZo37sSeJeRpzIVAVjssgrryXRBxs2XQUf0Zy0DTOlf9Kp6S1Z
|
||||||
|
GTzOB/cI9/YQAAAIEA9+BLSqcOQl4TZ+btaLy16duMaPlNlsc6kp+2EWqYxP5JAPB3SwO9
|
||||||
|
SCaJ2Pab4xt3V51WX4n3HbIJBAobTYweSVJKBY6sKqVEXRiuXjLTC6ywutS3U8iNJh1bj+
|
||||||
|
ro+V/aKHHyiM7b1AxbQiYyo94uhQi/gzHz2lqQQUjigjmbRhkAAACBANYgnazVKNvFXADF
|
||||||
|
vrBfvFrAN8g1IDft7tOowhbb4YCaDptEyj14GhvVe0PEKhaeA3jP7ovr2IyvglpAqVcjvx
|
||||||
|
S7ZYF/JJB3ChvY1ofTGwNNvLfiJ4yDDZhqU2YIIPl320yJAUaVnbGdRf9kEx+CS/Q3S44q
|
||||||
|
LJ2/aaf5bJyLDRe3AAAAE21pbmdoZUBvbGRtYWMubG9jYWwBAgMEBQYH
|
||||||
|
-----END OPENSSH PRIVATE KEY-----
|
||||||
1
test/id_rsa.pub
Normal file
1
test/id_rsa.pub
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDPVROluD9aW8YEsHiMefr0Yk70TzMJ+yRXkTN0DSDQje6fycffZaxI4vb5JO/tfXkTQCg+uo3t9YVQU3ceFAPpnznCnCr3jnOo7s2BqV5zDRjIW/fG3MLuVyZKvecA5RDIj2WLfvlsev+J6LI/Q/kMr9i8dI9BHp5B3u8Nv3sePEzKU9YRnTd/UTbSdAHKqfpGhgwZEI00q3iiP6f5DKVXZ4b7ZVEsV3cPVrRskurYClSMd32/yaJ+68mFlpwTKI/aq7tZBd5lLsAsd2IxshGE23g4bU04GeeJ76tFT7BvDyL8woshECisRHSdEsdlY9MXIcC/a4hIV4baHXJDkFrf minghe@oldmac.local
|
||||||
6
test/k3s/ssh-cloud-init.yaml
Normal file
6
test/k3s/ssh-cloud-init.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#cloud-config
|
||||||
|
|
||||||
|
# add each entry to ~/.ssh/authorized_keys for the configured user or the
|
||||||
|
# first user defined in the user definition directive.
|
||||||
|
ssh_authorized_keys:
|
||||||
|
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDPVROluD9aW8YEsHiMefr0Yk70TzMJ+yRXkTN0DSDQje6fycffZaxI4vb5JO/tfXkTQCg+uo3t9YVQU3ceFAPpnznCnCr3jnOo7s2BqV5zDRjIW/fG3MLuVyZKvecA5RDIj2WLfvlsev+J6LI/Q/kMr9i8dI9BHp5B3u8Nv3sePEzKU9YRnTd/UTbSdAHKqfpGhgwZEI00q3iiP6f5DKVXZ4b7ZVEsV3cPVrRskurYClSMd32/yaJ+68mFlpwTKI/aq7tZBd5lLsAsd2IxshGE23g4bU04GeeJ76tFT7BvDyL8woshECisRHSdEsdlY9MXIcC/a4hIV4baHXJDkFrf minghe@oldmac.local
|
||||||
Reference in New Issue
Block a user