feat(refactor): * (#173)

* 1. user docker remote api instead of docker golang sdk to do stuff
2. remote gRPC code
3. simplify command

* fix(dep): remove private import

* chore(*): clean up

* chore(*): clean up

* chore(*): commit source

* fix(test): *

* fix(env): enable docker remote api

* fix(env): enable docker remote api

* chore(*): clean up

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* fix(ci): use machine to do build

* feat(timeout): longger timeout

* fix(test): *

* fix(typo): flag is name

* chore(*): debug

* chore(*): debug

* chore(*): decrease test time

* fix(type): disable call test since not well design yet

* fix(typo): remove,

* feat(images): pull images

* fix(Pkg): using Pkg first

* fix(Pkg): using Pkg first

* feat(julia): fix version

* chore(*): clean up

* fix(file): ensure file exist

* feat(http): remote http test

* fix(circleci): fix deploy job

* chore(*): update info

* feat(init): update script

* doc(architect): update readme

* refactor(api): refactor api code

* refacot(api): seperate version

* refactor(build): service build as single file

* feat(concurrent): down concurrent

* refactor(api): refactor API

* fix(endpoint): valid endpoint

* fix(path): path change

* feat(release): fix release script

* fix(path): correct path

* feat(api): seperate service run

* fix(release): install goreleaser

* fix(cc): fix cc

* fix(releaser): install releaser from script outside of circleci config

* fix(env): init logic move env

* fix(cli): fix init

* fix(env): avoid conflict

* chore(*): update output

* test(stop): add test for stop

* feat(label):apply default label

* doc(readme): update workflow graph

* fix(init): should just start when container exist

* feat(state): update container by state
This commit is contained in:
Minghe
2019-03-20 17:06:52 +08:00
committed by GitHub
parent 21ec74b03b
commit f04bbd2403
3361 changed files with 3170 additions and 759214 deletions

View File

@@ -1,15 +1,23 @@
defaults: &defaults
docker:
- image: circleci/golang:1.10
working_directory: /go/src/github.com/metrue/fx
machine: true
environment:
IMPORT_PATH: "github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME"
working_directory: /home/circleci/.go_workspace/src/github.com/metrue/fx
attach_workspace: &attach_workspace
attach_workspace:
at: /go/src/github.com/metrue/fx
setup_remote_docker: &setup_remote_docker
setup_remote_docker:
docker_layer_caching: true
install_dep: &install_packr
run:
name: Install packr
command: |
go get -u github.com/gobuffalo/packr/packr
install_dep: &install_dep
run:
name: Install dep
command: |
go get github.com/golang/dep
cd /home/circleci/.go_workspace/src/github.com/golang/dep
go install ./...
install_httpie: &install_httpie
run:
@@ -44,37 +52,28 @@ cli_test: &cli_test
name: cli test
command: make cli-test
http_test: &http_test
run:
name: http test
command: make http-test
grpc_test: &grpc_test
run:
name: grpc test
command: make grpc-test
version: 2
jobs:
test:
<<: *defaults
steps:
- checkout
- *setup_remote_docker
- *install_httpie
- *install_jq
- *install_packr
- *install_dep
- *install_dependencies
- *build_binary
- *unit_test
- *cli_test
- *http_test
- *grpc_test
deploy:
<<: *defaults
steps:
- checkout
- *setup_remote_docker
- *install_packr
- *install_dep
- *install_dependencies
- *build_binary
- run:
name: Release
command: make release
@@ -82,7 +81,6 @@ jobs:
<<: *defaults
steps:
- checkout
- *setup_remote_docker
- *install_dependencies
- run:
name: Release
@@ -107,4 +105,4 @@ workflows:
filters:
branches:
only:
- production
- production

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "third_party/googleapis"]
path = third_party/googleapis
url = https://github.com/googleapis/googleapis
[submodule "wiki"]
path = wiki
url = https://github.com/metrue/fx.wiki.git

307
Gopkg.lock generated Normal file
View File

@@ -0,0 +1,307 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:317998b1359366dda9f69cdbd108c39c7b3813a69002246fac4e3548646c1620"
name = "github.com/apex/log"
packages = ["."]
pruneopts = ""
revision = "941dea75d3ebfbdd905a5d8b7b232965c5e5c684"
version = "v1.1.0"
[[projects]]
digest = "1:0deddd908b6b4b768cfc272c16ee61e7088a60f7fe2f06c547bd3d8e1f8b8e77"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = ""
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
branch = "master"
digest = "1:0e2a96f87af56565ab9c5ad79104b84a142192fbb0a969f79b1baab26c6c50a9"
name = "github.com/docker/docker"
packages = [
"api/types",
"api/types/blkiodev",
"api/types/container",
"api/types/filters",
"api/types/mount",
"api/types/network",
"api/types/registry",
"api/types/strslice",
"api/types/swarm",
"api/types/swarm/runtime",
"api/types/versions",
"errdefs",
]
pruneopts = ""
revision = "46036c230805d55eed797c45678e5e69ef9c1386"
[[projects]]
digest = "1:ebe593d8b65a2947b78b6e164a2dac1a230b977a700b694da3a398b03b7afb04"
name = "github.com/docker/go-connections"
packages = ["nat"]
pruneopts = ""
revision = "7395e3f8aa162843a74ed6d48e79627d9792ac55"
version = "v0.4.0"
[[projects]]
digest = "1:582d54fcb7233da8dde1dfd2210a5b9675d0685f84246a8d317b07d680c18b1b"
name = "github.com/docker/go-units"
packages = ["."]
pruneopts = ""
revision = "47565b4f722fb6ceae66b95f853feed578a4a51c"
version = "v0.3.3"
[[projects]]
digest = "1:ba9a3ee3359edeeec2c6c1ddb6976db4b7ca6ad045e73b43471e32f01b5786e4"
name = "github.com/dsnet/compress"
packages = [
".",
"bzip2",
"bzip2/internal/sais",
"internal",
"internal/errors",
"internal/prefix",
]
pruneopts = ""
revision = "da652975a8eea9fa0735aba8056747a751db0bd3"
version = "v0.0.1"
[[projects]]
digest = "1:83e42cd058ee568f8897de6fb1ec66ba5751ab88436e8d9759824502a9fbed27"
name = "github.com/gobuffalo/envy"
packages = ["."]
pruneopts = ""
revision = "fa0dfdc10b5366ce365b7d9d1755a03e4e797bc5"
version = "v1.6.15"
[[projects]]
branch = "master"
digest = "1:4dc3a807179b54e44ee710e30cae87ea4ba5f3ee6b96a1929ab8042ece05fe19"
name = "github.com/gobuffalo/packd"
packages = ["."]
pruneopts = ""
revision = "d04dd98aca5b9aa7ca7c36ee639a21bcc69de32a"
[[projects]]
digest = "1:169b0f13452aa130cad334e8398aa8358759ab96e4d045bd8b6cba3e27ccda4d"
name = "github.com/gobuffalo/packr"
packages = ["."]
pruneopts = ""
revision = "5aa3f2df6770adb7f8719a43ef25c2522af176ed"
version = "v2.0.3"
[[projects]]
branch = "master"
digest = "1:03179c4a6eecf90cbfbb07648e6202dd999904531c9757ba034f009904de3a9c"
name = "github.com/gobuffalo/syncx"
packages = ["."]
pruneopts = ""
revision = "33c29581e754bd354236e977dfe426e55331c45d"
[[projects]]
digest = "1:fd53b471edb4c28c7d297f617f4da0d33402755f58d6301e7ca1197ef0a90937"
name = "github.com/gogo/protobuf"
packages = ["proto"]
pruneopts = ""
revision = "ba06b47c162d49f2af050fb4c75bcbc86a159d5c"
version = "v1.2.1"
[[projects]]
digest = "1:530233672f656641b365f8efb38ed9fba80e420baff2ce87633813ab3755ed6d"
name = "github.com/golang/mock"
packages = ["gomock"]
pruneopts = ""
revision = "51421b967af1f557f93a59e0057aaf15ca02e29c"
version = "v1.2.0"
[[projects]]
digest = "1:6a6322a15aa8e99bd156fbba0aae4e5d67b4bb05251d860b348a45dfdcba9cce"
name = "github.com/golang/snappy"
packages = ["."]
pruneopts = ""
revision = "2a8bb927dd31d8daada140a5d09578521ce5c36a"
version = "v0.0.1"
[[projects]]
digest = "1:cea4aa2038169ee558bf507d5ea02c94ca85bcca28a4c7bb99fd59b31e43a686"
name = "github.com/google/go-querystring"
packages = ["query"]
pruneopts = ""
revision = "44c6ddd0a2342c386950e880b658017258da92fc"
version = "v1.0.0"
[[projects]]
digest = "1:ad92aa49f34cbc3546063c7eb2cabb55ee2278b72842eda80e2a20a8a06a8d73"
name = "github.com/google/uuid"
packages = ["."]
pruneopts = ""
revision = "0cd6bf5da1e1c83f8b45653022c74f71af0538a4"
version = "v1.1.1"
[[projects]]
digest = "1:0f31ddb2589297fc1d716f45b34e34bff34e968de1aa239543274c87522e86f4"
name = "github.com/h2non/parth"
packages = ["."]
pruneopts = ""
revision = "b4df798d65426f8c8ab5ca5f9987aec5575d26c9"
version = "v2.0.1"
[[projects]]
digest = "1:7df5a9695a743c3e1626b28bb8741602c8c15527e1efaeaec48ab2ff9a23f74c"
name = "github.com/joho/godotenv"
packages = ["."]
pruneopts = ""
revision = "23d116af351c84513e1946b527c88823e476be13"
version = "v1.3.0"
[[projects]]
digest = "1:e26c759de83ce6fe27f99082458a7ad9fcc51a95215b4f5bbacd1345b61bc6ba"
name = "github.com/mholt/archiver"
packages = ["."]
pruneopts = ""
revision = "d572b2e8b82726cee9476d1b9d63a7fe9b601ff1"
version = "v3.1.1"
[[projects]]
digest = "1:b93a939355dc613ca1c23bf39f6d4c207da431a31dd8af001cc334ad24cab9d3"
name = "github.com/nwaples/rardecode"
packages = ["."]
pruneopts = ""
revision = "cc3e4b2381762c56dbcdf9f93be03349a1dc1c14"
version = "v1.0.0"
[[projects]]
digest = "1:5d9b668b0b4581a978f07e7d2e3314af18eb27b3fb5d19b70185b7c575723d11"
name = "github.com/opencontainers/go-digest"
packages = ["."]
pruneopts = ""
revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf"
version = "v1.0.0-rc1"
[[projects]]
digest = "1:f26c8670b11e29a49c8e45f7ec7f2d5bac62e8fd4e3c0ae1662baa4a697f984a"
name = "github.com/opencontainers/image-spec"
packages = [
"specs-go",
"specs-go/v1",
]
pruneopts = ""
revision = "d60099175f88c47cd379c4738d158884749ed235"
version = "v1.0.1"
[[projects]]
digest = "1:7879c0c194c2befcb7e9aaf0163f21daf9f33e5c74372949bed08b6396736ed0"
name = "github.com/phayes/freeport"
packages = ["."]
pruneopts = ""
revision = "b8543db493a5ed890c5499e935e2cad7504f3a04"
version = "1.0.2"
[[projects]]
digest = "1:21b276e792044150a96175edd51d653649b9fd175c51a4613f2741b411c6d674"
name = "github.com/pierrec/lz4"
packages = [
".",
"internal/xxh32",
]
pruneopts = ""
revision = "062282ea0dcff40c9fb8525789eef9644b1fbd6e"
version = "v2.1.0"
[[projects]]
digest = "1:1d7e1867c49a6dd9856598ef7c3123604ea3daabf5b83f303ff457bcbc410b1d"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = ""
revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
version = "v0.8.1"
[[projects]]
digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = ""
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
digest = "1:d367886e3a8134415fad58fb2ac44e2f38aa88068adca7a02d59a555f87085c0"
name = "github.com/rogpeppe/go-internal"
packages = [
"modfile",
"module",
"semver",
]
pruneopts = ""
revision = "1cf9852c553c5b7da2d5a4a091129a7822fed0c9"
version = "v1.2.2"
[[projects]]
digest = "1:381bcbeb112a51493d9d998bbba207a529c73dbb49b3fd789e48c63fac1f192c"
name = "github.com/stretchr/testify"
packages = ["assert"]
pruneopts = ""
revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
version = "v1.3.0"
[[projects]]
digest = "1:cc4c87dc4fa2a87abd2a0901cbd8c0ca10a4a83929d62947de0ad111ab830e01"
name = "github.com/ulikunitz/xz"
packages = [
".",
"internal/hash",
"internal/xlog",
"lzma",
]
pruneopts = ""
revision = "6f934d456d51e742b4eeab20d925a827ef22320a"
version = "v0.5.6"
[[projects]]
digest = "1:e85837cb04b78f61688c6eba93ea9d14f60d611e2aaf8319999b1a60d2dafbfa"
name = "github.com/urfave/cli"
packages = ["."]
pruneopts = ""
revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1"
version = "v1.20.0"
[[projects]]
branch = "master"
digest = "1:5d5ea0c53c32b0465b910eb1d98b045c2f14c416880c54dd5356a1c6b4569041"
name = "github.com/xi2/xz"
packages = ["."]
pruneopts = ""
revision = "48954b6210f8d154cb5f8484d3a3e1f83489309e"
[[projects]]
digest = "1:5eeb4bfc6db411dbb34a6d9e5d49a9956b160d59fd004ee8f03fe53c9605c082"
name = "gopkg.in/h2non/gock.v1"
packages = ["."]
pruneopts = ""
revision = "ba88c4862a27596539531ce469478a91bc5a0511"
version = "v1.0.14"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/apex/log",
"github.com/docker/docker/api/types",
"github.com/docker/docker/api/types/container",
"github.com/docker/docker/api/types/network",
"github.com/docker/go-connections/nat",
"github.com/gobuffalo/packr",
"github.com/golang/mock/gomock",
"github.com/google/go-querystring/query",
"github.com/google/uuid",
"github.com/mholt/archiver",
"github.com/phayes/freeport",
"github.com/stretchr/testify/assert",
"github.com/urfave/cli",
"gopkg.in/h2non/gock.v1",
]
solver-name = "gps-cdcl"
solver-version = 1

49
Gopkg.toml Normal file
View File

@@ -0,0 +1,49 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/docker/docker"
[[constraint]]
name = "github.com/golang/mock"
version = "1.2.0"
[[constraint]]
name = "github.com/mholt/archiver"
version = "3.1.1"
[[constraint]]
name = "github.com/google/uuid"
version = "1.1.0"
[[constraint]]
name = "github.com/google/go-querystring"
version = "1.0.0"
[[constraint]]
name = "gopkg.in/h2non/gock.v1"
version = "1.0.14"

View File

@@ -2,22 +2,10 @@ OUTPUT_DIR=./build
DIST_DIR=./dist
install-deps:
git submodule update --init --recursive
go get -u github.com/olekukonko/tablewriter
go get -u github.com/jteeuwen/go-bindata/...
go get -u golang.org/x/sys/...
go get -u golang.org/x/text/...
go get -u google.golang.org/grpc
go get -u github.com/urfave/cli
go get github.com/goreleaser/goreleaser
# install protoc and plugins
./scripts/install_protoc.sh third_party/protoc
dep ensure
generate:
# generate gRPC related code
cd api && ./gen.sh
# bundle assert into binary
go-bindata -pkg common -o common/asset.go ./assets/dockerfiles/fx/...
packr
build: generate
go build -o ${OUTPUT_DIR}/fx fx.go
@@ -33,6 +21,7 @@ clean:
rm -rf ${DIST_DIR}
unit-test: generate
./scripts/init.sh
./scripts/coverage.sh
cli-test: generate
@@ -41,9 +30,6 @@ cli-test: generate
http-test: generate
./scripts/http_test.sh
grpc-test: generate
echo "TODO"
zip:
zip -r images.zip images/
.PHONY: test build start list clean generate

View File

@@ -1,5 +1,39 @@
fx
------
┌────────┐
│fx init │ fx━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
└────────┘ ┃ ┌───────────────────────┐ ┃
────────────────────────╋─────────▶│Environment initialize │ ┃
┌──────┐ ┃ │* proxy docker sock │ ┃
│fx up │ ┃ │* pull fx base docker │ ┃
┌ ─ ─ ─ ┴──────┘─ ─ ┐ ┃ └───────────────────────┘ ┃
Function Source ┃ ┌──────────────┐ ┌─────────────────────────────┐ ┃
└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘────╋──┬──────▶│ Pack │ │ │ ┃
┃ │ └──────┬───────┘ │ │ ┃
┌────────┐ ┃ │ ┌──────▼───────┐ │ │ ┃
│fx call │ ┃ │ │Build Service │◀─────▶│ │ ┃
└────────┘ ┃ │ └──────┬───────┘ │ │ ┃
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┃ │ ┌──────▼───────┐ │ │ ┃
Function Source ┃ │ │ Run Service │◀─────▶│ │ ┃
│ (with params) │────╋──┤ └──────────────┘ │ │ ┃
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┃ │ │ │ ┃
┃ │ │ │ ┃
┃ │ ┌──────────────┐ │ Docker API │ ┃
┌────────┐ ┃ └──────▶│ Call Service │ │ │ ┃
│fx down │ ┃ │ (http) │ │ │ ┃
└────────┘ ┃ └──────────────┘ │ │ ┃
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┃ ┌──────────────┐ │ │ ┃
Service Name │────╋─────────▶│ Stop Service │◀─────▶│ │ ┃
└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┃ └──────────────┘ │ │ ┃
┌────────┐ ┃ │ │ ┃
│fx list │ ┃ │ │ ┃
└────────┘ ┃ │ │ ┃
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┃ ┌──────────────┐ │ │ ┃
Service Name │────╋─────────▶│List Services │◀─────▶│ │ ┃
└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┃ └──────────────┘ └─────────────────────────────┘ ┃
┃ ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
Poor man's function as a service.
<br/>

196
api/api.go Normal file
View File

@@ -0,0 +1,196 @@
package api
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
dockerTypes "github.com/docker/docker/api/types"
"github.com/gobuffalo/packr"
"github.com/google/go-querystring/query"
"github.com/metrue/fx/types"
)
const serviceNamePrefix = "fx_"
// API interact with dockerd http api
type API struct {
endpoint string
version string
box packr.Box
}
// NewWithDockerRemoteAPI create a api with docker remote api
func NewWithDockerRemoteAPI(url string, version string) *API {
box := packr.NewBox("./images")
endpoint := fmt.Sprintf("%s/v%s", url, version)
return &API{
endpoint: endpoint,
box: box,
}
}
func (api *API) get(path string, qs string, v interface{}) error {
url := fmt.Sprintf("%s%s", api.endpoint, path)
if !strings.HasPrefix(url, "http") {
url = "http://" + url
}
if qs != "" {
url += "?" + qs
}
req, err := http.NewRequest("GET", url, 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", url, resp.StatusCode, resp.Status)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
err = json.Unmarshal(body, &v)
if err != nil {
return err
}
return nil
}
func (api *API) post(path string, body []byte, expectStatus int, v interface{}) error {
url := fmt.Sprintf("%s%s", api.endpoint, path)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
client := &http.Client{Timeout: 20 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != expectStatus {
return fmt.Errorf("request %s (%s) failed: %d - %s", url, string(body), resp.StatusCode, resp.Status)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
err = json.Unmarshal(b, &v)
if err != nil {
return err
}
return nil
}
// List list service
func (api *API) list(name string) ([]types.Service, error) {
if name != "" {
info, err := api.inspect(name)
if err != nil {
return []types.Service{}, err
}
port, err := strconv.Atoi(info.HostConfig.PortBindings["3000/tcp"][0].HostPort)
if err != nil {
return []types.Service{}, err
}
return []types.Service{
types.Service{
Name: name,
Image: info.Image,
Status: types.ServiceStatusRunning,
Instances: []types.Instance{
types.Instance{
ID: info.ID,
Host: info.HostConfig.PortBindings["3000/tcp"][0].HostIP,
Port: port,
State: info.State.Status,
},
},
},
}, nil
}
type filterItem struct {
Status []string `json:"url,omitempty"`
Label []string `json:"label,omitempty"`
Name []string `json:"name,omitempty"`
}
type Filters struct {
Items string `url:"filters"`
}
filter := filterItem{
// Status: []string{"running"},
Label: []string{"belong-to=fx"},
}
q, err := json.Marshal(filter)
if err != nil {
return []types.Service{}, err
}
filters := Filters{Items: string(q)}
qs, err := query.Values(filters)
if err != nil {
return []types.Service{}, err
}
var containers []dockerTypes.Container
if err := api.get("/containers/json", qs.Encode(), &containers); err != nil {
return []types.Service{}, err
}
svs := make(map[string]types.Service)
for _, container := range containers {
// container name have extra forward slash
// https://github.com/moby/moby/issues/6705
if strings.HasPrefix(container.Names[0], fmt.Sprintf("/%s", name)) {
instance := types.Instance{
ID: container.ID,
Host: container.Ports[0].IP,
Port: int(container.Ports[0].PublicPort),
State: container.State,
}
if svs[container.Image].Instances != nil {
instances := append(svs[container.Image].Instances, instance)
svs[container.Image] = types.Service{Instances: instances}
} else {
svs[container.Image] = types.Service{
Name: name,
Image: container.Image,
Status: types.ServiceStatusRunning,
Instances: []types.Instance{instance},
}
}
}
}
services := []types.Service{}
for _, s := range svs {
services = append(services, s)
}
return services, nil
}

91
api/api_test.go Normal file
View File

@@ -0,0 +1,91 @@
package api
import (
"testing"
"github.com/metrue/fx/types"
)
func TestDockerHTTP(t *testing.T) {
dockerRemoteAPI := "http://127.0.0.1:1234"
version, err := Version(dockerRemoteAPI)
if err != nil {
t.Fatal(err)
}
api := NewWithDockerRemoteAPI(dockerRemoteAPI, version)
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 err != nil {
t.Fatal(err)
}
if service.Status != types.ServiceStatusInit {
t.Fatalf("should get %d but got %d", types.ServiceStatusInit, service.Status)
}
if service.Name != serviceName {
t.Fatalf("should get %s but got %s", serviceName, service.Name)
}
if err := api.Run(&service); err != nil {
t.Fatal(err)
}
services, err := api.list(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)
}
}

View File

@@ -0,0 +1,5 @@
FROM ubuntu:16.04
RUN apt-get update && apt-get install -y build-essential curl libcurl3 \
&& curl -OL http://downloads.dlang.org/releases/2.x/2.077.1/dmd_2.077.1-0_amd64.deb \
&& apt install ./dmd_2.077.1-0_amd64.deb

View File

@@ -0,0 +1,12 @@
FROM java:8
# Install maven
RUN apt-get update
RUN apt-get install -y maven
WORKDIR /code
# Prepare by downloading dependencies
ADD pom.xml /code/pom.xml
RUN ["mvn", "dependency:resolve"]
RUN ["mvn", "verify"]

View File

@@ -0,0 +1,3 @@
build:
docker build -t metrue/fx-java-base:latest .
docker push metrue/fx-java-base

View File

@@ -0,0 +1,3 @@
FROM julia:latest
RUN apt-get update && apt-get install -y gcc apt-utils unzip make libhttp-parser-dev

View File

@@ -0,0 +1,3 @@
build:
docker build -t metrue/fx-julia-base:latest .
docker push metrue/fx-julia-base

View File

@@ -0,0 +1,4 @@
FROM node:latest
COPY . .
RUN npm install

View File

@@ -0,0 +1,21 @@
{
"name": "aok",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"get-port": "^3.2.0",
"is-generator-function": "^1.0.6",
"koa": "^2.3.0",
"koa-bodyparser": "^4.2.0"
},
"devDependencies": {
"get-port-cli": "^1.1.0"
}
}

View File

@@ -0,0 +1,5 @@
FROM python:3
RUN pip install flask
ENV FLASK_APP=app.py

View File

@@ -0,0 +1,970 @@
[[package]]
name = "base64"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "base64"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bytes"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cookie"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-utils"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "devise"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"devise_codegen 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"devise_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "devise_codegen"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"devise_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "devise_core"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "filetime"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fsevent"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fsevent-sys"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "httparse"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hyper"
version = "0.10.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "idna"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "indexmap"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "inotify"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "inotify-sys"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "iovec"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "isatty"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itoa"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "language-tags"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazycell"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lock_api"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "matches"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mime"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio"
version = "0.6.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio-extras"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "miow"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "net2"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "notify"
version = "4.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"fsevent 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
"fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num_cpus"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "owning_ref"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pear"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"pear_codegen 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pear_codegen"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "percent-encoding"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_syscall"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ring"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rocket"
version = "0.4.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"isatty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pear 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rocket_codegen 0.4.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rocket_http 0.4.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)",
"state 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rocket_codegen"
version = "0.4.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"devise 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"rocket_http 0.4.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rocket_contrib"
version = "0.4.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"notify 4.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rocket 0.4.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rocket_http"
version = "0.4.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pear 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
"state 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rust"
version = "0.1.0"
dependencies = [
"rocket 0.4.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rocket_contrib 0.4.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ryu"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "safemem"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "same-file"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "scopeguard"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_derive"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "slab"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallvec"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "stable_deref_trait"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "state"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.15.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "time"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-executor"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-io"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-reactor"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam-utils 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "toml"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "traitobject"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "typeable"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicase"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-bidi"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-normalization"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unreachable"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "untrusted"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "url"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "version_check"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "walkdir"
version = "2.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-util"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "yansi"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "yansi"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2"
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
"checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa"
"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1465f8134efa296b4c19db34d909637cb2bf0f7aaf21299e23e18fa29ac557cf"
"checksum crossbeam-utils 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c55913cc2799171a550e307918c0a360e8c16004820291bf3b638969b4a01816"
"checksum devise 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74e04ba2d03c5fa0d954c061fc8c9c288badadffc272ebb87679a89846de3ed3"
"checksum devise_codegen 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "066ceb7928ca93a9bedc6d0e612a8a0424048b0ab1f75971b203d01420c055d7"
"checksum devise_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf41c59b22b5e3ec0ea55c7847e5f358d340f3a8d6d53a5cf4f1564967f96487"
"checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646"
"checksum fsevent 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "c4bbbf71584aeed076100b5665ac14e3d85eeb31fdbb45fbd41ef9a682b5ec05"
"checksum fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a772d36c338d07a032d5375a36f15f9a7043bf0cb8ce7cee658e037c6032874"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b"
"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83"
"checksum hyper 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)" = "df0caae6b71d266b91b4a83111a61d2b94ed2e2bea024c532b933dcff867e58c"
"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
"checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718"
"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
"checksum isatty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e31a8281fc93ec9693494da65fbf28c0c2aa60a2eaec25dc58e2f31952e95edc"
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
"checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"
"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
"checksum memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a3eb002f0535929f1199681417029ebea04aadc0c7a4224b46be99c7f5d6a16"
"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432"
"checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
"checksum notify 4.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "873ecfd8c174964ae30f401329d140142312c8e5590719cf1199d5f1717d8078"
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13"
"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5"
"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c"
"checksum pear 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c26d2b92e47063ffce70d3e3b1bd097af121a9e0db07ca38a6cc1cf0cc85ff25"
"checksum pear_codegen 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "336db4a192cc7f54efeb0c4e11a9245394824cc3bcbd37ba3ff51240c35d7a6e"
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"
"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
"checksum redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "679da7508e9a6390aeaf7fbd02a800fdc64b73fe2204dd2c8ae66d22d9d5ad5d"
"checksum ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4db68a2e35f3497146b7e4563df7d4773a2433230c5e4b448328e31740458a"
"checksum rocket 0.4.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eaa997ea8de9b14112aa38b2b6a0ecf3e651ff2c08d2fdf384fa765b5f9c2c98"
"checksum rocket_codegen 0.4.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fc68f90452ac88c6c1e02a922a0a23ef0ade08f9af899056d0c919b25fa7768c"
"checksum rocket_contrib 0.4.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2cc6a37cb7a6256efe6648f2d0ab9978c49ab883909ea4fabefb81d7c685d841"
"checksum rocket_http 0.4.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c7d555ce896830602aedf4bce2eec8d64713d45a2492c5a3625c3faa5f719b0f"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9"
"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267"
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef"
"checksum serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "225de307c6302bec3898c51ca302fc94a7a1697ef0845fcee6448f33c032249c"
"checksum serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "c37ccd6be3ed1fdf419ee848f7c758eb31b054d7cd3ae3600e3bae0adf569811"
"checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d"
"checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
"checksum state 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7345c971d1ef21ffdbd103a75990a15eb03604fc8b8852ca8cb418ee1a099028"
"checksum syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)" = "ae8b29eb5210bc5cf63ed6149cbf9adfc82ac0be023d8735c176ee74a2db4da7"
"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b"
"checksum tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c117b6cf86bb730aab4834f10df96e4dd586eff2c3c27d3781348da49e255bde"
"checksum tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "7392fe0a70d5ce0c882c4778116c519bd5dbaa8a7c3ae3d04578b3afafdcda21"
"checksum tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "502b625acb4ee13cbb3b90b8ca80e0addd263ddacf6931666ef751e610b07fb5"
"checksum toml 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "19782e145d5abefb03758958f06ea35f7b1d8421b534140e0238fd3d0bfd66e3"
"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f"
"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d60c3b48c9cdec42fb06b3b84b5b087405e1fa1c644a1af3930e4dfafe93de48"
"checksum yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"

View File

@@ -0,0 +1,13 @@
[package]
name = "rust"
version = "0.1.0"
authors = ["FrontMage <xbgxwh@outlook.com>"]
edition = "2018"
[dependencies]
rocket = "0.4.0-rc.2"
rocket_contrib = "0.4.0-rc.2"
serde_json = "1.0"
serde_derive = "1.0.70"
serde = "1.0.70"

View File

@@ -0,0 +1,7 @@
FROM liuchong/rustup
WORKDIR /usr/src/myapp
COPY . .
RUN cp ./config ~/.cargo/ && rustup default nightly && cargo build
EXPOSE 3000
CMD ["cargo", "run"]

View File

@@ -0,0 +1,5 @@
[development]
address = "0.0.0.0"
port = 3000
log = "normal"

View File

@@ -0,0 +1,6 @@
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"

View File

@@ -0,0 +1,18 @@
pub mod fns {
#[derive(Serialize)]
pub struct Response {
pub result: i32,
}
#[derive(Deserialize)]
pub struct Request {
pub a: i32,
pub b: i32,
}
pub fn func(req: Request) -> Response {
Response {
result: req.a + req.b,
}
}
}

View File

@@ -0,0 +1,18 @@
pub mod fns {
#[derive(Serialize)]
pub struct Response {
pub result: i32,
}
#[derive(Deserialize)]
pub struct Request {
pub a: i32,
pub b: i32,
}
pub fn func(req: Request) -> Response {
Response {
result: req.a + req.b,
}
}
}

View File

@@ -0,0 +1,22 @@
#![feature(proc_macro_hygiene, decl_macro, plugin)]
#[macro_use]
extern crate rocket;
extern crate rocket_contrib;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
mod fns;
use rocket_contrib::json::Json;
#[post("/", format = "application/json", data = "<req>")]
fn index(req: Json<fns::fns::Request>) -> Json<fns::fns::Response> {
Json(fns::fns::func(req.0))
}
fn main() {
rocket::ignite().mount("/", routes![index]).launch();
}

83
api/call.go Normal file
View File

@@ -0,0 +1,83 @@
package api
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/apex/log"
"github.com/google/uuid"
"github.com/metrue/fx/types"
"github.com/metrue/fx/utils"
)
// Call function directly with given params
func (api *API) Call(file string, param string) error {
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.ServiceFunctionSource{
Language: lang,
Source: string(src),
}
project, err := api.Pack(uuid.New().String(), fn)
if err != nil {
log.Fatalf("Pack Service: %v", err)
return err
}
log.Info("Pack Service: \u2713")
service, err := api.Build(project)
if err != nil {
log.Fatalf("Build Service: %v", err)
return err
}
log.Info("Build Service: \u2713")
if err := api.Run(&service); err != nil {
log.Fatalf("Run Service: %v", err)
return err
}
log.Info("Run Service: \u2713")
params := utils.PairsToParams(strings.Fields(param))
body, err := json.Marshal(params)
if err != nil {
return err
}
// Wait 2 seconds for service startup
time.Sleep(time.Second * 2)
url := fmt.Sprintf("http://%s:%d", service.Instances[0].Host, service.Instances[0].Port)
r, err := http.NewRequest("POST", url, bytes.NewReader(body))
if err != nil {
return err
}
r.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 20 * time.Second}
resp, err := client.Do(r)
if err != nil {
log.Fatalf("Call Service: %v", err)
return err
}
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Call Service: %v", err)
return err
}
log.Info("Call Service: \u2713")
utils.OutputJSON(string(buf))
return nil
}

26
api/container_inspect.go Normal file
View File

@@ -0,0 +1,26 @@
package api
import (
"fmt"
dockerTypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
)
type containerInfo struct {
ID string `json:"Id"`
State dockerTypes.ContainerState `json:"State"`
Image string `json:"Image"`
HostConfig container.HostConfig `json:"HostConfig"`
}
func (api *API) inspect(identify string) (containerInfo, error) {
var info containerInfo
path := fmt.Sprintf("/containers/%s/json", identify)
if err := api.get(path, "", &info); err != nil {
return info, err
}
return info, nil
}

View File

@@ -0,0 +1 @@
package api

48
api/docker_api_version.go Normal file
View File

@@ -0,0 +1,48 @@
package api
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
dockerTypes "github.com/docker/docker/api/types"
)
// Version get version of dockerd server
func Version(endpoint string) (string, error) {
path := "/version"
url := fmt.Sprintf("%s%s", endpoint, path)
if !strings.HasPrefix(url, "http") {
url = "http://" + url
}
req, err := http.NewRequest("GET", url, 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", url, 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
}

View File

@@ -0,0 +1,26 @@
package api
import (
"testing"
gock "gopkg.in/h2non/gock.v1"
)
func TestDockerAPIVersion(t *testing.T) {
defer gock.Off()
dockerRemoteAPI := "http://127.0.0.1:1234"
gock.New(dockerRemoteAPI).
Get("/version").
Reply(200).
JSON(map[string]string{
"ApiVersion": "0.2.1",
})
v, err := Version(dockerRemoteAPI)
if err != nil {
t.Fatal(err)
}
if v != "0.2.1" {
t.Fatalf("should get %s but got %s", "0.2.1", v)
}
}

28
api/down.go Normal file
View File

@@ -0,0 +1,28 @@
package api
import (
"sync"
"github.com/apex/log"
)
// Down destroy services
func (api *API) Down(names []string) error {
var wg sync.WaitGroup
for _, name := range names {
wg.Add(1)
go func(s string) {
if err := api.Stop(s); err != nil {
log.Fatalf("Down Service %s: %v", s, err)
} else {
log.Infof("Down Service %s: \u2713", s)
}
defer wg.Done()
}(name)
}
wg.Wait()
return nil
}

7
api/down_test.go Normal file
View File

@@ -0,0 +1,7 @@
package api
import "testing"
func TestDown(t *testing.T) {
}

View File

@@ -1,106 +0,0 @@
syntax = "proto3";
package api;
import "googleapis/google/api/annotations.proto";
message FunctionMeta {
string Lang = 1;
string Content = 2;
}
message CallRequest {
string Lang = 1;
string Content = 2;
string Params = 3;
}
message CallResponse {
string Error = 1;
string Data = 2;
}
message UpRequest {
repeated FunctionMeta Functions = 1;
}
message DownRequest {
repeated string ID = 1;
}
message ListRequest {
repeated string ID = 1;
}
message UpMsgMeta {
string FunctionID = 1;
string LocalAddress = 2;
string RemoteAddress = 3;
string Error = 4;
}
message UpResponse {
repeated UpMsgMeta Instances = 1;
}
message DownMsgMeta {
string ContainerId = 1;
string ContainerStatus = 2;
string ImageStatus = 3;
string Error = 4;
}
message DownResponse {
repeated DownMsgMeta Instances = 1;
}
message ListItem {
string FunctionID = 1;
string State = 2;
string ServiceURL = 3;
}
message ListResponse {
repeated ListItem Instances = 1;
}
message PingRequest {}
message PingResponse {
string Status = 1;
}
service FxService {
rpc Ping(PingRequest) returns (PingResponse) {
option (google.api.http) = {
get: "/ping"
};
}
rpc Up (UpRequest) returns (UpResponse) {
option (google.api.http) = {
post: "/v1/up"
body: "*"
};
}
rpc Down (DownRequest) returns (DownResponse) {
option (google.api.http) = {
post: "/v1/down"
body: "*"
};
}
rpc List (ListRequest) returns (ListResponse) {
option (google.api.http) = {
post: "/v1/list"
body: "*"
};
}
rpc Call (CallRequest) returns (CallResponse) {
option (google.api.http) = {
post: "/v1/call"
body: "*"
};
}
}

View File

@@ -1,44 +0,0 @@
#!/usr/bin/env bash
HERE=$( cd "$( dirname "${0}" )" && pwd )
ROOT=$( cd ${HERE}/.. && pwd )
PROTOSRC="./fx.proto"
protoc_bin="${ROOT}/third_party/protoc/bin/protoc"
protoc_include="${ROOT}/third_party/protoc/include"
VENDOR="${ROOT}/vendor"
# generate the gRPC code
${protoc_bin} -I/usr/local/include \
-I${protoc_include} \
-I. \
--go_out=plugins=grpc:. \
$PROTOSRC
# generate the JSON interface code
${protoc_bin} -I/usr/local/include \
-I${protoc_include} \
-I. \
-I$GOPATH/src \
-I${VENDOR}/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--go_out=plugins=grpc:. \
$PROTOSRC
# generate the reverse proxy
${protoc_bin} -I/usr/local/include \
-I${protoc_include} \
-I. \
-I$GOPATH/src \
-I${VENDOR}/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--grpc-gateway_out=logtostderr=true:. \
$PROTOSRC
# generate the swagger definitions
${protoc_bin} -I/usr/local/include \
-I. \
-I${protoc_include} \
-I$GOPATH/src \
-I${VENDOR}/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--swagger_out=logtostderr=true:../swagger \
$PROTOSRC

View File

@@ -1 +0,0 @@
../third_party/googleapis/google

View File

@@ -1 +0,0 @@
../third_party/googleapis

30
api/image_pull.go Normal file
View File

@@ -0,0 +1,30 @@
package api
import (
"bufio"
"fmt"
"net/http"
"time"
)
func (api *API) pull(name string) error {
path := fmt.Sprintf("/%s/images/create?fromImage=%s&tag=latest", api.version, name)
url := fmt.Sprintf("http://%s%s", api.endpoint, path)
req, err := http.NewRequest("POST", url, nil)
client := &http.Client{Timeout: 120 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
fmt.Printf("%s\n", scanner.Text())
}
if err := scanner.Err(); err != nil {
return err
}
return nil
}

6
api/image_pull_test.go Normal file
View File

@@ -0,0 +1,6 @@
package api
import "testing"
func TestPull(t *testing.T) {
}

51
api/images/java/pom.xml Normal file
View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>fx-app-java</artifactId>
<packaging>jar</packaging>
<version>0.1.0</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>fx.app</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>io.javalin</groupId>
<artifactId>javalin</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20171018</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,4 +1,4 @@
FROM julia:latest
FROM julia:0.7
COPY . /app
@@ -6,4 +6,4 @@ RUN apt-get update && apt-get install -y gcc apt-utils unzip make libhttp-parser
RUN julia /app/deps.jl
CMD julia /app/app.jl
EXPOSE 3000
EXPOSE 3000

View File

@@ -5,8 +5,8 @@ const func = require('./fx');
const app = new Koa();
app.use(bodyParser());
app.use(ctx => {
const msg = func(ctx.request.body);
ctx.body = msg;
const msg = func(ctx.request.body);
ctx.body = msg;
});
app.listen(3000);

55
api/init.go Normal file
View File

@@ -0,0 +1,55 @@
package api
import (
"os/exec"
"sync"
"github.com/apex/log"
)
// Init init a host to be a fx running
func (api *API) Init() error {
// enable docker remote api
// docker run -d -v /var/run/docker.sock:/var/run/docker.sock -p 127.0.0.1:1234:1234 bobrik/socat TCP-LISTEN:1234,fork UNIX-CONNECT:/var/run/docker.sock
cmd := exec.Command(
"docker",
"run",
"-d",
"-v",
"/var/run/docker.sock:/var/run/docker.sock",
"-p",
"127.0.0.1:1234:1234",
"bobrik/socat",
"TCP-LISTEN:1234,fork",
"UNIX-CONNECT:/var/run/docker.sock",
)
stdoutStderr, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("Initialize Environment: %v", err)
return err
}
log.Infof("Initialize Environment: \u2713 %s", stdoutStderr)
baseImages := []string{
"metrue/fx-java-base",
"metrue/fx-julia-base",
"metrue/fx-python-base",
"metrue/fx-node-base",
"metrue/fx-d-base",
}
var wg sync.WaitGroup
for _, image := range baseImages {
wg.Add(1)
go func(img string) {
if err := api.pull(img); err != nil {
log.Fatalf("Pulling %s failed", img)
} else {
log.Infof("Pull %s ok", img)
}
wg.Done()
}(image)
}
return nil
}

20
api/list.go Normal file
View File

@@ -0,0 +1,20 @@
package api
import (
"github.com/apex/log"
"github.com/metrue/fx/utils"
)
// List services
func (api *API) List(name string) error {
services, err := api.list(name)
if err != nil {
log.Fatalf("List Services: %v", err)
return err
}
for _, service := range services {
utils.OutputJSON(service)
}
return nil
}

50
api/pack.go Normal file
View File

@@ -0,0 +1,50 @@
package api
import (
"fmt"
"path/filepath"
"strings"
"github.com/metrue/fx/types"
)
func isHandler(lang string, name string) bool {
basename := filepath.Base(name)
nameWithoutExt := strings.TrimSuffix(basename, filepath.Ext(basename))
return nameWithoutExt == "fx" || nameWithoutExt == "Fx" // Fx is for Java
}
// Pack pack a single function source code to be project
func (api *API) Pack(serviceName string, fn types.ServiceFunctionSource) (types.Project, error) {
var files []types.ProjectSourceFile
for _, name := range api.box.List() {
prefix := fmt.Sprintf("%s/", fn.Language)
if strings.HasPrefix(name, prefix) {
content, err := api.box.FindString(name)
if err != nil {
return types.Project{}, err
}
// if preset's file is handler function of project, replace it with give one
if isHandler(fn.Language, name) {
files = append(files, types.ProjectSourceFile{
Path: strings.Replace(name, prefix, "", 1),
Body: fn.Source,
IsHandler: true,
})
} else {
files = append(files, types.ProjectSourceFile{
Path: strings.Replace(name, prefix, "", 1),
Body: content,
IsHandler: false,
})
}
}
}
return types.Project{
Name: serviceName,
Files: files,
Language: fn.Language,
}, nil
}

58
api/pack_test.go Normal file
View File

@@ -0,0 +1,58 @@
package api
import (
"testing"
"github.com/metrue/fx/types"
)
func TestPacker(t *testing.T) {
api := NewWithDockerRemoteAPI("127.0.0.1:1234", "0.2.1")
mockSource := `
module.exports = ({a, b}) => {
return a + b
}
`
fn := types.ServiceFunctionSource{
Language: "node",
Source: mockSource,
}
serviceName := "service-mock"
project, err := api.Pack(serviceName, fn)
if err != nil {
t.Fatal(err)
}
if project.Name != serviceName {
t.Fatalf("should get %s but got %s", serviceName, project.Name)
}
if project.Language != "node" {
t.Fatal("incorrect Language")
}
if len(project.Files) != 3 {
t.Fatal("node project should have 3 files")
}
for _, file := range project.Files {
if file.Path == "fx.js" {
if file.IsHandler == false {
t.Fatal("fx.js should be handler")
}
if file.Body != mockSource {
t.Fatalf("should get %s but got %v", mockSource, file.Body)
}
} else if file.Path == "Dockerfile" {
if file.IsHandler == true {
t.Fatalf("should get %v but got %v", false, file.IsHandler)
}
} else {
if file.IsHandler == true {
t.Fatalf("should get %v but %v", false, file.IsHandler)
}
}
}
}

115
api/service_build.go Normal file
View File

@@ -0,0 +1,115 @@
package api
import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"time"
"github.com/google/go-querystring/query"
"github.com/google/uuid"
"github.com/metrue/fx/types"
"github.com/metrue/fx/utils"
)
func makeTar(project types.Project, tarFilePath string) error {
dir, err := ioutil.TempDir("/tmp", "fx-build-dir")
if err != nil {
return err
}
defer os.RemoveAll(dir)
for _, file := range project.Files {
tmpfn := filepath.Join(dir, file.Path)
if err := utils.EnsureFile(tmpfn); err != nil {
return err
}
if err := ioutil.WriteFile(tmpfn, []byte(file.Body), 0666); err != nil {
return err
}
}
return utils.TarDir(dir, tarFilePath)
}
// Build build a project
func (api *API) Build(project types.Project) (types.Service, error) {
tarDir, err := ioutil.TempDir("/tmp", "fx-tar")
if err != nil {
return types.Service{}, err
}
defer os.RemoveAll(tarDir)
imageID := uuid.New().String()
tarFilePath := filepath.Join(tarDir, fmt.Sprintf("%s.tar", imageID))
if err := makeTar(project, tarFilePath); err != nil {
return types.Service{}, err
}
dockerBuildContext, err := os.Open(tarFilePath)
if err != nil {
return types.Service{}, err
}
defer dockerBuildContext.Close()
type buildQuery struct {
Labels string `url:"labels,omitempty"`
Tags string `url:"t,omitempty"`
Dockerfile string `url:"dockerfile,omitempty"`
}
// Apply default labels
labelsJSON, _ := json.Marshal(
map[string]string{
"belong-to": "fx",
},
)
q := buildQuery{
Tags: imageID,
Labels: string(labelsJSON),
Dockerfile: "Dockerfile",
}
qs, err := query.Values(q)
if err != nil {
return types.Service{}, err
}
if err != nil {
return types.Service{}, err
}
path := "/build"
url := fmt.Sprintf("%s%s?%s", api.endpoint, path, qs.Encode())
req, err := http.NewRequest("POST", url, dockerBuildContext)
if err != nil {
return types.Service{}, err
}
req.Header.Set("Content-Type", "application/x-tar")
client := &http.Client{Timeout: 120 * time.Second}
resp, err := client.Do(req)
if err != nil {
return types.Service{}, err
}
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
// TODO Maybe need log something out
// fmt.Printf("%s\n", scanner.Text())
}
if err := scanner.Err(); err != nil {
return types.Service{}, err
}
return types.Service{
Name: project.Name,
Status: types.ServiceStatusInit,
Image: imageID,
}, nil
}

157
api/service_build_test.go Normal file
View File

@@ -0,0 +1,157 @@
package api
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"testing"
"github.com/metrue/fx/types"
gock "gopkg.in/h2non/gock.v1"
)
func TestMakeTar(t *testing.T) {
serviceName := "mock-service-abc"
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,
},
},
}
tarDir, err := ioutil.TempDir("/tmp", "fx-tar")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tarDir)
tarFilePath := filepath.Join(tarDir, fmt.Sprintf("%s.tar", serviceName))
if err := makeTar(project, tarFilePath); err != nil {
t.Fatal(err)
}
file, err := os.Open(tarFilePath)
if err != nil {
t.Fatal(err)
}
stat, err := file.Stat()
if err != nil {
t.Fatal(err)
}
if stat.Name() != serviceName+".tar" {
t.Fatalf("should get %s but got %s", serviceName+".tar", stat.Name())
}
if stat.Size() <= 0 {
t.Fatalf("tarfile invalid: size %d", stat.Size())
}
}
func TestBuild(t *testing.T) {
defer gock.Off()
dockerRemoteAPI := "http://127.0.0.1:1234"
version := "0.2.1"
gock.New(dockerRemoteAPI).
Post("/v0.2.1/build").
AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) {
return true, nil
}).
Reply(200).
JSON(map[string]string{
"stream": "Step 1/5...",
})
serviceName := "mock-service-abc"
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,
},
},
}
api := NewWithDockerRemoteAPI(dockerRemoteAPI, version)
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 service.Image == "" {
t.Fatal("service image should not be empty")
}
}

122
api/service_run.go Normal file
View File

@@ -0,0 +1,122 @@
package api
import (
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/go-connections/nat"
"github.com/metrue/fx/types"
"github.com/phayes/freeport"
)
type healtCheck struct {
Test []string `json:"Test"`
Interval float64 `json:"Interval"`
Timeout float64 `json:"Timeout"`
Retries int64 `json:"Retries"`
StartPeriod float64 `json:"StartPeriod"`
}
// ContainerCreateRequestPayload request paylaod
type ContainerCreateRequestPayload struct {
Hostname string `json:"Hostname,omitempty"`
Domainname string `json:"Domainname,omitempty"`
User string `json:"User,omitempty"`
AttachStdin bool `json:"AttachStdin,omitempty"`
AttachStdout bool `json:"AttachStdout,omitempty"`
AttachStderr bool `json:"AttachStderr,omitempty"`
Tty bool `json:"Tty,omitempty"`
OpenStdin bool `json:"OpenStdin,omitempty"`
StdinOnce bool `json:"StdinOnce,omitempty"`
Env []string `json:"Env,omitempty"`
Cmd []string `json:"Cmd,omitempty"`
Entrypoint string `json:"Entrypoint,omitempty"`
Image string `json:"Image,omitempty"`
Labels map[string]string `json:"Labels,omitempty"`
Volumes map[string]interface{} `json:"Volumes,omitempty"`
Healthcheck healtCheck `json:"Healthcheck,omitempty"`
WorkingDir string `json:"WorkingDir,omitempty"`
NetworkDisabled bool `json:"NetworkDisabled,omitempty"`
MacAddress string `json:"MacAddress,omitempty"`
ExposedPorts nat.PortSet `json:"ExposedPorts,omitempty"`
StopSignal string `json:"StopSignal,omitempty"`
HostConfig container.HostConfig `json:"HostConfig,omitempty"`
NetworkingConfig network.NetworkingConfig `json:"NetworkingConfig,omitempty"`
}
// Run a service
func (api *API) Run(service *types.Service) error {
port, err := freeport.GetFreePort()
if err != nil {
return err
}
req := ContainerCreateRequestPayload{
Image: service.Image,
Labels: map[string]string{},
ExposedPorts: nat.PortSet{
"3000/tcp": struct{}{},
},
HostConfig: container.HostConfig{
AutoRemove: true,
PortBindings: nat.PortMap{
"3000/tcp": []nat.PortBinding{
{
HostIP: types.DefaultHost,
HostPort: fmt.Sprintf("%d", port),
},
},
},
},
}
body, err := json.Marshal(req)
if err != nil {
return err
}
path := fmt.Sprintf("/containers/create?name=%s", service.Name)
type containerCreateResponse struct {
ID string `json:"Id"`
Warnings []string `json:"Warnings"`
}
var res containerCreateResponse
err = api.post(path, body, 201, &res)
if err != nil {
return err
}
if res.ID == "" {
return fmt.Errorf("container id is missing")
}
path = fmt.Sprintf("/containers/%s/start", res.ID)
url := fmt.Sprintf("%s%s", api.endpoint, path)
request, err := http.NewRequest("POST", url, nil)
if err != nil {
return err
}
client := &http.Client{Timeout: 20 * time.Second}
_, err = client.Do(request)
if err != nil {
return err
}
info, err := api.inspect(service.Name)
if err != nil {
return err
}
instance := types.Instance{
ID: info.ID,
Host: info.HostConfig.PortBindings["3000/tcp"][0].HostIP,
Port: port,
State: info.State.Status,
}
service.Instances = append(service.Instances, instance)
return nil
}

47
api/service_run_test.go Normal file
View File

@@ -0,0 +1,47 @@
package api
import (
"net/http"
"testing"
"github.com/metrue/fx/types"
gock "gopkg.in/h2non/gock.v1"
)
func TestServiceRun(t *testing.T) {
defer gock.Off()
dockerRemoteAPI := "http://127.0.0.1:1234"
version := "0.2.1"
service := types.Service{
Name: "a-mock-service",
Image: "a-mock-image-id",
}
mockContainerID := "mock-container-id"
gock.New(dockerRemoteAPI).
Post("/v0.2.1/containers").
AddMatcher(func(req *http.Request, ereq *gock.Request) (m bool, e error) {
// TODO multiple matching not supported by gock
if req.URL.String() == dockerRemoteAPI+"/v0.2.1/containers/"+mockContainerID+"/start" {
return true, nil
} else if req.URL.String() == dockerRemoteAPI+"/v0.2.1/containers/create?name="+service.Name {
return true, nil
}
return false, nil
}).
Reply(201).
JSON(map[string]interface{}{
"Id": mockContainerID,
"Warnings": []string{},
})
api := NewWithDockerRemoteAPI(dockerRemoteAPI, version)
// FIXME
if err := api.Run(&service); err == nil {
t.Fatal(err)
}
}

24
api/stop.go Normal file
View File

@@ -0,0 +1,24 @@
package api
import (
"fmt"
"net/http"
"time"
)
// Stop a container by name
func (api *API) Stop(name string) error {
path := fmt.Sprintf("/containers/%s/stop", name)
url := fmt.Sprintf("%s%s", api.endpoint, path)
request, err := http.NewRequest("POST", url, nil)
if err != nil {
return err
}
client := &http.Client{Timeout: 20 * time.Second}
_, err = client.Do(request)
if err != nil {
return err
}
return nil
}

23
api/stop_test.go Normal file
View File

@@ -0,0 +1,23 @@
package api
import (
"testing"
gock "gopkg.in/h2non/gock.v1"
)
func TestStop(t *testing.T) {
defer gock.Off()
dockerRemoteAPI := "http://127.0.0.1:1234"
version := "0.2.1"
api := NewWithDockerRemoteAPI(dockerRemoteAPI, version)
mockServiceName := "mock-service-name"
gock.New(dockerRemoteAPI).
Post("/v0.2.1/containers/" + mockServiceName + "/stop").
Reply(204)
if err := api.Stop(mockServiceName); err != nil {
t.Fatal(err)
}
}

47
api/up.go Normal file
View File

@@ -0,0 +1,47 @@
package api
import (
"io/ioutil"
"github.com/apex/log"
"github.com/metrue/fx/types"
"github.com/metrue/fx/utils"
)
// Up up a source code of function to be a service
func (api *API) Up(name string, file string) error {
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.ServiceFunctionSource{
Language: lang,
Source: string(src),
}
project, err := api.Pack(name, fn)
if err != nil {
log.Fatalf("Pack Service: %v", err)
return err
}
log.Info("Pack Service: \u2713")
service, err := api.Build(project)
if err != nil {
log.Fatalf("Build Service: %v", err)
return err
}
log.Info("Build Service: \u2713")
if err := api.Run(&service); err != nil {
log.Fatalf("Run Service: %v", err)
return err
}
log.Info("Run Service: \u2713")
return nil
}

View File

@@ -1,52 +0,0 @@
package commands
import (
"context"
"fmt"
"io/ioutil"
"github.com/metrue/fx/api"
"github.com/metrue/fx/pkg/client"
"github.com/metrue/fx/pkg/utils"
)
type CallOutput struct {
Error string `json:"error"`
Message string `json:"message"`
}
func InvokeCallRequest(address string, function string, params string) (*api.CallResponse, error) {
data, err := ioutil.ReadFile(function)
if err != nil {
return nil, err
}
req := &api.CallRequest{
Lang: utils.GetLangFromFileName(function),
Content: string(data),
Params: params,
}
client, conn, err := client.NewClient(address)
if err != nil {
fmt.Println(client, conn, err)
return nil, err
}
defer conn.Close()
ctx := context.Background()
res, err := client.Call(ctx, req)
if err != nil {
return nil, err
}
return res, nil
}
func Call(address string, function string, params string) error {
res, err := InvokeCallRequest(address, function, params)
if err != nil {
return nil
}
return utils.OutputJSON(res)
}

View File

@@ -1,47 +0,0 @@
package commands_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
. "github.com/metrue/fx/commands"
"github.com/metrue/fx/pkg/server"
"github.com/stretchr/testify/assert"
)
func TestCall(t *testing.T) {
addr := "localhost:23451"
s := server.NewFxServiceServer(addr)
go func() {
s.Start()
}()
time.Sleep(2 * time.Second)
defer s.Stop()
code := `
module.exports = (input) => {
console.log("acc")
return parseInt(input.a, 10) + parseInt(input.b, 10)
}
`
content := []byte(code)
tmpDir, err := ioutil.TempDir("", "fx")
assert.Nil(t, err)
assert.NotEqual(t, "", tmpDir)
defer os.RemoveAll(tmpDir)
tmpfn := filepath.Join(tmpDir, "func.js")
err = ioutil.WriteFile(tmpfn, content, 0666)
assert.Nil(t, err)
params := "a=1 b=1"
err = Call(addr, tmpfn, params)
assert.Nil(t, err)
time.Sleep(2 * time.Second)
}

View File

@@ -1,36 +0,0 @@
package commands
import (
"context"
"github.com/metrue/fx/api"
"github.com/metrue/fx/pkg/client"
"github.com/metrue/fx/pkg/utils"
)
func InvokeDownRequest(address string, functions []string) (*api.DownResponse, error) {
client, conn, err := client.NewClient(address)
if err != nil {
return nil, err
}
defer conn.Close()
ctx := context.Background()
req := &api.DownRequest{
ID: functions,
}
res, err := client.Down(ctx, req)
if err != nil {
return nil, err
}
return res, nil
}
func Down(address string, functions []string) error {
res, err := InvokeDownRequest(address, functions)
if err != nil {
return err
}
return utils.OutputJSON(res)
}

View File

@@ -1,27 +0,0 @@
package commands_test
import (
"testing"
"time"
. "github.com/metrue/fx/commands"
"github.com/metrue/fx/pkg/server"
"github.com/stretchr/testify/assert"
)
func TestDown(t *testing.T) {
addr := "localhost:23451"
functions := []string{"id-should-not-exist"}
s := server.NewFxServiceServer(addr)
go func() {
s.Start()
}()
time.Sleep(2 * time.Second)
err := Down(addr, functions)
assert.Nil(t, err)
s.Stop()
time.Sleep(2 * time.Second)
}

View File

@@ -1,30 +0,0 @@
package commands
import (
"context"
"github.com/metrue/fx/api"
"github.com/metrue/fx/pkg/client"
"github.com/metrue/fx/pkg/utils"
)
// List lists all running function services
func List(address string, functions []string) error {
client, conn, err := client.NewClient(address)
if err != nil {
return err
}
defer conn.Close()
ctx := context.Background()
req := &api.ListRequest{
ID: functions,
}
res, err := client.List(ctx, req)
if err != nil {
return err
}
return utils.OutputJSON(res)
}

View File

@@ -1,27 +0,0 @@
package commands_test
import (
"testing"
"time"
. "github.com/metrue/fx/commands"
"github.com/metrue/fx/pkg/server"
"github.com/stretchr/testify/assert"
)
func TestList(t *testing.T) {
addr := "localhost:23453"
s := server.NewFxServiceServer(addr)
go func() {
s.Start()
}()
time.Sleep(2 * time.Second)
functions := []string{"*"}
err := List(addr, functions)
assert.Nil(t, err)
s.Stop()
time.Sleep(2 * time.Second)
}

View File

@@ -1,28 +0,0 @@
package commands
import (
"context"
"github.com/metrue/fx/api"
"github.com/metrue/fx/pkg/client"
"github.com/metrue/fx/pkg/utils"
)
func Status(address string) error {
client, conn, err := client.NewClient(address)
if err != nil {
return err
}
defer conn.Close()
ctx := context.Background()
req := &api.PingRequest{}
_, err = client.Ping(ctx, req)
if err != nil {
return err
}
return utils.OutputJSON(map[string]string{
"status": "ok",
"server": address,
})
}

View File

@@ -1,25 +0,0 @@
package commands_test
import (
"testing"
"time"
. "github.com/metrue/fx/commands"
"github.com/metrue/fx/pkg/server"
"github.com/stretchr/testify/assert"
)
func TestStatus(t *testing.T) {
addr := "localhost:23453"
s := server.NewFxServiceServer(addr)
go func() {
s.Start()
}()
time.Sleep(2 * time.Second)
err := Status(addr)
assert.Nil(t, err)
s.Stop()
time.Sleep(2 * time.Second)
}

View File

@@ -1,53 +0,0 @@
package commands
import (
"context"
"io/ioutil"
"github.com/metrue/fx/api"
"github.com/metrue/fx/pkg/client"
"github.com/metrue/fx/pkg/utils"
)
func InvokeUpRequest(address string, functions []string) (*api.UpResponse, error) {
var funcList []*api.FunctionMeta
for _, function := range functions {
data, err := ioutil.ReadFile(function)
if err != nil {
return nil, err
}
funcMeta := &api.FunctionMeta{
Lang: utils.GetLangFromFileName(function),
Content: string(data),
}
funcList = append(funcList, funcMeta)
}
req := &api.UpRequest{
Functions: funcList,
}
client, conn, err := client.NewClient(address)
if err != nil {
return nil, err
}
defer conn.Close()
ctx := context.Background()
res, err := client.Up(ctx, req)
if err != nil {
return nil, err
}
return res, nil
}
func Up(address string, functions []string) error {
res, err := InvokeUpRequest(address, functions)
if err != nil {
return err
}
utils.OutputJSON(res)
return nil
}

View File

@@ -1,11 +0,0 @@
package commands_test
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestUp(t *testing.T) {
assert.Equal(t, nil, nil)
}

View File

@@ -1,8 +0,0 @@
package commands
import "github.com/metrue/fx/config"
func Use(address string) error {
config := config.GetConfig()
return config.SetHost(address)
}

View File

@@ -1,21 +0,0 @@
package commands_test
import (
"testing"
. "github.com/metrue/fx/commands"
"github.com/metrue/fx/config"
"github.com/stretchr/testify/assert"
)
func TestUse(t *testing.T) {
config.CONFIG = "/tmp/fx.json"
addr := "a.b.c.d"
err := Use(addr)
assert.Nil(t, err)
assert.Equal(t, config.GetHttpServerAddr(), "a.b.c.d:30080")
assert.Equal(t, config.GetGrpcEndpoint(), "a.b.c.d:50000")
}

View File

@@ -1,90 +0,0 @@
package config
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"github.com/metrue/fx/pkg/utils"
)
type FxConfig struct {
Http string `json:"http"`
Grpc string `json:"grpc"`
}
var CONFIG = path.Join(os.Getenv("HOME"), ".fx/config.json")
var HTTP_PORT = 30080
var GRPC_PORT = 50000
var DEFAULT_CONFIG = &FxConfig{
Http: fmt.Sprintf("localhost:%d", HTTP_PORT),
Grpc: fmt.Sprintf("localhost:%d", GRPC_PORT),
}
func GetConfig() *FxConfig {
_, err := os.Stat(CONFIG)
if err != nil {
DEFAULT_CONFIG.save()
return DEFAULT_CONFIG
}
raw, err := ioutil.ReadFile(CONFIG)
if err != nil {
panic(err)
}
var c FxConfig
json.Unmarshal(raw, &c)
if len(c.Http) > 0 && len(c.Grpc) > 0 {
return &c
}
return DEFAULT_CONFIG
}
func (c *FxConfig) save() error {
os.Remove(CONFIG)
utils.EnsureFile(CONFIG)
configFile, err := os.OpenFile(CONFIG, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
return err
}
configContent, err := json.Marshal(c)
if err != nil {
return err
}
_, err = configFile.Write(configContent)
if err != nil {
return err
}
return nil
}
func (c *FxConfig) SetHost(addr string) error {
c.Http = fmt.Sprintf("%s:%d", addr, HTTP_PORT)
c.Grpc = fmt.Sprintf("%s:%d", addr, GRPC_PORT)
err := c.save()
if err != nil {
panic(err)
}
return err
}
func GetGrpcEndpoint() string {
c := GetConfig()
return c.Grpc
}
func GetHttpServerAddr() string {
c := GetConfig()
return c.Http
}

View File

@@ -1,44 +0,0 @@
package config_test
import (
"os"
"testing"
. "github.com/metrue/fx/config"
"github.com/stretchr/testify/assert"
)
func TestGet(t *testing.T) {
c := GetConfig()
assert.NotNil(t, c)
assert.Equal(t, GetHttpServerAddr(), "localhost:30080")
assert.Equal(t, GetGrpcEndpoint(), "localhost:50000")
}
func TestSetHost(t *testing.T) {
CONFIG = "/tmp/fx.config.json"
c := GetConfig()
assert.Equal(t, GetHttpServerAddr(), "localhost:30080")
assert.Equal(t, GetGrpcEndpoint(), "localhost:50000")
err := c.SetHost("124.124.124.124")
assert.Nil(t, err)
assert.Equal(t, "124.124.124.124:30080", GetHttpServerAddr())
assert.Equal(t, "124.124.124.124:50000", GetGrpcEndpoint())
}
func cleanup() {
CONFIG = "/tmp/fx.config.json"
os.Remove(CONFIG)
}
func TestMain(m *testing.M) {
cleanup()
m.Run()
// cleanup()
}

122
env/env.go vendored Normal file
View File

@@ -0,0 +1,122 @@
package env
import (
"encoding/json"
"os/exec"
"sync"
"github.com/apex/log"
dockerTypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
)
// DockerRemoteAPIEndpoint docker remote api
const DockerRemoteAPIEndpoint = "127.0.0.1:1234"
type containerInfo struct {
ID string `json:"Id"`
State dockerTypes.ContainerState `json:"State"`
Image string `json:"Image"`
HostConfig container.HostConfig `json:"HostConfig"`
}
func proxyDockerSock() error {
name := "docker-sock-proxy-for-fx"
cmd := exec.Command(
"docker",
"inspect",
name,
)
var infos []containerInfo
stdoutStderr, err := cmd.CombinedOutput()
if err := json.Unmarshal(stdoutStderr, &infos); err != nil {
return err
}
const containerStateCreated = "created"
const containerStateRestarting = "restarting"
const containerStateRunning = "running"
const containerStatePaused = "paused"
const containerStateExited = "exited"
state := infos[0].State.Status
if state == containerStateRestarting ||
state == containerStateRunning { // FIXME should wait for it ready
return nil
}
if state == containerStateCreated ||
state == containerStatePaused ||
state == containerStateExited {
cmd := exec.Command(
"docker",
"start",
name,
)
_, err = cmd.CombinedOutput()
if err != nil {
return err
}
return nil
}
// enable docker remote api
// docker run -d -v /var/run/docker.sock:/var/run/docker.sock -p 127.0.0.1:1234:1234 bobrik/socat TCP-LISTEN:1234,fork UNIX-CONNECT:/var/run/docker.sock
cmd = exec.Command(
"docker",
"run",
"--name="+name,
"-d",
"-v",
"/var/run/docker.sock:/var/run/docker.sock",
"-p",
DockerRemoteAPIEndpoint+":1234",
"bobrik/socat",
"TCP-LISTEN:1234,fork",
"UNIX-CONNECT:/var/run/docker.sock",
)
_, err = cmd.CombinedOutput()
if err != nil {
return err
}
return nil
}
// Init init a host to make fx runnable
func Init() error {
if err := proxyDockerSock(); err != nil {
log.Fatalf("Proxy Docker Remote API Sock Failed: %v", err)
return err
}
log.Info("Proxy Docker Remote API Sock: \u2713")
baseImages := []string{
"metrue/fx-java-base",
"metrue/fx-julia-base",
"metrue/fx-python-base",
"metrue/fx-node-base",
"metrue/fx-d-base",
}
var wg sync.WaitGroup
for _, image := range baseImages {
wg.Add(1)
go func(img string) {
cmd := exec.Command(
"docker",
"pull",
img,
)
stdoutStderr, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("Pulling %s failed: %v", img, string(stdoutStderr))
} else {
log.Infof("%s Pulled: \u2713", img)
}
wg.Done()
}(image)
}
wg.Wait()
return nil
}

9
env/env_test.go vendored Normal file
View File

@@ -0,0 +1,9 @@
package env
import "testing"
func TestInit(t *testing.T) {
if err := Init(); err != nil {
t.Fatal(err)
}
}

104
fx.go
View File

@@ -1,28 +1,44 @@
package main
import (
"log"
"os"
"strings"
"github.com/metrue/fx/commands"
"github.com/metrue/fx/config"
"github.com/metrue/fx/server"
"github.com/apex/log"
"github.com/google/uuid"
"github.com/metrue/fx/api"
"github.com/metrue/fx/env"
"github.com/urfave/cli"
)
func fx() *api.API {
endpoint := "http://" + env.DockerRemoteAPIEndpoint
version, err := api.Version(endpoint)
if err != nil {
panic(err)
}
return api.NewWithDockerRemoteAPI(endpoint, version)
}
func main() {
app := cli.NewApp()
app.Name = "fx"
app.Usage = "make function as a service"
app.Version = "0.2.2"
app.Usage = "makes function as a service"
app.Version = "0.3.0"
app.Commands = []cli.Command{
{
Name: "serve",
Usage: "start fx server on current host",
Name: "init",
Usage: "initialize fx running enviroment",
Action: func(c *cli.Context) error {
return server.Start(true)
log.Info("Init Enviroment ....")
err := env.Init()
if err != nil {
log.Fatalf("Init Enviroment%v", err)
} else {
log.Info("Init Enviroment: \u2713")
}
return err
},
},
{
@@ -31,54 +47,31 @@ func main() {
ArgsUsage: "[func.go func.js func.py func.rb ...]",
Flags: []cli.Flag{
cli.StringFlag{
Name: "host, H",
Usage: "fx server host, default is localhost",
Name: "name, n",
Usage: "service name",
},
},
Action: func(c *cli.Context) error {
host := c.String("host")
if host == "" {
host = config.GetGrpcEndpoint()
name := c.String("name")
if name == "" {
name = uuid.New().String()
}
functionSources := c.Args()
return commands.Up(host, functionSources)
return fx().Up(name, c.Args().First())
},
},
{
Name: "down",
Usage: "destroy a function or a group of functions",
ArgsUsage: "[id1, id2, ...]",
Flags: []cli.Flag{
cli.StringFlag{
Name: "host, H",
Usage: "fx server host, default is localhost",
},
},
Usage: "destroy a service",
ArgsUsage: "[service 1, service 2, ....]",
Action: func(c *cli.Context) error {
host := c.String("host")
if host == "" {
host = config.GetGrpcEndpoint()
}
functions := c.Args()
return commands.Down(host, functions)
return fx().Down(c.Args())
},
},
{
Name: "list",
Usage: "list deployed services",
Flags: []cli.Flag{
cli.StringFlag{
Name: "host, H",
Usage: "fx server host, default is localhost",
},
},
Action: func(c *cli.Context) error {
host := c.String("host")
if host == "" {
host = config.GetGrpcEndpoint()
}
functions := c.Args()
return commands.List(host, functions)
return fx().List(c.Args().First())
},
},
{
@@ -91,37 +84,14 @@ func main() {
},
},
Action: func(c *cli.Context) error {
host := c.String("host")
if host == "" {
host = config.GetGrpcEndpoint()
}
params := strings.Join(c.Args()[1:], " ")
functions := c.Args()[0]
return commands.Call(host, functions, params)
},
},
{
Name: "use",
Usage: "set target deploy server address, default is localhost",
Action: func(c *cli.Context) error {
return commands.Use(c.Args().First())
},
},
{
Name: "status",
Usage: "show fx status",
Action: func(c *cli.Context) error {
host := c.String("host")
if host == "" {
host = config.GetGrpcEndpoint()
}
return commands.Status(host)
return fx().Call(c.Args().First(), params)
},
},
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
log.Fatalf("fx startup with fatal: %v", err)
}
}

View File

@@ -1,9 +0,0 @@
FROM r-base
COPY . /usr/local/src/fx
WORKDIR /usr/local/src/fx
RUN R -f packages.R
EXPOSE 3000
CMD ["Rscript", "app.R"]

View File

@@ -1,11 +0,0 @@
library(jug)
library(jsonlite)
source('./fx.R')
jug() %>%
post("/", function(req, res, err) {
input <- fromJSON(req$body)
fx(input)
}) %>%
simple_error_handler_json() %>%
serve_it(port = 3000)

View File

@@ -1,3 +0,0 @@
fx <- function(input) {
return (strtoi(input$a) + strtoi(input$b))
}

View File

@@ -1 +0,0 @@
install.packages(c("jug", "jsonlite"), repos="http://cran.us.r-project.org", dependencies=TRUE)

View File

@@ -1,108 +0,0 @@
package bundler
import (
"io/ioutil"
"path"
"path/filepath"
"strings"
"github.com/metrue/fx/common"
"github.com/metrue/fx/pkg/utils"
)
var funcNames = map[string]string{
"go": "/fx.go",
"node": "/fx.js",
"ruby": "/fx.rb",
"python": "/fx.py",
"php": "/fx.php",
"julia": "/fx.jl",
"java": "/src/main/java/fx/Fx.java",
"d": "/fx.d",
}
var assetsMap = map[string][]string{
"go": {
"assets/dockerfiles/fx/go/Dockerfile",
"assets/dockerfiles/fx/go/app.go",
"assets/dockerfiles/fx/go/fx.go",
},
"java": {
"assets/dockerfiles/fx/java/Dockerfile",
"assets/dockerfiles/fx/java/pom.xml",
"assets/dockerfiles/fx/java/src/main/java/fx/Fx.java",
"assets/dockerfiles/fx/java/src/main/java/fx/app.java",
},
"julia": {
"assets/dockerfiles/fx/julia/Dockerfile",
"assets/dockerfiles/fx/julia/REQUIRE",
"assets/dockerfiles/fx/julia/app.jl",
"assets/dockerfiles/fx/julia/deps.jl",
"assets/dockerfiles/fx/julia/fx.jl",
},
"node": {
"assets/dockerfiles/fx/node/Dockerfile",
"assets/dockerfiles/fx/node/app.js",
"assets/dockerfiles/fx/node/fx.js",
},
"php": {
"assets/dockerfiles/fx/php/Dockerfile",
"assets/dockerfiles/fx/php/fx.php",
"assets/dockerfiles/fx/php/index.php",
},
"python": {
"assets/dockerfiles/fx/python/Dockerfile",
"assets/dockerfiles/fx/python/app.py",
"assets/dockerfiles/fx/python/fx.py",
},
"ruby": {
"assets/dockerfiles/fx/ruby/Dockerfile",
"assets/dockerfiles/fx/ruby/app.rb",
"assets/dockerfiles/fx/ruby/fx.rb",
},
"d": {
"assets/dockerfiles/fx/d/Dockerfile",
"assets/dockerfiles/fx/d/app.d",
"assets/dockerfiles/fx/d/fx.d",
"assets/dockerfiles/fx/d/arsd/cgi.d",
},
}
func removePrefix(lang string, filename string) (name string) {
prefix := "assets/dockerfiles/fx" + "/" + lang + "/"
return strings.Split(filename, prefix)[1]
}
func isFxFuncSource(lang string, name string) (ret bool) {
basename := filepath.Base(name)
nameWithoutExt := strings.TrimSuffix(basename, filepath.Ext(basename))
return nameWithoutExt == "fx" || nameWithoutExt == "Fx" // Fx is for Java
}
//Bundle Prepare a container base image and insert the function body
func Bundle(dir string, lang string, body []byte) (err error) {
names := assetsMap[lang]
err = nil
for _, name := range names {
data, assetErr := common.Asset(name)
if assetErr != nil {
err = assetErr
}
name = removePrefix(lang, name)
targetPath := path.Join(dir, name)
dir := filepath.Dir(targetPath)
utils.EnsurerDir(dir)
if isFxFuncSource(lang, targetPath) {
data = body
}
writeErr := ioutil.WriteFile(targetPath, data, 0644)
if writeErr != nil {
err = writeErr
}
}
return err
}

View File

@@ -1,42 +0,0 @@
package bundler
import (
"io/ioutil"
"os"
"path"
"testing"
)
func TestBundle(t *testing.T) {
targetDir, _ := ioutil.TempDir("", "image_test")
Bundle(targetDir, "go", []byte("import \"fmt\""))
files, _ := ioutil.ReadDir(targetDir)
if len(files) != 3 {
t.Errorf("files number not correct, got: %d, want: %d.", len(files), 3)
}
if files[0].Name() != "Dockerfile" {
t.Errorf("Dockerfile not correct, got: %s, want: %s.", files[0], "Dockerfile")
}
if files[1].Name() != "app.go" {
t.Errorf("app.go not correct, got: %s, want: %s.", files[1], "app.go")
}
if files[2].Name() != "fx.go" {
t.Errorf("fx.go not correct, got: %s, want: %s.", files[2], "fx.go")
}
filePath := path.Join(targetDir, files[2].Name())
data, err := ioutil.ReadFile(filePath)
if err != nil {
t.Errorf("open fx.go error: %s", err)
}
if string(data) != "import \"fmt\"" {
t.Errorf("content of fx.go not correct, got: %s, want: %s.", data, "import \"fmt\"")
}
defer os.RemoveAll(targetDir)
}

Some files were not shown because too many files have changed in this diff Show More