Compare commits
91 Commits
0.8.5-alph
...
0.9.35-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
275ec78ad0 | ||
|
|
2455eb215e | ||
|
|
3b3503de1e | ||
|
|
7135633b60 | ||
|
|
b69cf16250 | ||
|
|
4527da6251 | ||
|
|
2a17ea6131 | ||
|
|
5adca0dd2f | ||
|
|
521a9e64a2 | ||
|
|
bfe8dc4249 | ||
|
|
b569820d3e | ||
|
|
99b3696b29 | ||
|
|
779679a809 | ||
|
|
ec49f75717 | ||
|
|
23598ce1c6 | ||
|
|
2831949814 | ||
|
|
e712e3d0e2 | ||
|
|
7df1c64740 | ||
|
|
91325f9ca3 | ||
|
|
7326325c19 | ||
|
|
59e195fa94 | ||
|
|
5ed4f8795a | ||
|
|
a02e7e66af | ||
|
|
9c1d093bb8 | ||
|
|
302877d4b4 | ||
|
|
871bb29dbe | ||
|
|
3be144f644 | ||
|
|
2560dc23fc | ||
|
|
3d6c3d10bf | ||
|
|
5f811693f1 | ||
|
|
0068fb92eb | ||
|
|
71174ead45 | ||
|
|
43c18caceb | ||
|
|
7b4c9c3154 | ||
|
|
9d2649433d | ||
|
|
6353fa7dd3 | ||
|
|
bfa837c88d | ||
|
|
bdc454e7e5 | ||
|
|
9b3e85754c | ||
|
|
af3dcc5f31 | ||
|
|
c375fb9eaf | ||
|
|
70c314229f | ||
|
|
66e23ead00 | ||
|
|
2e5666c2b6 | ||
|
|
7675656a54 | ||
|
|
3d7f7b0ad1 | ||
|
|
a1ccbd6cab | ||
|
|
33cb4ce63c | ||
|
|
aefb4497e2 | ||
|
|
0047e66f10 | ||
|
|
6bae4254af | ||
|
|
a9689993b0 | ||
|
|
8c0182b29f | ||
|
|
02d55c7143 | ||
|
|
f343b537f1 | ||
|
|
a84e7da65f | ||
|
|
f3b64387cb | ||
|
|
e132435ff8 | ||
|
|
fb492fa6f7 | ||
|
|
159714491d | ||
|
|
64cbbc70bb | ||
|
|
d0559f627e | ||
|
|
0a6784e270 | ||
|
|
b6fd3c7e98 | ||
|
|
1c05534071 | ||
|
|
3627d5bb40 | ||
|
|
1f7714c1e9 | ||
|
|
d868ebf4a1 | ||
|
|
4640379b06 | ||
|
|
922120efbb | ||
|
|
91fec99b00 | ||
|
|
2f89c1fe1f | ||
|
|
2298f39cca | ||
|
|
23d68bc27b | ||
|
|
74c0423f0d | ||
|
|
06f87c4d8e | ||
|
|
35262de828 | ||
|
|
34a495984c | ||
|
|
d7130c4e28 | ||
|
|
c9630a53c3 | ||
|
|
0522690472 | ||
|
|
a8a0fbed32 | ||
|
|
26ae9585f6 | ||
|
|
b69bd699c8 | ||
|
|
650ee5f63a | ||
|
|
e3c60cbb77 | ||
|
|
0daca43d10 | ||
|
|
d3c239dc54 | ||
|
|
05ac2441da | ||
|
|
c0009b1b64 | ||
|
|
82960824ef |
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -19,21 +19,16 @@ jobs:
|
||||
|
||||
- name: lint
|
||||
run: |
|
||||
docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint \
|
||||
golangci-lint run -v
|
||||
make lint
|
||||
|
||||
- name: unit test
|
||||
env:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
run: |
|
||||
export KUBECONFIG="$(kind get kubeconfig-path)"
|
||||
DEBUG=true go test -v ./...
|
||||
- name: code cov
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
run: |
|
||||
./scripts/coverage.sh
|
||||
export KUBECONFIG="$(kind get kubeconfig-path)"
|
||||
make unit-test
|
||||
bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN}
|
||||
|
||||
- name: build fx
|
||||
@@ -47,11 +42,6 @@ jobs:
|
||||
make docker-build
|
||||
make test
|
||||
# make docker-publish #TODO in release workflow
|
||||
- name: test fx docker cloud
|
||||
run: |
|
||||
make start_docker_infra
|
||||
make test_docker_infra
|
||||
make stop_docker_infra
|
||||
|
||||
- name: test fx cli
|
||||
env:
|
||||
|
||||
18
.github/workflows/docker.yml
vendored
18
.github/workflows/docker.yml
vendored
@@ -35,10 +35,10 @@ jobs:
|
||||
docker build -t metrue/fx-d-base:latest -f ./assets/dockerfiles/base/d/Dockerfile ./assets/dockerfiles/base/d
|
||||
docker push metrue/fx-d-base:latest
|
||||
|
||||
# - name: build and publish fx java image
|
||||
# run: |
|
||||
# docker build -t metrue/fx-go-base:latest -f ./assets/dockerfiles/base/java/Dockerfile ./assets/dockerfiles/base/java
|
||||
# docker push metrue/fx-java-base:latest
|
||||
- name: build and publish fx go image
|
||||
run: |
|
||||
docker build -t metrue/fx-go-base:latest -f ./assets/dockerfiles/base/go/Dockerfile ./assets/dockerfiles/base/go
|
||||
docker push metrue/fx-go-base:latest
|
||||
|
||||
- name: build and publish fx node image
|
||||
if: always()
|
||||
@@ -57,11 +57,11 @@ jobs:
|
||||
run: |
|
||||
docker push metrue/fx-python-base:latest
|
||||
|
||||
# - name: build and publish fx rust image
|
||||
# if: always()
|
||||
# run: |
|
||||
# docker build -t metrue/fx-rust-base:latest -f ./assets/dockerfiles/base/rust/Dockerfile ./assets/dockerfiles/base/python
|
||||
# docker push metrue/fx-rust-base:latest
|
||||
- name: build and publish fx perl image
|
||||
if: always()
|
||||
run: |
|
||||
docker build -t metrue/fx-perl-base:latest -f ./assets/dockerfiles/base/perl/Dockerfile ./assets/dockerfiles/base/perl
|
||||
docker push metrue/fx-perl-base:latest
|
||||
|
||||
- name: build and publish fx julia image
|
||||
if: always()
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
run: |
|
||||
export KUBECONFIG="$(kind get kubeconfig-path)"
|
||||
DEBUG=true go test -v ./container_runtimes/... ./deploy/...
|
||||
make unit-test
|
||||
|
||||
- name: build fx
|
||||
run: |
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
run:
|
||||
deadline: 10m
|
||||
timeout: 10m
|
||||
deadline: 20m
|
||||
timeout: 20m
|
||||
issues-exit-code: 1
|
||||
tests: true
|
||||
skip-dirs:
|
||||
- examples
|
||||
- api/images
|
||||
- test/functions
|
||||
- assets/
|
||||
- bundler/go/assets
|
||||
linters:
|
||||
enable:
|
||||
- goimports
|
||||
|
||||
@@ -32,3 +32,5 @@ brews:
|
||||
caveats: ""
|
||||
homepage: "https://github.com/metrue/fx"
|
||||
description: "fx, a simple but powerful Function as a Service build tools"
|
||||
dependencies:
|
||||
- docker
|
||||
|
||||
9
Makefile
9
Makefile
@@ -4,16 +4,17 @@ DOCKER_REMOTE_HOST_ADDR ?= "127.0.0.1"
|
||||
DOCKER_REMOTE_HOST_USER ?= $(whoami)
|
||||
|
||||
lint:
|
||||
golangci-lint run
|
||||
docker pull golangci/golangci-lint
|
||||
docker run --rm -v $(CURDIR):/app -w /app golangci/golangci-lint golangci-lint run -v
|
||||
|
||||
generate:
|
||||
packr
|
||||
|
||||
b:
|
||||
go build -o ${OUTPUT_DIR}/fx fx.go
|
||||
go build -ldflags="-s -w" -o ${OUTPUT_DIR}/fx fx.go
|
||||
|
||||
build:
|
||||
go build -o ${OUTPUT_DIR}/fx fx.go
|
||||
go build -ldflags="-s -w" -o ${OUTPUT_DIR}/fx fx.go
|
||||
|
||||
pull:
|
||||
./scripts/pull.sh
|
||||
@@ -32,7 +33,7 @@ cli-test-ci:
|
||||
./scripts/test_cli.sh 'js'
|
||||
|
||||
cli-test:
|
||||
./scripts/test_cli.sh 'js rb py go php java d'
|
||||
./scripts/test_cli.sh 'js rb py go java d pl'
|
||||
|
||||
http-test:
|
||||
./scripts/http_test.sh
|
||||
|
||||
152
README.md
152
README.md
@@ -1,8 +1,10 @@
|
||||
fx
|
||||
------
|
||||
|
||||
Poor man's function as a service.
|
||||
<br/>
|
||||

|
||||

|
||||
[](https://codecov.io/gh/metrue/fx)
|
||||
[](https://goreportcard.com/report/github.com/metrue/fx)
|
||||
[](http://godoc.org/github.com/metrue/fx)
|
||||
@@ -13,13 +15,9 @@ Poor man's function as a service.
|
||||
- [Introduction](#introduction)
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Contribute](#contribute)
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||

|
||||
|
||||
fx is a tool to help you do Function as a Service on your own server, fx can make your stateless function a service in seconds, both Docker host and Kubernetes cluster supported. The most exciting thing is that you can write your functions with most programming languages.
|
||||
|
||||
Feel free hacking fx to support the languages not listed. Welcome to tweet me [@_metrue](https://twitter.com/_metrue) on Twitter, [@metrue](https://www.weibo.com/u/2165714507) on Weibo.
|
||||
@@ -36,6 +34,7 @@ Feel free hacking fx to support the languages not listed. Welcome to tweet me [@
|
||||
| PHP | Supported | [@chlins](https://github.com/chlins)| [/examples/PHP](https://github.com/metrue/fx/tree/master/examples/functions/PHP) |
|
||||
| Julia | Supported | [@matbesancon](https://github.com/matbesancon)| [/examples/Julia](https://github.com/metrue/fx/tree/master/examples/functions/Julia) |
|
||||
| D | Supported | [@andre2007](https://github.com/andre2007)| [/examples/D](https://github.com/metrue/fx/tree/master/examples/functions/D) |
|
||||
| Perl | Supported | fx | [/examples/Perl](https://github.com/metrue/fx/tree/master/examples/functions/Perl) |
|
||||
| R | Working on [need your help](https://github.com/metrue/fx/issues/31) | ||
|
||||
|
||||
# Installation
|
||||
@@ -69,8 +68,6 @@ You can go the release page to [download](https://github.com/metrue/fx/releases)
|
||||
|
||||
## Usage
|
||||
|
||||
Make sure [Docker](https://docs.docker.com/engine/installation/) installed and running on your server first. then type `fx -h` on your terminal to check out basic help.
|
||||
|
||||
```
|
||||
NAME:
|
||||
fx - makes function as a service
|
||||
@@ -78,17 +75,11 @@ NAME:
|
||||
USAGE:
|
||||
fx [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
0.8.4
|
||||
|
||||
COMMANDS:
|
||||
infra manage infrastructure
|
||||
up deploy a function
|
||||
down destroy a service
|
||||
list, ls list deployed services
|
||||
call run a function instantly
|
||||
image manage image of service
|
||||
doctor health check for fx
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
@@ -96,44 +87,54 @@ GLOBAL OPTIONS:
|
||||
--version, -v print the version
|
||||
```
|
||||
|
||||
1. Write a function
|
||||
### Deploy function
|
||||
|
||||
You can check out [examples](https://github.com/metrue/fx/tree/master/examples/functions) for reference. Let's write a function as an example, it calculates the sum of two numbers then returns:
|
||||
#### Local Docker environment
|
||||
|
||||
By default, function will be deployed on localhost, make sure [Docker](https://docs.docker.com/engine/installation/) installed and running on your server first. then type `fx -h` on your terminal to check out basic help.
|
||||
|
||||
```js
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello world'
|
||||
}
|
||||
```
|
||||
Then save it to a file `func.js`.
|
||||
$ fx up --name hello ./examples/functions/JavaScript/func.js
|
||||
|
||||
2. Deploy your function as a service
|
||||
+------------------------------------------------------------------+-----------+---------------+
|
||||
| ID | NAME | ENDPOINT |
|
||||
+------------------------------------------------------------------+-----------+---------------+
|
||||
| 5b24d36608ee392c937a61a530805f74551ddec304aea3aca2ffa0fabcf98cf3 | /hello | 0.0.0.0:58328 |
|
||||
+------------------------------------------------------------------+-----------+---------------+
|
||||
```
|
||||
|
||||
Give your service a port with `--port`, and name with `--name`, heath checking with `--healthcheck` if you want.
|
||||
#### Remote host
|
||||
|
||||
Use `--host` to specify the target host for your function, or you can just set it to `FX_HOST` environment variable.
|
||||
|
||||
```shell
|
||||
$ fx up -name fx_service_name -p 10001 --healthcheck func.js
|
||||
$ fx up --host roo@<your host> --name hello ./examples/functions/JavaScript/func.js
|
||||
|
||||
2019/08/10 13:26:37 info Pack Service: ✓
|
||||
2019/08/10 13:26:39 info Build Service: ✓
|
||||
2019/08/10 13:26:39 info Run Service: ✓
|
||||
2019/08/10 13:26:39 info Service (fx_service_name) is running on: 0.0.0.0:10001
|
||||
2019/08/10 13:26:39 info up function fx_service_name(func.js) to machine localhost: ✓
|
||||
+------------------------------------------------------------------+-----------+---------------+
|
||||
| ID | NAME | ENDPOINT |
|
||||
+------------------------------------------------------------------+-----------+---------------+
|
||||
| 5b24d36608ee392c937a61a530805f74551ddec304aea3aca2ffa0fabcf98cf3 | /hello | 0.0.0.0:58345 |
|
||||
+------------------------------------------------------------------+-----------+---------------+
|
||||
```
|
||||
|
||||
if you want see what the source code of your service looks like, you can export it into a dirctory,
|
||||
#### Kubernetes
|
||||
|
||||
```shell
|
||||
$ fx image export -o <path of dir> func.js
|
||||
2019/09/25 19:31:19 info exported to <path of dir>: ✓
|
||||
```
|
||||
$ FX_KUBECONF=~/.kube/config fx up examples/functions/JavaScript/func.js --name hello
|
||||
|
||||
+-------------------------------+------+----------------+
|
||||
| ID | NAME | ENDPOINT |
|
||||
+----+--------------------------+-----------------------+
|
||||
| 5b24d36608ee392c937a | hello-fx | 10.0.242.75:80 |
|
||||
+------------------------+-------------+----------------+
|
||||
```
|
||||
|
||||
3. Test your service
|
||||
### Test service
|
||||
|
||||
then you can test your service:
|
||||
|
||||
```shell
|
||||
$ curl -v 0.0.0.0:10001
|
||||
$ curl -v 0.0.0.0:58328
|
||||
|
||||
|
||||
GET / HTTP/1.1
|
||||
@@ -155,39 +156,7 @@ hello world
|
||||
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
**fx** is originally designed to turn a function into a runnable Docker container in a easiest way, on a host with Docker running, you can just deploy your function with `fx up` command,
|
||||
|
||||
```shell
|
||||
fx up --name hello-svc --port 7777 hello.js # onto localhost
|
||||
DOCKER_REMOTE_HOST_ADDR=xx.xx.xx.xx DOCKER_REMOTE_HOST_USER=xxxx DOCKER_REMOTE_HOST_PASSWORD=xxxx fx up --name hello-svc --port 7777 hello.js # onto remote host
|
||||
```
|
||||
|
||||
## Kubernetes
|
||||
|
||||
**fx** supports deploy function to be a service onto Kubernetes cluster infrasture, and we encourage you to do that other than on bare Docker environment, there are lots of advantage to run your function on Kubernetes like self-healing, load balancing, easy horizontal scaling, etc. It's pretty simple to deploy your function onto Kubernetes with **fx**, you just set KUBECONFIG in your enviroment.
|
||||
|
||||
```shell
|
||||
KUBECONFIG=<Your KUBECONFIG> fx deploy -n fx-service-abc_js -p 12349 examples/functions/JavaScript/func.js # function will be deploy to your Kubernetes cluster and expose a IP address of your loadbalencer
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```shell
|
||||
$ export KUBECONFIG=<Your KUBECONFIG>
|
||||
$ fx deploy -n fx-service-abc_js -p 12349 examples/functions/JavaScript/func.js # function will be deploy to your Kubernetes cluster and expose a IP address of your loadbalencer
|
||||
```
|
||||
|
||||
* Local Kubernetes Cluster
|
||||
|
||||
Docker for Mac and Docker for Windows already support Kubernetes with single node cluster, we can use it directly, and the default `KUBECONFIG` is `~/.kube/config`.
|
||||
|
||||
```shell
|
||||
$ export KUBECONFIG=~/.kube/config # then fx will take the config to deloy function
|
||||
```
|
||||
|
||||
if you have multiple Kubernetes clusters configured, you have to set context correctly. FYI [configure-access-multiple-clusters](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/)
|
||||
## Use Public Cloud Kubernetes Service as infrastructure to run your functions
|
||||
|
||||
* Azure Kubernetes Service (AKS)
|
||||
|
||||
@@ -211,7 +180,7 @@ aks-nodepool1-31718369-0 Ready agent 6m44s v1.12.8
|
||||
Since AKS's config will be merged into `~/.kube/config` and set to be current context after you run `az aks get-credentials` command, so you can just set KUBECONFIG to default config also,
|
||||
|
||||
```shell
|
||||
$ export KUBECONFIG=~/.kube/config # then fx will take the config to deloy function
|
||||
$ export FX_KUBECONF=~/.kube/config # then fx will take the config to deloy function
|
||||
```
|
||||
|
||||
But we would suggest you run `kubectl config current-context` to check if the current context is what you want.
|
||||
@@ -219,42 +188,33 @@ But we would suggest you run `kubectl config current-context` to check if the cu
|
||||
* Amazon Elastic Kubernetes Service (EKS)
|
||||
TODO
|
||||
|
||||
* Google Kubernetes Engine (GKET)
|
||||
TODO
|
||||
* Google Kubernetes Engine (GKE)
|
||||
|
||||
First you should create a Kubernetes cluster in your GKE, then make sure your KUBECONFIG is ready in `~/.kube/config`, if not, you can run following commands,
|
||||
|
||||
``` shell
|
||||
$ gcloud auth login
|
||||
$ gcloud container clusters get-credentials <your cluster> --zone <zone> --project <project>
|
||||
```
|
||||
|
||||
Then make sure you current context is GKE cluster, you can check it with command,
|
||||
|
||||
``` shell
|
||||
$ kubectl config current-context
|
||||
```
|
||||
|
||||
Then you can deploy your function onto GKE cluster with,
|
||||
|
||||
```shell
|
||||
$ FX_KUBECONF=~/.kube/config fx up examples/functions/JavaScript/func.js --name hellojs
|
||||
```
|
||||
|
||||
* Setup your own Kubernetes cluster
|
||||
|
||||

|
||||
|
||||
```shell
|
||||
fx infra create --type k3s --name fx-cluster-1 --master root@123.11.2.3 --agents 'root@1.1.1.1,root@2.2.2.2'
|
||||
```
|
||||
|
||||
## Contribute
|
||||
|
||||
fx uses [Project](https://github.com/metrue/fx/projects/4) to manage the development.
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
Docker: make sure [Docker](https://docs.docker.com/engine/installation/) installed and running on your server.
|
||||
|
||||
|
||||
<a name="buildtest"></a>
|
||||
#### Build & Test
|
||||
|
||||
```
|
||||
$ git clone https://github.com/metrue/fx
|
||||
$ cd fx
|
||||
$ make build
|
||||
```
|
||||
|
||||
Then you can build and test:
|
||||
|
||||
```
|
||||
$ make build
|
||||
$ ./build/fx -h
|
||||
```
|
||||
|
||||
|
||||
## Contributors
|
||||
|
||||
|
||||
4
assets/dockerfiles/base/go/Dockerfile
vendored
Normal file
4
assets/dockerfiles/base/go/Dockerfile
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
FROM golang:latest
|
||||
|
||||
# dependency management
|
||||
RUN go get github.com/gin-gonic/gin
|
||||
26
assets/dockerfiles/base/node/package-lock.json
generated
vendored
Normal file
26
assets/dockerfiles/base/node/package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "fx-node-base",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@koa/cors": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@koa/cors/-/cors-2.2.3.tgz",
|
||||
"integrity": "sha512-tCVVXa39ETsit5kGBtEWWimjLn1sDaeu8+0phgb8kT3GmBDZOykkI3ZO8nMjV2p3MGkJI4K5P+bxR8Ztq0bwsA==",
|
||||
"requires": {
|
||||
"vary": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
|
||||
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||
}
|
||||
}
|
||||
}
|
||||
9
assets/dockerfiles/base/node/package.json
vendored
9
assets/dockerfiles/base/node/package.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "aok",
|
||||
"name": "fx-node-base",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
@@ -10,12 +10,11 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@koa/cors": "^2.2.3",
|
||||
"get-port": "^3.2.0",
|
||||
"is-generator-function": "^1.0.6",
|
||||
"koa": "^2.3.0",
|
||||
"koa-bodyparser": "^4.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"get-port-cli": "^1.1.0"
|
||||
"koa-bodyparser": "^4.2.0",
|
||||
"node-fetch": "^2.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
10
assets/dockerfiles/base/perl/Dockerfile
vendored
Normal file
10
assets/dockerfiles/base/perl/Dockerfile
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
FROM alpine:3.4
|
||||
MAINTAINER Mojolicious
|
||||
|
||||
ADD . .
|
||||
COPY cpanfile /
|
||||
ENV EV_EXTRA_DEFS -DEV_NO_ATFORK
|
||||
|
||||
RUN apk update && \
|
||||
apk add perl perl-io-socket-ssl perl-dbd-pg perl-dev g++ make wget curl && \
|
||||
curl -L https://cpanmin.us | perl - App::cpanminus && cpanm --installdeps . -M https://cpan.metacpan.org
|
||||
3
assets/dockerfiles/base/perl/cpanfile
vendored
Normal file
3
assets/dockerfiles/base/perl/cpanfile
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
requires "EV";
|
||||
requires "JSON";
|
||||
requires "Mojolicious::Lite";
|
||||
3
assets/dockerfiles/base/ruby/Dockerfile
vendored
Normal file
3
assets/dockerfiles/base/ruby/Dockerfile
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM ruby:latest
|
||||
|
||||
RUN gem install sinatra
|
||||
44
bundle/bundle.go
Normal file
44
bundle/bundle.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/metrue/fx/bundler"
|
||||
"github.com/metrue/fx/bundler/d"
|
||||
golang "github.com/metrue/fx/bundler/go"
|
||||
"github.com/metrue/fx/bundler/java"
|
||||
"github.com/metrue/fx/bundler/julia"
|
||||
"github.com/metrue/fx/bundler/node"
|
||||
"github.com/metrue/fx/bundler/perl"
|
||||
"github.com/metrue/fx/bundler/python"
|
||||
"github.com/metrue/fx/bundler/ruby"
|
||||
"github.com/metrue/fx/bundler/rust"
|
||||
)
|
||||
|
||||
// Bundle function to project
|
||||
func Bundle(workdir string, language string, fn string, deps ...string) error {
|
||||
var bundler bundler.Bundler
|
||||
switch language {
|
||||
case "d":
|
||||
bundler = d.New()
|
||||
case "node":
|
||||
bundler = node.New()
|
||||
case "go":
|
||||
bundler = golang.New()
|
||||
case "java":
|
||||
bundler = java.New()
|
||||
case "julia":
|
||||
bundler = julia.New()
|
||||
case "perl":
|
||||
bundler = perl.New()
|
||||
case "python":
|
||||
bundler = python.New()
|
||||
case "ruby":
|
||||
bundler = ruby.New()
|
||||
case "rust":
|
||||
bundler = rust.New()
|
||||
default:
|
||||
return fmt.Errorf("%s not suppported yet", language)
|
||||
}
|
||||
return bundler.Bundle(workdir, fn, deps...)
|
||||
}
|
||||
117
bundle/bundler_test.go
Normal file
117
bundle/bundler_test.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func createFn(content string, t *testing.T) string {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ioutil.WriteFile(fd.Name(), []byte(content), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return fd.Name()
|
||||
}
|
||||
|
||||
func TestBundle(t *testing.T) {
|
||||
workdir, err := ioutil.TempDir("", "fx-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(workdir)
|
||||
|
||||
cases := []struct {
|
||||
workdir string
|
||||
language string
|
||||
fn string
|
||||
deps []string
|
||||
}{
|
||||
{
|
||||
workdir: workdir,
|
||||
language: "d",
|
||||
fn: `
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello fx'
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
workdir: workdir,
|
||||
language: "go",
|
||||
fn: `
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello fx'
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
workdir: workdir,
|
||||
language: "java",
|
||||
fn: `
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello fx'
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
workdir: workdir,
|
||||
language: "julia",
|
||||
fn: `
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello fx'
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
workdir: workdir,
|
||||
language: "perl",
|
||||
fn: `
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello fx'
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
workdir: workdir,
|
||||
language: "python",
|
||||
fn: `
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello fx'
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
workdir: workdir,
|
||||
language: "ruby",
|
||||
fn: `
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello fx'
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
workdir: workdir,
|
||||
language: "rust",
|
||||
fn: `
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello fx'
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
fn := createFn(c.fn, t)
|
||||
defer os.Remove(fn)
|
||||
|
||||
if err := Bundle(c.workdir, c.language, fn, c.deps...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
63
bundler/README.md
Normal file
63
bundler/README.md
Normal file
@@ -0,0 +1,63 @@
|
||||
The only thing `fx` does is to make a single function to be an HTTP service, `fx` takes two steps to finish the process,
|
||||
* Wraps the function into a web server project and make it be the handler function of the HTTP request.
|
||||
* Builds the bundled web server to be Docker image, then run it as a Docker container and expose the service port.
|
||||
|
||||
`bundler` is the component responsible for step 1: wrap a single function (and its dependencies) into a web server. Take a Node web service as an example, the project looks like this,
|
||||
```
|
||||
helloworld
|
||||
├── Dockerfile
|
||||
├── app.js
|
||||
└── fx.js
|
||||
```
|
||||
|
||||
And the codes is pretty simple,
|
||||
|
||||
**app.js**
|
||||
```javascript
|
||||
const Koa = require('koa');
|
||||
const bodyParser = require('koa-bodyparser');
|
||||
const cors = require('@koa/cors');
|
||||
const fx = require('./fx');
|
||||
|
||||
const app = new Koa();
|
||||
app.use(cors({
|
||||
origin: '*',
|
||||
}));
|
||||
app.use(bodyParser());
|
||||
app.use(fx);
|
||||
|
||||
app.listen(3000);
|
||||
```
|
||||
|
||||
**fx.js**
|
||||
```javascript
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello world'
|
||||
}
|
||||
```
|
||||
|
||||
**Dockerfile**
|
||||
```dockerfile
|
||||
FROM metrue/fx-node-base
|
||||
|
||||
COPY . .
|
||||
EXPOSE 3000
|
||||
CMD ["node", "app.js"]
|
||||
```
|
||||
|
||||
You can see that it's a web service built with `Koa`, the request handler function defined in `fx.js` to response plain text `hello world`, so to make a general JavaScript/Node function to be a web service, we only have to put it into `fx.js` above, then build and run it with Docker and that's it.
|
||||
|
||||
To support different programming languages, `fx` needs to implement different `bundlers`, the reasons are,
|
||||
* The way to set up a web service is different in different languages
|
||||
* The way to manage dependency is different in different languages.
|
||||
|
||||
So there will be (are) different `bundlers` in `fx`.
|
||||
```
|
||||
go-bundler: based on Gin
|
||||
ruby-bundler: based on Sinatra
|
||||
node-bundler: based on Koa
|
||||
python-bundler: based on flask
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
88
bundler/bundler.go
Normal file
88
bundler/bundler.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package bundler
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gobuffalo/packd"
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/metrue/fx/constants"
|
||||
"github.com/metrue/fx/utils"
|
||||
)
|
||||
|
||||
// Bundler defines interface
|
||||
type Bundler interface {
|
||||
Scaffold(output string) error
|
||||
Bundle(output string, fn string, deps ...string) error
|
||||
}
|
||||
|
||||
// IsHandler check if it's handle file
|
||||
func IsHandler(name string, lang string) bool {
|
||||
basename := filepath.Base(name)
|
||||
nameWithoutExt := strings.TrimSuffix(basename, filepath.Ext(basename))
|
||||
if constants.ExtLangMapping[filepath.Ext(basename)] != lang {
|
||||
return false
|
||||
}
|
||||
|
||||
return (nameWithoutExt == "fx" ||
|
||||
// Fx is for Java
|
||||
nameWithoutExt == "Fx" ||
|
||||
// mod.rs is for Rust)
|
||||
nameWithoutExt == "mod")
|
||||
}
|
||||
|
||||
// Restore directory from packr box
|
||||
func Restore(box *packr.Box, output string) error {
|
||||
if err := box.Walk(func(name string, fd packd.File) error {
|
||||
content, err := box.Find(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dest := filepath.Join(output, name)
|
||||
if err := utils.EnsureFile(dest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(dest, content, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bundle bundle a function
|
||||
func Bundle(box *packr.Box, output string, language string, fn string, deps ...string) error {
|
||||
if err := Restore(box, output); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := utils.Merge(output, deps...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Replace the default handler source file with given function source file
|
||||
if err := filepath.Walk(output, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if IsHandler(path, language) {
|
||||
if err := utils.CopyFile(fn, path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
51
bundler/bundler_test.go
Normal file
51
bundler/bundler_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package bundler
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
)
|
||||
|
||||
func TestBundler(t *testing.T) {
|
||||
t.Run("Restore", func(t *testing.T) {
|
||||
box := packr.New("", "./node/assets")
|
||||
outputDir, err := ioutil.TempDir("", "fx_koa")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
if err := Restore(box, outputDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Bundle", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content := `
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello fx'
|
||||
}`
|
||||
if err = ioutil.WriteFile(fd.Name(), []byte(content), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_koa")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
box := packr.New("", "./node/assets")
|
||||
if err := Bundle(box, outputDir, "node", fd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
8
bundler/d/d-packr.go
Normal file
8
bundler/d/d-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package d
|
||||
|
||||
import _ "github.com/metrue/fx/packrd"
|
||||
32
bundler/d/d.go
Normal file
32
bundler/d/d.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package d
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/metrue/fx/bundler"
|
||||
)
|
||||
|
||||
// D defines d bundler
|
||||
type D struct {
|
||||
assets *packr.Box
|
||||
}
|
||||
|
||||
// New a koa bundler
|
||||
func New() *D {
|
||||
return &D{
|
||||
assets: packr.New("d", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
// Scaffold a koa app
|
||||
func (d *D) Scaffold(output string) error {
|
||||
return bundler.Restore(d.assets, output)
|
||||
}
|
||||
|
||||
// Bundle a function into a koa project
|
||||
func (d *D) Bundle(output string, fn string, deps ...string) error {
|
||||
return bundler.Bundle(d.assets, output, "d", fn, deps...)
|
||||
}
|
||||
|
||||
var (
|
||||
_ bundler.Bundler = &D{}
|
||||
)
|
||||
@@ -1,11 +1,8 @@
|
||||
FROM golang:latest
|
||||
FROM metrue/fx-go-base
|
||||
|
||||
COPY . /go/src/github.com/metrue/fx
|
||||
WORKDIR /go/src/github.com/metrue/fx
|
||||
|
||||
# dependency management
|
||||
RUN go get github.com/gin-gonic/gin
|
||||
|
||||
RUN go build -ldflags "-w -s" -o fx fx.go app.go
|
||||
|
||||
EXPOSE 3000
|
||||
32
bundler/go/go.go
Normal file
32
bundler/go/go.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/metrue/fx/bundler"
|
||||
)
|
||||
|
||||
// Gin defines javascript bundler
|
||||
type Gin struct {
|
||||
assets *packr.Box
|
||||
}
|
||||
|
||||
// New a koa bundler
|
||||
func New() *Gin {
|
||||
return &Gin{
|
||||
assets: packr.New("go", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
// Scaffold a koa app
|
||||
func (g *Gin) Scaffold(output string) error {
|
||||
return bundler.Restore(g.assets, output)
|
||||
}
|
||||
|
||||
// Bundle a function into a koa project
|
||||
func (g *Gin) Bundle(output string, fn string, deps ...string) error {
|
||||
return bundler.Bundle(g.assets, output, "go", fn, deps...)
|
||||
}
|
||||
|
||||
var (
|
||||
_ bundler.Bundler = &Gin{}
|
||||
)
|
||||
143
bundler/go/go_test.go
Normal file
143
bundler/go/go_test.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/metrue/fx/utils"
|
||||
)
|
||||
|
||||
func TestKoaBundler(t *testing.T) {
|
||||
t.Run("Scaffold", func(t *testing.T) {
|
||||
outputDir, err := ioutil.TempDir("", "fx_koa")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
koa := New()
|
||||
if err := koa.Scaffold(outputDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, _, _, err := utils.Diff(outputDir, "./assets")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff {
|
||||
t.Fatalf("%s is not equal with %s", outputDir, "./assets")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleSingleFunc", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content := `
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello fx'
|
||||
}`
|
||||
err = ioutil.WriteFile(fd.Name(), []byte(content), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_koa")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
koa := New()
|
||||
if err := koa.Bundle(outputDir, fd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle functino should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(content)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
|
||||
preHandleFunc, err := ioutil.ReadFile("./assets/fx.go")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(pre, preHandleFunc) {
|
||||
{
|
||||
}
|
||||
t.Fatalf("it should get %s but got %s", preHandleFunc, pre)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleFuncAndDeps", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content, err := ioutil.ReadFile("./assets/fx.go")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ioutil.WriteFile(fd.Name(), content, 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addFunc := `
|
||||
module.exports = (a, b) => a+b
|
||||
`
|
||||
addFd, err := ioutil.TempFile("", "fx_add_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(addFd.Name(), []byte(addFunc), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_koa")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
koa := New()
|
||||
if err := koa.Bundle(outputDir, fd.Name(), addFd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle functino should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(addFunc)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
if pre != nil {
|
||||
t.Fatal(pre)
|
||||
}
|
||||
})
|
||||
}
|
||||
8
bundler/go/golang-packr.go
Normal file
8
bundler/go/golang-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package golang
|
||||
|
||||
import _ "github.com/metrue/fx/packrd"
|
||||
8
bundler/java/java-packr.go
Normal file
8
bundler/java/java-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package java
|
||||
|
||||
import _ "github.com/metrue/fx/packrd"
|
||||
32
bundler/java/java.go
Normal file
32
bundler/java/java.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package java
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/metrue/fx/bundler"
|
||||
)
|
||||
|
||||
// Java defines javascript bundler
|
||||
type Java struct {
|
||||
assets *packr.Box
|
||||
}
|
||||
|
||||
// New a koa bundler
|
||||
func New() *Java {
|
||||
return &Java{
|
||||
assets: packr.New("java", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
// Scaffold a koa app
|
||||
func (k *Java) Scaffold(output string) error {
|
||||
return bundler.Restore(k.assets, output)
|
||||
}
|
||||
|
||||
// Bundle a function into a koa project
|
||||
func (k *Java) Bundle(output string, fn string, deps ...string) error {
|
||||
return bundler.Bundle(k.assets, output, "java", fn, deps...)
|
||||
}
|
||||
|
||||
var (
|
||||
_ bundler.Bundler = &Java{}
|
||||
)
|
||||
155
bundler/java/java_test.go
Normal file
155
bundler/java/java_test.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package java
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/metrue/fx/utils"
|
||||
)
|
||||
|
||||
func TestJavaBundler(t *testing.T) {
|
||||
t.Run("Scaffold", func(t *testing.T) {
|
||||
outputDir, err := ioutil.TempDir("", "fx_java")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
java := New()
|
||||
if err := java.Scaffold(outputDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, _, _, err := utils.Diff(outputDir, "./assets")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff {
|
||||
t.Fatalf("%s is not equal with %s", outputDir, "./assets")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleSingleFunc", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.java")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content := `package fx;
|
||||
|
||||
import io.javalin.Javalin;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class app {
|
||||
public static void main(String[] args) {
|
||||
Javalin app = Javalin.start(3000);
|
||||
Fx handler = new Fx();
|
||||
app.post("/", ctx -> {
|
||||
JSONObject obj = new JSONObject(ctx.body());
|
||||
ctx.result(""+handler.handle(obj));
|
||||
});
|
||||
}
|
||||
}
|
||||
`
|
||||
err = ioutil.WriteFile(fd.Name(), []byte(content), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_java")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
java := New()
|
||||
if err := java.Bundle(outputDir, fd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle functino should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(content)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
|
||||
preHandleFunc, err := ioutil.ReadFile("./assets/src/main/java/fx/Fx.java")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(pre, preHandleFunc) {
|
||||
{
|
||||
}
|
||||
t.Fatalf("it should get %s but got %s", preHandleFunc, pre)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleFuncAndDeps", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content, err := ioutil.ReadFile("./assets/src/main/java/fx/Fx.java")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ioutil.WriteFile(fd.Name(), content, 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addFunc := `
|
||||
module.exports = (a, b) => a+b
|
||||
`
|
||||
addFd, err := ioutil.TempFile("", "fx_add_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(addFd.Name(), []byte(addFunc), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_java")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
java := New()
|
||||
if err := java.Bundle(outputDir, fd.Name(), addFd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle functino should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(addFunc)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
if pre != nil {
|
||||
t.Fatal(pre)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
struct Input
|
||||
a::Number
|
||||
b::Number
|
||||
8
bundler/julia/julia-packr.go
Normal file
8
bundler/julia/julia-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package julia
|
||||
|
||||
import _ "github.com/metrue/fx/packrd"
|
||||
32
bundler/julia/julia.go
Normal file
32
bundler/julia/julia.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package julia
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/metrue/fx/bundler"
|
||||
)
|
||||
|
||||
// Julia defines javascript bundler
|
||||
type Julia struct {
|
||||
assets *packr.Box
|
||||
}
|
||||
|
||||
// New a koa bundler
|
||||
func New() *Julia {
|
||||
return &Julia{
|
||||
assets: packr.New("julia", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
// Scaffold a koa app
|
||||
func (k *Julia) Scaffold(output string) error {
|
||||
return bundler.Restore(k.assets, output)
|
||||
}
|
||||
|
||||
// Bundle a function into a koa project
|
||||
func (k *Julia) Bundle(output string, fn string, deps ...string) error {
|
||||
return bundler.Bundle(k.assets, output, "julia", fn, deps...)
|
||||
}
|
||||
|
||||
var (
|
||||
_ bundler.Bundler = &Julia{}
|
||||
)
|
||||
148
bundler/julia/julia_test.go
Normal file
148
bundler/julia/julia_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package julia
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/metrue/fx/utils"
|
||||
)
|
||||
|
||||
func TestJuliaBundler(t *testing.T) {
|
||||
t.Run("Scaffold", func(t *testing.T) {
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Scaffold(outputDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, _, _, err := utils.Diff(outputDir, "./assets")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff {
|
||||
t.Fatalf("%s is not equal with %s", outputDir, "./assets")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleSingleFunc", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.julia")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content := `struct Input
|
||||
a::Number
|
||||
b::Number
|
||||
end
|
||||
|
||||
fx = function(input::Input)
|
||||
return input.a + input.b
|
||||
end
|
||||
`
|
||||
err = ioutil.WriteFile(fd.Name(), []byte(content), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Bundle(outputDir, fd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle function should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(content)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
|
||||
preHandleFunc, err := ioutil.ReadFile("./assets/fx.jl")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(pre, preHandleFunc) {
|
||||
{
|
||||
}
|
||||
t.Fatalf("it should get %s but got %s", preHandleFunc, pre)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleFuncAndDeps", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content, err := ioutil.ReadFile("./assets/fx.jl")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ioutil.WriteFile(fd.Name(), content, 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addFunc := `
|
||||
module.exports = (a, b) => a+b
|
||||
`
|
||||
addFd, err := ioutil.TempFile("", "fx_add_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(addFd.Name(), []byte(addFunc), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Bundle(outputDir, fd.Name(), addFd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle functino should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(addFunc)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
if pre != nil {
|
||||
t.Fatal(pre)
|
||||
}
|
||||
})
|
||||
}
|
||||
13
bundler/node/assets/app.js
Normal file
13
bundler/node/assets/app.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const Koa = require('koa');
|
||||
const bodyParser = require('koa-bodyparser');
|
||||
const cors = require('@koa/cors');
|
||||
const fx = require('./fx');
|
||||
|
||||
const app = new Koa();
|
||||
app.use(cors({
|
||||
origin: '*',
|
||||
}));
|
||||
app.use(bodyParser());
|
||||
app.use(fx);
|
||||
|
||||
app.listen(3000);
|
||||
8
bundler/node/node-packr.go
Normal file
8
bundler/node/node-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package node
|
||||
|
||||
import _ "github.com/metrue/fx/packrd"
|
||||
34
bundler/node/node.go
Normal file
34
bundler/node/node.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/metrue/fx/bundler"
|
||||
)
|
||||
|
||||
const language = "node"
|
||||
|
||||
// Node defines node bundler
|
||||
type Node struct {
|
||||
assets *packr.Box
|
||||
}
|
||||
|
||||
// New a koa bundler
|
||||
func New() *Node {
|
||||
return &Node{
|
||||
assets: packr.New("node", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
// Scaffold a koa app
|
||||
func (k *Node) Scaffold(output string) error {
|
||||
return bundler.Restore(k.assets, output)
|
||||
}
|
||||
|
||||
// Bundle a function into a koa project
|
||||
func (k *Node) Bundle(output string, fn string, deps ...string) error {
|
||||
return bundler.Bundle(k.assets, output, language, fn, deps...)
|
||||
}
|
||||
|
||||
var (
|
||||
_ bundler.Bundler = &Node{}
|
||||
)
|
||||
143
bundler/node/node_test.go
Normal file
143
bundler/node/node_test.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/metrue/fx/utils"
|
||||
)
|
||||
|
||||
func TestNodeBundler(t *testing.T) {
|
||||
t.Run("Scaffold", func(t *testing.T) {
|
||||
outputDir, err := ioutil.TempDir("", "fx_koa")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
koa := New()
|
||||
if err := koa.Scaffold(outputDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, _, _, err := utils.Diff(outputDir, "./assets")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff {
|
||||
t.Fatalf("%s is not equal with %s", outputDir, "./assets")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleSingleFunc", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content := `
|
||||
module.exports = (ctx) => {
|
||||
ctx.body = 'hello fx'
|
||||
}`
|
||||
err = ioutil.WriteFile(fd.Name(), []byte(content), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_koa")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
koa := New()
|
||||
if err := koa.Bundle(outputDir, fd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle functino should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(content)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
|
||||
preHandleFunc, err := ioutil.ReadFile("./assets/fx.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(pre, preHandleFunc) {
|
||||
{
|
||||
}
|
||||
t.Fatalf("it should get %s but got %s", preHandleFunc, pre)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleFuncAndDeps", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content, err := ioutil.ReadFile("./assets/fx.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ioutil.WriteFile(fd.Name(), content, 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addFunc := `
|
||||
module.exports = (a, b) => a+b
|
||||
`
|
||||
addFd, err := ioutil.TempFile("", "fx_add_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(addFd.Name(), []byte(addFunc), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_koa")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
koa := New()
|
||||
if err := koa.Bundle(outputDir, fd.Name(), addFd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle functino should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(addFunc)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
if pre != nil {
|
||||
t.Fatal(pre)
|
||||
}
|
||||
})
|
||||
}
|
||||
6
bundler/perl/assets/Dockerfile
Normal file
6
bundler/perl/assets/Dockerfile
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM metrue/fx-perl-base
|
||||
|
||||
ADD . .
|
||||
|
||||
EXPOSE 3000
|
||||
CMD ["perl", "app.pl", "daemon"]
|
||||
17
bundler/perl/assets/app.pl
Normal file
17
bundler/perl/assets/app.pl
Normal file
@@ -0,0 +1,17 @@
|
||||
use Mojolicious::Lite;
|
||||
|
||||
require "./fx.pl";
|
||||
|
||||
get '/' => sub {
|
||||
my $ctx = shift;
|
||||
my $res = fx($ctx);
|
||||
$ctx->render(json => $res);
|
||||
};
|
||||
|
||||
post '/' => sub {
|
||||
my $ctx = shift;
|
||||
my $res = fx($ctx);
|
||||
$ctx->render(json => $res);
|
||||
};
|
||||
|
||||
app->start;
|
||||
6
bundler/perl/assets/fx.pl
Normal file
6
bundler/perl/assets/fx.pl
Normal file
@@ -0,0 +1,6 @@
|
||||
sub fx {
|
||||
my $ctx = shift;
|
||||
return 'hello fx'
|
||||
}
|
||||
|
||||
1;
|
||||
8
bundler/perl/perl-packr.go
Normal file
8
bundler/perl/perl-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package perl
|
||||
|
||||
import _ "github.com/metrue/fx/packrd"
|
||||
32
bundler/perl/perl.go
Normal file
32
bundler/perl/perl.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package perl
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/metrue/fx/bundler"
|
||||
)
|
||||
|
||||
// Julia defines javascript bundler
|
||||
type Julia struct {
|
||||
assets *packr.Box
|
||||
}
|
||||
|
||||
// New a koa bundler
|
||||
func New() *Julia {
|
||||
return &Julia{
|
||||
assets: packr.New("perl", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
// Scaffold a koa app
|
||||
func (k *Julia) Scaffold(output string) error {
|
||||
return bundler.Restore(k.assets, output)
|
||||
}
|
||||
|
||||
// Bundle a function into a koa project
|
||||
func (k *Julia) Bundle(output string, fn string, deps ...string) error {
|
||||
return bundler.Bundle(k.assets, output, "perl", fn, deps...)
|
||||
}
|
||||
|
||||
var (
|
||||
_ bundler.Bundler = &Julia{}
|
||||
)
|
||||
148
bundler/perl/perl_test.go
Normal file
148
bundler/perl/perl_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package perl
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/metrue/fx/utils"
|
||||
)
|
||||
|
||||
func TestJuliaBundler(t *testing.T) {
|
||||
t.Run("Scaffold", func(t *testing.T) {
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Scaffold(outputDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, _, _, err := utils.Diff(outputDir, "./assets")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff {
|
||||
t.Fatalf("%s is not equal with %s", outputDir, "./assets")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleSingleFunc", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.julia")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content := `struct Input
|
||||
a::Number
|
||||
b::Number
|
||||
end
|
||||
|
||||
fx = function(input::Input)
|
||||
return input.a + input.b
|
||||
end
|
||||
`
|
||||
err = ioutil.WriteFile(fd.Name(), []byte(content), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Bundle(outputDir, fd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle function should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(content)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
|
||||
preHandleFunc, err := ioutil.ReadFile("./assets/fx.pl")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(pre, preHandleFunc) {
|
||||
{
|
||||
}
|
||||
t.Fatalf("it should get %s but got %s", preHandleFunc, pre)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleFuncAndDeps", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content, err := ioutil.ReadFile("./assets/fx.pl")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ioutil.WriteFile(fd.Name(), content, 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addFunc := `
|
||||
module.exports = (a, b) => a+b
|
||||
`
|
||||
addFd, err := ioutil.TempFile("", "fx_add_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(addFd.Name(), []byte(addFunc), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Bundle(outputDir, fd.Name(), addFd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle functino should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(addFunc)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
if pre != nil {
|
||||
t.Fatal(pre)
|
||||
}
|
||||
})
|
||||
}
|
||||
8
bundler/python/python-packr.go
Normal file
8
bundler/python/python-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package python
|
||||
|
||||
import _ "github.com/metrue/fx/packrd"
|
||||
32
bundler/python/python.go
Normal file
32
bundler/python/python.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package python
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/metrue/fx/bundler"
|
||||
)
|
||||
|
||||
// Julia defines javascript bundler
|
||||
type Julia struct {
|
||||
assets *packr.Box
|
||||
}
|
||||
|
||||
// New a koa bundler
|
||||
func New() *Julia {
|
||||
return &Julia{
|
||||
assets: packr.New("python", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
// Scaffold a koa app
|
||||
func (k *Julia) Scaffold(output string) error {
|
||||
return bundler.Restore(k.assets, output)
|
||||
}
|
||||
|
||||
// Bundle a function into a koa project
|
||||
func (k *Julia) Bundle(output string, fn string, deps ...string) error {
|
||||
return bundler.Bundle(k.assets, output, "python", fn, deps...)
|
||||
}
|
||||
|
||||
var (
|
||||
_ bundler.Bundler = &Julia{}
|
||||
)
|
||||
148
bundler/python/python_test.go
Normal file
148
bundler/python/python_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package python
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/metrue/fx/utils"
|
||||
)
|
||||
|
||||
func TestJuliaBundler(t *testing.T) {
|
||||
t.Run("Scaffold", func(t *testing.T) {
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Scaffold(outputDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, _, _, err := utils.Diff(outputDir, "./assets")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff {
|
||||
t.Fatalf("%s is not equal with %s", outputDir, "./assets")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleSingleFunc", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.julia")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content := `struct Input
|
||||
a::Number
|
||||
b::Number
|
||||
end
|
||||
|
||||
fx = function(input::Input)
|
||||
return input.a + input.b
|
||||
end
|
||||
`
|
||||
err = ioutil.WriteFile(fd.Name(), []byte(content), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Bundle(outputDir, fd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle function should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(content)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
|
||||
preHandleFunc, err := ioutil.ReadFile("./assets/fx.py")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(pre, preHandleFunc) {
|
||||
{
|
||||
}
|
||||
t.Fatalf("it should get %s but got %s", preHandleFunc, pre)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleFuncAndDeps", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content, err := ioutil.ReadFile("./assets/fx.py")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ioutil.WriteFile(fd.Name(), content, 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addFunc := `
|
||||
module.exports = (a, b) => a+b
|
||||
`
|
||||
addFd, err := ioutil.TempFile("", "fx_add_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(addFd.Name(), []byte(addFunc), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Bundle(outputDir, fd.Name(), addFd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle functino should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(addFunc)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
if pre != nil {
|
||||
t.Fatal(pre)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
FROM ruby:latest
|
||||
|
||||
RUN gem install sinatra
|
||||
FROM metrue/fx-ruby-base
|
||||
|
||||
COPY . .
|
||||
EXPOSE 3000
|
||||
8
bundler/ruby/ruby-packr.go
Normal file
8
bundler/ruby/ruby-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package ruby
|
||||
|
||||
import _ "github.com/metrue/fx/packrd"
|
||||
32
bundler/ruby/ruby.go
Normal file
32
bundler/ruby/ruby.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package ruby
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/metrue/fx/bundler"
|
||||
)
|
||||
|
||||
// Julia defines javascript bundler
|
||||
type Julia struct {
|
||||
assets *packr.Box
|
||||
}
|
||||
|
||||
// New a koa bundler
|
||||
func New() *Julia {
|
||||
return &Julia{
|
||||
assets: packr.New("ruby", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
// Scaffold a koa app
|
||||
func (k *Julia) Scaffold(output string) error {
|
||||
return bundler.Restore(k.assets, output)
|
||||
}
|
||||
|
||||
// Bundle a function into a koa project
|
||||
func (k *Julia) Bundle(output string, fn string, deps ...string) error {
|
||||
return bundler.Bundle(k.assets, output, "ruby", fn, deps...)
|
||||
}
|
||||
|
||||
var (
|
||||
_ bundler.Bundler = &Julia{}
|
||||
)
|
||||
148
bundler/ruby/ruby_test.go
Normal file
148
bundler/ruby/ruby_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package ruby
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/metrue/fx/utils"
|
||||
)
|
||||
|
||||
func TestJuliaBundler(t *testing.T) {
|
||||
t.Run("Scaffold", func(t *testing.T) {
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Scaffold(outputDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, _, _, err := utils.Diff(outputDir, "./assets")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff {
|
||||
t.Fatalf("%s is not equal with %s", outputDir, "./assets")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleSingleFunc", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.julia")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content := `struct Input
|
||||
a::Number
|
||||
b::Number
|
||||
end
|
||||
|
||||
fx = function(input::Input)
|
||||
return input.a + input.b
|
||||
end
|
||||
`
|
||||
err = ioutil.WriteFile(fd.Name(), []byte(content), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Bundle(outputDir, fd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle function should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(content)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
|
||||
preHandleFunc, err := ioutil.ReadFile("./assets/fx.rb")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(pre, preHandleFunc) {
|
||||
{
|
||||
}
|
||||
t.Fatalf("it should get %s but got %s", preHandleFunc, pre)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleFuncAndDeps", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content, err := ioutil.ReadFile("./assets/fx.rb")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ioutil.WriteFile(fd.Name(), content, 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addFunc := `
|
||||
module.exports = (a, b) => a+b
|
||||
`
|
||||
addFd, err := ioutil.TempFile("", "fx_add_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(addFd.Name(), []byte(addFunc), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Bundle(outputDir, fd.Name(), addFd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle functino should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(addFunc)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
if pre != nil {
|
||||
t.Fatal(pre)
|
||||
}
|
||||
})
|
||||
}
|
||||
8
bundler/rust/rust-packr.go
Normal file
8
bundler/rust/rust-packr.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build !skippackr
|
||||
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
package rust
|
||||
|
||||
import _ "github.com/metrue/fx/packrd"
|
||||
32
bundler/rust/rust.go
Normal file
32
bundler/rust/rust.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package rust
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/metrue/fx/bundler"
|
||||
)
|
||||
|
||||
// Julia defines javascript bundler
|
||||
type Julia struct {
|
||||
assets *packr.Box
|
||||
}
|
||||
|
||||
// New a koa bundler
|
||||
func New() *Julia {
|
||||
return &Julia{
|
||||
assets: packr.New("rust", "./assets"),
|
||||
}
|
||||
}
|
||||
|
||||
// Scaffold a koa app
|
||||
func (k *Julia) Scaffold(output string) error {
|
||||
return bundler.Restore(k.assets, output)
|
||||
}
|
||||
|
||||
// Bundle a function into a koa project
|
||||
func (k *Julia) Bundle(output string, fn string, deps ...string) error {
|
||||
return bundler.Bundle(k.assets, output, "rust", fn, deps...)
|
||||
}
|
||||
|
||||
var (
|
||||
_ bundler.Bundler = &Julia{}
|
||||
)
|
||||
148
bundler/rust/rust_test.go
Normal file
148
bundler/rust/rust_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package rust
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/metrue/fx/utils"
|
||||
)
|
||||
|
||||
func TestJuliaBundler(t *testing.T) {
|
||||
t.Run("Scaffold", func(t *testing.T) {
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Scaffold(outputDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, _, _, err := utils.Diff(outputDir, "./assets")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff {
|
||||
t.Fatalf("%s is not equal with %s", outputDir, "./assets")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleSingleFunc", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.julia")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content := `struct Input
|
||||
a::Number
|
||||
b::Number
|
||||
end
|
||||
|
||||
fx = function(input::Input)
|
||||
return input.a + input.b
|
||||
end
|
||||
`
|
||||
err = ioutil.WriteFile(fd.Name(), []byte(content), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Bundle(outputDir, fd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle function should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(content)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
|
||||
preHandleFunc, err := ioutil.ReadFile("./assets/src/fns/mod.rs")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(pre, preHandleFunc) {
|
||||
{
|
||||
}
|
||||
t.Fatalf("it should get %s but got %s", preHandleFunc, pre)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BundleFuncAndDeps", func(t *testing.T) {
|
||||
fd, err := ioutil.TempFile("", "fx_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
content, err := ioutil.ReadFile("./assets/src/fns/mod.rs")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ioutil.WriteFile(fd.Name(), content, 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addFunc := `
|
||||
module.exports = (a, b) => a+b
|
||||
`
|
||||
addFd, err := ioutil.TempFile("", "fx_add_func_*.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(addFd.Name(), []byte(addFunc), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "fx_julia")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
julia := New()
|
||||
if err := julia.Bundle(outputDir, fd.Name(), addFd.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, pre, cur, err := utils.Diff("./assets", outputDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
t.Fatalf("handle functino should be changed")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur, []byte(addFunc)) {
|
||||
t.Fatalf("it should be %s but got %s", content, cur)
|
||||
}
|
||||
if pre != nil {
|
||||
t.Fatal(pre)
|
||||
}
|
||||
})
|
||||
}
|
||||
195
config/config.go
195
config/config.go
@@ -1,195 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/metrue/fx/utils"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Items data of config file
|
||||
type Items struct {
|
||||
Clouds map[string]map[string]string `json:"clouds"`
|
||||
CurrentCloud string `json:"current_cloud"`
|
||||
}
|
||||
|
||||
// Config config of fx
|
||||
type Config struct {
|
||||
mux sync.Mutex
|
||||
configFile string
|
||||
Items
|
||||
}
|
||||
|
||||
// LoadDefault load default config
|
||||
func LoadDefault() (*Config, error) {
|
||||
configFile, err := homedir.Expand("~/.fx/config.yml")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if os.Getenv("FX_CONFIG") != "" {
|
||||
configFile = os.Getenv("FX_CONFIG")
|
||||
|
||||
}
|
||||
if _, err := os.Stat(configFile); os.IsNotExist(err) {
|
||||
if err := utils.EnsureFile(configFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := writeDefaultConfig(configFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return load(configFile)
|
||||
}
|
||||
|
||||
// Load config
|
||||
func Load(configFile string) (*Config, error) {
|
||||
if configFile == "" {
|
||||
return nil, fmt.Errorf("invalid config file")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(configFile); os.IsNotExist(err) {
|
||||
if err := utils.EnsureFile(configFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := writeDefaultConfig(configFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return load(configFile)
|
||||
}
|
||||
|
||||
// AddCloud add a cloud
|
||||
func (c *Config) addCloud(name string, cloud map[string]string) error {
|
||||
c.Items.Clouds[name] = cloud
|
||||
return save(c)
|
||||
}
|
||||
|
||||
// AddDockerCloud add docker cloud
|
||||
func (c *Config) AddDockerCloud(name string, config []byte) error {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
var conf map[string]string
|
||||
err := json.Unmarshal(config, &conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cloud := map[string]string{
|
||||
"type": "docker",
|
||||
"host": conf["ip"],
|
||||
"user": conf["user"],
|
||||
}
|
||||
return c.addCloud(name, cloud)
|
||||
}
|
||||
|
||||
// AddK8SCloud add k8s cloud
|
||||
func (c *Config) AddK8SCloud(name string, kubeconfig []byte) error {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
dir := path.Dir(c.configFile)
|
||||
kubecfg := path.Join(dir, name+".kubeconfig")
|
||||
if err := utils.EnsureFile(kubecfg); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(kubecfg, kubeconfig, 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cloud := map[string]string{
|
||||
"type": "k8s",
|
||||
"kubeConfig": kubecfg,
|
||||
}
|
||||
|
||||
return c.addCloud(name, cloud)
|
||||
}
|
||||
|
||||
// Use set cloud instance with name as current context
|
||||
func (c *Config) Use(name string) error {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
has := false
|
||||
for n := range c.Clouds {
|
||||
if n == name {
|
||||
has = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !has {
|
||||
return fmt.Errorf("no cloud with name = %s", name)
|
||||
}
|
||||
c.Items.CurrentCloud = name
|
||||
return save(c)
|
||||
}
|
||||
|
||||
// View view current config
|
||||
func (c *Config) View() ([]byte, error) {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
return ioutil.ReadFile(c.configFile)
|
||||
}
|
||||
|
||||
func load(configFile string) (*Config, error) {
|
||||
conf, err := ioutil.ReadFile(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var items Items
|
||||
if err := yaml.Unmarshal(conf, &items); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var c = Config{
|
||||
configFile: configFile,
|
||||
Items: items,
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func save(c *Config) error {
|
||||
conf, err := yaml.Marshal(c.Items)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(c.configFile, conf, 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeDefaultConfig(configFile string) error {
|
||||
me, err := user.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items := Items{
|
||||
Clouds: map[string]map[string]string{
|
||||
"default": map[string]string{
|
||||
"type": "docker",
|
||||
"host": "127.0.0.1",
|
||||
"user": me.Username,
|
||||
},
|
||||
},
|
||||
CurrentCloud: "default",
|
||||
}
|
||||
|
||||
body, err := yaml.Marshal(items)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(configFile, body, 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
configPath := "./tmp/config.yml"
|
||||
defer func() {
|
||||
if err := os.RemoveAll("./tmp"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
c, err := Load(configPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(c.Clouds) != 1 {
|
||||
t.Fatal("should contain default cloud")
|
||||
}
|
||||
|
||||
name := "fx_cluster_1"
|
||||
if err := c.Use(name); err == nil {
|
||||
t.Fatal("should get no such cloud error")
|
||||
}
|
||||
|
||||
if err := c.AddK8SCloud(name, []byte("sampe kubeconfg")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config := map[string]string{
|
||||
"ip": "127.0.0.1",
|
||||
"user": "use1",
|
||||
}
|
||||
configData, _ := json.Marshal(config)
|
||||
if err := c.AddDockerCloud("docker-1", configData); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := c.Use(name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c.CurrentCloud != name {
|
||||
t.Fatalf("should get %s but got %s", name, c.CurrentCloud)
|
||||
}
|
||||
|
||||
conf, err := Load(configPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if conf.CurrentCloud != name {
|
||||
t.Fatalf("should get %s but got %s", name, c.CurrentCloud)
|
||||
}
|
||||
|
||||
body, err := c.View()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
14
config/env.go
Normal file
14
config/env.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// DisableContainerAutoremove to tell if to run container with --rm
|
||||
var DisableContainerAutoremove = false
|
||||
|
||||
func init() {
|
||||
if os.Getenv("DISABLE_CONTAINER_AUTOREMOVE") == "true" {
|
||||
DisableContainerAutoremove = true
|
||||
}
|
||||
}
|
||||
17
config/env_test.go
Normal file
17
config/env_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var _ = func() (_ struct{}) {
|
||||
os.Setenv("DISABLE_CONTAINER_AUTOREMOVE", "true")
|
||||
return
|
||||
}()
|
||||
|
||||
func TestEnvLoad(t *testing.T) {
|
||||
if !DisableContainerAutoremove {
|
||||
t.Fatalf("should be true after set")
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package config
|
||||
|
||||
// CloudTypeDocker docker type
|
||||
const CloudTypeDocker = "docker"
|
||||
|
||||
// CloudTypeK8S k8s type
|
||||
const CloudTypeK8S = "k8s"
|
||||
15
constants/languages.go
Normal file
15
constants/languages.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package constants
|
||||
|
||||
// ExtLangMapping file extension mapping with programming language
|
||||
var ExtLangMapping = map[string]string{
|
||||
".js": "node",
|
||||
".go": "go",
|
||||
".rb": "ruby",
|
||||
".py": "python",
|
||||
".php": "php",
|
||||
".jl": "julia",
|
||||
".java": "java",
|
||||
".d": "d",
|
||||
".rs": "rust",
|
||||
".pl": "perl",
|
||||
}
|
||||
@@ -18,11 +18,11 @@ import (
|
||||
"github.com/apex/log"
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
dockerTypesContainer "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/google/go-querystring/query"
|
||||
"github.com/google/uuid"
|
||||
fxConfig "github.com/metrue/fx/config"
|
||||
containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||
"github.com/metrue/fx/types"
|
||||
"github.com/metrue/fx/utils"
|
||||
@@ -31,33 +31,57 @@ import (
|
||||
|
||||
// API interact with dockerd http api
|
||||
type API struct {
|
||||
host string
|
||||
port string
|
||||
endpoint string
|
||||
version string
|
||||
}
|
||||
|
||||
// New a API
|
||||
func New(host string, port string) *API {
|
||||
return &API{
|
||||
host: host,
|
||||
port: port,
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize an API
|
||||
func (api *API) Initialize() error {
|
||||
addr := api.host + ":" + api.port
|
||||
v, err := version(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoint := fmt.Sprintf("http://%s:%s/v%s", api.host, api.port, v)
|
||||
api.endpoint = endpoint
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a API
|
||||
func Create(host string, port string) (*API, error) {
|
||||
version, err := utils.DockerVersion(host, port)
|
||||
addr := host + ":" + port
|
||||
v, err := version(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endpoint := fmt.Sprintf("http://%s:%s/v%s", host, port, version)
|
||||
endpoint := fmt.Sprintf("http://%s:%s/v%s", host, port, v)
|
||||
return &API{
|
||||
endpoint: endpoint,
|
||||
version: version,
|
||||
version: v,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MustCreate a api object, panic if not
|
||||
func MustCreate(host string, port string) *API {
|
||||
version, err := utils.DockerVersion(host, port)
|
||||
addr := host + ":" + port
|
||||
v, err := version(addr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
endpoint := fmt.Sprintf("http://%s:%s/v%s", host, port, version)
|
||||
endpoint := fmt.Sprintf("http://%s:%s/v%s", host, port, v)
|
||||
return &API{
|
||||
endpoint: endpoint,
|
||||
version: version,
|
||||
version: v,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +153,46 @@ func (api *API) post(path string, body []byte, expectStatus int, v interface{})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Version get version of docker engine
|
||||
func (api *API) Version(ctx context.Context) (string, error) {
|
||||
addr := api.host + ":" + api.port
|
||||
return version(addr)
|
||||
}
|
||||
|
||||
func version(endpoint string) (string, error) {
|
||||
path := endpoint + "/version"
|
||||
if !strings.HasPrefix(path, "http") {
|
||||
path = "http://" + path
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", path, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
client := &http.Client{Timeout: 20 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("request %s failed: %d - %s", path, resp.StatusCode, resp.Status)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var res dockerTypes.Version
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return res.APIVersion, nil
|
||||
}
|
||||
|
||||
// ListContainer list service
|
||||
func (api *API) ListContainer(ctx context.Context, name string) ([]types.Service, error) {
|
||||
if name != "" {
|
||||
@@ -186,17 +250,27 @@ func (api *API) ListContainer(ctx context.Context, name string) ([]types.Service
|
||||
|
||||
svs := make(map[string]types.Service)
|
||||
for _, container := range containers {
|
||||
name := "UNKNOWN"
|
||||
if len(container.Names) > 0 {
|
||||
name = container.Names[0]
|
||||
}
|
||||
|
||||
port := -1
|
||||
ip := "UNKNOWN"
|
||||
if len(container.Ports) > 0 {
|
||||
ip = container.Ports[0].IP
|
||||
port = int(container.Ports[0].PublicPort)
|
||||
}
|
||||
|
||||
// container name have extra forward slash
|
||||
// https://github.com/moby/moby/issues/6705
|
||||
if strings.HasPrefix(container.Names[0], fmt.Sprintf("/%s", name)) {
|
||||
svs[container.Image] = types.Service{
|
||||
Name: container.Names[0],
|
||||
Image: container.Image,
|
||||
ID: container.ID,
|
||||
Host: container.Ports[0].IP,
|
||||
Port: int(container.Ports[0].PublicPort),
|
||||
State: container.State,
|
||||
}
|
||||
svs[container.Image] = types.Service{
|
||||
Name: name,
|
||||
Image: container.Image,
|
||||
ID: container.ID,
|
||||
Host: ip,
|
||||
Port: port,
|
||||
State: container.State,
|
||||
}
|
||||
}
|
||||
services := []types.Service{}
|
||||
@@ -345,13 +419,13 @@ func (api *API) StartContainer(ctx context.Context, name string, image string, b
|
||||
portSet[port] = struct{}{}
|
||||
portMap[port] = bindings
|
||||
}
|
||||
config := &dockerTypesContainer.Config{
|
||||
config := &container.Config{
|
||||
Image: image,
|
||||
ExposedPorts: portSet,
|
||||
}
|
||||
|
||||
hostConfig := &dockerTypesContainer.HostConfig{
|
||||
AutoRemove: true,
|
||||
hostConfig := &container.HostConfig{
|
||||
AutoRemove: !fxConfig.DisableContainerAutoremove,
|
||||
PortBindings: portMap,
|
||||
}
|
||||
|
||||
@@ -416,6 +490,10 @@ func (api *API) StopContainer(ctx context.Context, name string) error {
|
||||
|
||||
// InspectContainer inspect container
|
||||
func (api *API) InspectContainer(ctx context.Context, name string, container interface{}) error {
|
||||
path := fmt.Sprintf("/containers/%s/json", name)
|
||||
if err := api.get(path, "", &container); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,111 +1,31 @@
|
||||
package api
|
||||
|
||||
// func TestDockerHTTP(t *testing.T) {
|
||||
// const addr = "127.0.0.1"
|
||||
// const user = ""
|
||||
// const passord = ""
|
||||
// provisioner := provision.NewWithHost(addr, user, passord)
|
||||
// if err := utils.RunWithRetry(func() error {
|
||||
// if !provisioner.IsFxAgentRunning() {
|
||||
// if err := provisioner.StartFxAgent(); err != nil {
|
||||
// log.Infof("could not start fx agent on host: %s", err)
|
||||
// return err
|
||||
// }
|
||||
// log.Infof("fx agent started")
|
||||
// } else {
|
||||
// log.Infof("fx agent is running")
|
||||
// }
|
||||
// return nil
|
||||
// }, 2*time.Second, 10); err != nil {
|
||||
// t.Fatal(err)
|
||||
// } else {
|
||||
// defer provisioner.StopFxAgent()
|
||||
// }
|
||||
//
|
||||
// host := config.Host{Host: "127.0.0.1"}
|
||||
// api, err := Create(host.Host, constants.AgentPort)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// serviceName := "a-test-service"
|
||||
// project := types.Project{
|
||||
// Name: serviceName,
|
||||
// Language: "node",
|
||||
// Files: []types.ProjectSourceFile{
|
||||
// types.ProjectSourceFile{
|
||||
// Path: "Dockerfile",
|
||||
// Body: `
|
||||
// FROM metrue/fx-node-base
|
||||
//
|
||||
// COPY . .
|
||||
// EXPOSE 3000
|
||||
// CMD ["node", "app.js"]`,
|
||||
// IsHandler: false,
|
||||
// },
|
||||
// types.ProjectSourceFile{
|
||||
// Path: "app.js",
|
||||
// Body: `
|
||||
// const Koa = require('koa');
|
||||
// const bodyParser = require('koa-bodyparser');
|
||||
// const func = require('./fx');
|
||||
//
|
||||
// const app = new Koa();
|
||||
// app.use(bodyParser());
|
||||
// app.use(ctx => {
|
||||
// const msg = func(ctx.request.body);
|
||||
// ctx.body = msg;
|
||||
// });
|
||||
//
|
||||
// app.listen(3000);`,
|
||||
// IsHandler: false,
|
||||
// },
|
||||
// types.ProjectSourceFile{
|
||||
// Path: "fx.js",
|
||||
// Body: `
|
||||
// module.exports = (input) => {
|
||||
// return input.a + input.b
|
||||
// }
|
||||
// `,
|
||||
// IsHandler: true,
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// service, err := api.Build(project)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if service.Name != serviceName {
|
||||
// t.Fatalf("should get %s but got %s", serviceName, service.Name)
|
||||
// }
|
||||
//
|
||||
// if err := api.Run(9999, &service); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// services, err := api.ListContainer(serviceName)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if len(services) != 1 {
|
||||
// t.Fatal("service number should be 1")
|
||||
// }
|
||||
//
|
||||
// if err := api.Stop(serviceName); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// const network = "fx-net"
|
||||
// if err := api.CreateNetwork(network); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// nws, err := api.GetNetwork(network)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if nws[0].Name != network {
|
||||
// t.Fatalf("should get %s but got %s", network, nws[0].Name)
|
||||
// }
|
||||
// }
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
func TestDockerHTTP(t *testing.T) {
|
||||
host := os.Getenv("DOCKER_ENGINE_HOST")
|
||||
port := os.Getenv("DOCKER_ENGINE_PORT")
|
||||
if host == "" ||
|
||||
port == "" {
|
||||
t.Skip("DOCKER_ENGINE_HOST and DOCKER_ENGINE_PORT required")
|
||||
}
|
||||
|
||||
api, err := Create(host, port)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
name := "fx-agent"
|
||||
var container types.ContainerJSON
|
||||
if err := api.InspectContainer(context.Background(), name, &container); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if container.Name != "/"+name {
|
||||
t.Fatalf("should get %s but got %s", name, container.Name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/google/uuid"
|
||||
fxConfig "github.com/metrue/fx/config"
|
||||
containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||
"github.com/metrue/fx/types"
|
||||
"github.com/metrue/fx/utils"
|
||||
@@ -161,7 +162,7 @@ func (d *Docker) StartContainer(ctx context.Context, name string, image string,
|
||||
}
|
||||
|
||||
hostConfig := &dockerTypesContainer.HostConfig{
|
||||
AutoRemove: true,
|
||||
AutoRemove: !fxConfig.DisableContainerAutoremove,
|
||||
PortBindings: portMap,
|
||||
}
|
||||
resp, err := d.ContainerCreate(ctx, config, hostConfig, nil, name)
|
||||
@@ -189,7 +190,16 @@ func (d *Docker) StopContainer(ctx context.Context, name string) error {
|
||||
|
||||
// InspectContainer inspect a container
|
||||
func (d *Docker) InspectContainer(ctx context.Context, name string, container interface{}) error {
|
||||
return nil
|
||||
res, err := d.ContainerInspect(ctx, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
body, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(body, &container)
|
||||
}
|
||||
|
||||
// ListContainer list containers
|
||||
@@ -226,6 +236,15 @@ func (d *Docker) ListContainer(ctx context.Context, name string) ([]types.Servic
|
||||
return services, nil
|
||||
}
|
||||
|
||||
// Version get version of docker engine
|
||||
func (d *Docker) Version(ctx context.Context) (string, error) {
|
||||
ping, err := d.Ping(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ping.APIVersion, nil
|
||||
}
|
||||
|
||||
var (
|
||||
_ containerruntimes.ContainerRuntime = &Docker{}
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/metrue/fx/types"
|
||||
)
|
||||
|
||||
func TestDocker(t *testing.T) {
|
||||
@@ -42,6 +43,23 @@ func TestDocker(t *testing.T) {
|
||||
t.Fatalf("should have built image with tag %s", name)
|
||||
}
|
||||
|
||||
if err := cli.StartContainer(ctx, name, name, []types.PortBinding{
|
||||
types.PortBinding{
|
||||
ServiceBindingPort: 9000,
|
||||
ContainerExposePort: 3000,
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var container dockerTypes.ContainerJSON
|
||||
if err := cli.InspectContainer(ctx, name, &container); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if container.Name != "/"+name {
|
||||
t.Fatalf("should get %s but got %s", "/"+name, container.Name)
|
||||
}
|
||||
|
||||
username := os.Getenv("DOCKER_USERNAME")
|
||||
password := os.Getenv("DOCKER_PASSWORD")
|
||||
if username == "" || password == "" {
|
||||
|
||||
164
container_runtimes/mocks/runtime.go
Normal file
164
container_runtimes/mocks/runtime.go
Normal file
@@ -0,0 +1,164 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: runtimes.go
|
||||
|
||||
// Package mock_containerruntimes is a generated GoMock package.
|
||||
package mock_containerruntimes
|
||||
|
||||
import (
|
||||
context "context"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
types "github.com/metrue/fx/types"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockContainerRuntime is a mock of ContainerRuntime interface
|
||||
type MockContainerRuntime struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockContainerRuntimeMockRecorder
|
||||
}
|
||||
|
||||
// MockContainerRuntimeMockRecorder is the mock recorder for MockContainerRuntime
|
||||
type MockContainerRuntimeMockRecorder struct {
|
||||
mock *MockContainerRuntime
|
||||
}
|
||||
|
||||
// NewMockContainerRuntime creates a new mock instance
|
||||
func NewMockContainerRuntime(ctrl *gomock.Controller) *MockContainerRuntime {
|
||||
mock := &MockContainerRuntime{ctrl: ctrl}
|
||||
mock.recorder = &MockContainerRuntimeMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockContainerRuntime) EXPECT() *MockContainerRuntimeMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// BuildImage mocks base method
|
||||
func (m *MockContainerRuntime) BuildImage(ctx context.Context, workdir, name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "BuildImage", ctx, workdir, name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// BuildImage indicates an expected call of BuildImage
|
||||
func (mr *MockContainerRuntimeMockRecorder) BuildImage(ctx, workdir, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildImage", reflect.TypeOf((*MockContainerRuntime)(nil).BuildImage), ctx, workdir, name)
|
||||
}
|
||||
|
||||
// PushImage mocks base method
|
||||
func (m *MockContainerRuntime) PushImage(ctx context.Context, name string) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "PushImage", ctx, name)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// PushImage indicates an expected call of PushImage
|
||||
func (mr *MockContainerRuntimeMockRecorder) PushImage(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PushImage", reflect.TypeOf((*MockContainerRuntime)(nil).PushImage), ctx, name)
|
||||
}
|
||||
|
||||
// InspectImage mocks base method
|
||||
func (m *MockContainerRuntime) InspectImage(ctx context.Context, name string, img interface{}) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InspectImage", ctx, name, img)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// InspectImage indicates an expected call of InspectImage
|
||||
func (mr *MockContainerRuntimeMockRecorder) InspectImage(ctx, name, img interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InspectImage", reflect.TypeOf((*MockContainerRuntime)(nil).InspectImage), ctx, name, img)
|
||||
}
|
||||
|
||||
// TagImage mocks base method
|
||||
func (m *MockContainerRuntime) TagImage(ctx context.Context, name, tag string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "TagImage", ctx, name, tag)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// TagImage indicates an expected call of TagImage
|
||||
func (mr *MockContainerRuntimeMockRecorder) TagImage(ctx, name, tag interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TagImage", reflect.TypeOf((*MockContainerRuntime)(nil).TagImage), ctx, name, tag)
|
||||
}
|
||||
|
||||
// StartContainer mocks base method
|
||||
func (m *MockContainerRuntime) StartContainer(ctx context.Context, name, image string, bindings []types.PortBinding) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "StartContainer", ctx, name, image, bindings)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// StartContainer indicates an expected call of StartContainer
|
||||
func (mr *MockContainerRuntimeMockRecorder) StartContainer(ctx, name, image, bindings interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartContainer", reflect.TypeOf((*MockContainerRuntime)(nil).StartContainer), ctx, name, image, bindings)
|
||||
}
|
||||
|
||||
// StopContainer mocks base method
|
||||
func (m *MockContainerRuntime) StopContainer(ctx context.Context, name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "StopContainer", ctx, name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// StopContainer indicates an expected call of StopContainer
|
||||
func (mr *MockContainerRuntimeMockRecorder) StopContainer(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopContainer", reflect.TypeOf((*MockContainerRuntime)(nil).StopContainer), ctx, name)
|
||||
}
|
||||
|
||||
// InspectContainer mocks base method
|
||||
func (m *MockContainerRuntime) InspectContainer(ctx context.Context, name string, container interface{}) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InspectContainer", ctx, name, container)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// InspectContainer indicates an expected call of InspectContainer
|
||||
func (mr *MockContainerRuntimeMockRecorder) InspectContainer(ctx, name, container interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InspectContainer", reflect.TypeOf((*MockContainerRuntime)(nil).InspectContainer), ctx, name, container)
|
||||
}
|
||||
|
||||
// ListContainer mocks base method
|
||||
func (m *MockContainerRuntime) ListContainer(ctx context.Context, filter string) ([]types.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListContainer", ctx, filter)
|
||||
ret0, _ := ret[0].([]types.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ListContainer indicates an expected call of ListContainer
|
||||
func (mr *MockContainerRuntimeMockRecorder) ListContainer(ctx, filter interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListContainer", reflect.TypeOf((*MockContainerRuntime)(nil).ListContainer), ctx, filter)
|
||||
}
|
||||
|
||||
// Version mocks base method
|
||||
func (m *MockContainerRuntime) Version(ctx context.Context) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Version", ctx)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Version indicates an expected call of Version
|
||||
func (mr *MockContainerRuntimeMockRecorder) Version(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Version", reflect.TypeOf((*MockContainerRuntime)(nil).Version), ctx)
|
||||
}
|
||||
@@ -16,4 +16,5 @@ type ContainerRuntime interface {
|
||||
StopContainer(ctx context.Context, name string) error
|
||||
InspectContainer(ctx context.Context, name string, container interface{}) error
|
||||
ListContainer(ctx context.Context, filter string) ([]types.Service, error)
|
||||
Version(ctx context.Context) (string, error)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,15 @@ const (
|
||||
keyCliCtx = key("cmd_cli")
|
||||
)
|
||||
|
||||
// Contexter ctx interface
|
||||
type Contexter interface {
|
||||
Get(k string) interface{}
|
||||
Set(k string, v interface{})
|
||||
Use(fn func(ctx *Context) error) error
|
||||
GetContext() context.Context
|
||||
GetCliContext() *cli.Context
|
||||
}
|
||||
|
||||
// Context fx context
|
||||
type Context struct {
|
||||
context.Context
|
||||
@@ -43,6 +52,7 @@ func (ctx *Context) GetCliContext() *cli.Context {
|
||||
|
||||
// Set a value with name
|
||||
func (ctx *Context) Set(name string, value interface{}) {
|
||||
// nolint
|
||||
newCtx := context.WithValue(ctx.Context, name, value)
|
||||
ctx.Context = newCtx
|
||||
}
|
||||
@@ -56,3 +66,12 @@ func (ctx *Context) Get(name string) interface{} {
|
||||
func (ctx *Context) Use(fn func(ctx *Context) error) error {
|
||||
return fn(ctx)
|
||||
}
|
||||
|
||||
// GetContext get context
|
||||
func (ctx *Context) GetContext() context.Context {
|
||||
return ctx.Context
|
||||
}
|
||||
|
||||
var (
|
||||
_ Contexter = &Context{}
|
||||
)
|
||||
|
||||
@@ -22,4 +22,8 @@ func TestContext(t *testing.T) {
|
||||
if v != value {
|
||||
t.Fatalf("should get %v but %v", value, v)
|
||||
}
|
||||
|
||||
if ctx.GetContext() == nil {
|
||||
t.Fatalf("should get context")
|
||||
}
|
||||
}
|
||||
|
||||
104
context/mocks/context.go
Normal file
104
context/mocks/context.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: context.go
|
||||
|
||||
// Package mock_context is a generated GoMock package.
|
||||
package mock_context
|
||||
|
||||
import (
|
||||
context "context"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
context0 "github.com/metrue/fx/context"
|
||||
cli "github.com/urfave/cli"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockContexter is a mock of Contexter interface
|
||||
type MockContexter struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockContexterMockRecorder
|
||||
}
|
||||
|
||||
// MockContexterMockRecorder is the mock recorder for MockContexter
|
||||
type MockContexterMockRecorder struct {
|
||||
mock *MockContexter
|
||||
}
|
||||
|
||||
// NewMockContexter creates a new mock instance
|
||||
func NewMockContexter(ctrl *gomock.Controller) *MockContexter {
|
||||
mock := &MockContexter{ctrl: ctrl}
|
||||
mock.recorder = &MockContexterMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockContexter) EXPECT() *MockContexterMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Get mocks base method
|
||||
func (m *MockContexter) Get(k string) interface{} {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Get", k)
|
||||
ret0, _ := ret[0].(interface{})
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Get indicates an expected call of Get
|
||||
func (mr *MockContexterMockRecorder) Get(k interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockContexter)(nil).Get), k)
|
||||
}
|
||||
|
||||
// Set mocks base method
|
||||
func (m *MockContexter) Set(k string, v interface{}) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "Set", k, v)
|
||||
}
|
||||
|
||||
// Set indicates an expected call of Set
|
||||
func (mr *MockContexterMockRecorder) Set(k, v interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockContexter)(nil).Set), k, v)
|
||||
}
|
||||
|
||||
// Use mocks base method
|
||||
func (m *MockContexter) Use(fn func(*context0.Context) error) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Use", fn)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Use indicates an expected call of Use
|
||||
func (mr *MockContexterMockRecorder) Use(fn interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Use", reflect.TypeOf((*MockContexter)(nil).Use), fn)
|
||||
}
|
||||
|
||||
// GetContext mocks base method
|
||||
func (m *MockContexter) GetContext() context.Context {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetContext")
|
||||
ret0, _ := ret[0].(context.Context)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetContext indicates an expected call of GetContext
|
||||
func (mr *MockContexterMockRecorder) GetContext() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContext", reflect.TypeOf((*MockContexter)(nil).GetContext))
|
||||
}
|
||||
|
||||
// GetCliContext mocks base method
|
||||
func (m *MockContexter) GetCliContext() *cli.Context {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetCliContext")
|
||||
ret0, _ := ret[0].(*cli.Context)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetCliContext indicates an expected call of GetCliContext
|
||||
func (mr *MockContexterMockRecorder) GetCliContext() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCliContext", reflect.TypeOf((*MockContexter)(nil).GetCliContext))
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
types "github.com/metrue/fx/types"
|
||||
)
|
||||
|
||||
// Deployer make a image a service
|
||||
type Deployer interface {
|
||||
Deploy(ctx context.Context, fn types.Func, name string, image string, bindings []types.PortBinding) error
|
||||
Destroy(ctx context.Context, name string) error
|
||||
Update(ctx context.Context, name string) error
|
||||
GetStatus(ctx context.Context, name string) error
|
||||
List(ctx context.Context, name string) ([]types.Service, error)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||
"github.com/metrue/fx/deploy"
|
||||
"github.com/metrue/fx/types"
|
||||
)
|
||||
|
||||
// Docker manage container
|
||||
type Docker struct {
|
||||
cli containerruntimes.ContainerRuntime
|
||||
}
|
||||
|
||||
// CreateClient create a docker instance
|
||||
func CreateClient(client containerruntimes.ContainerRuntime) (d *Docker, err error) {
|
||||
return &Docker{cli: client}, nil
|
||||
}
|
||||
|
||||
// Deploy create a Docker container from given image, and bind the constants.FxContainerExposePort to given port
|
||||
func (d *Docker) Deploy(ctx context.Context, fn types.Func, name string, image string, ports []types.PortBinding) error {
|
||||
return d.cli.StartContainer(ctx, name, image, ports)
|
||||
}
|
||||
|
||||
// Update a container
|
||||
func (d *Docker) Update(ctx context.Context, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroy stop and remove container
|
||||
func (d *Docker) Destroy(ctx context.Context, name string) error {
|
||||
return d.cli.StopContainer(ctx, name)
|
||||
}
|
||||
|
||||
// GetStatus get status of container
|
||||
func (d *Docker) GetStatus(ctx context.Context, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// List services
|
||||
func (d *Docker) List(ctx context.Context, name string) ([]types.Service, error) {
|
||||
// FIXME support remote host
|
||||
return d.cli.ListContainer(ctx, name)
|
||||
}
|
||||
|
||||
var (
|
||||
_ deploy.Deployer = &Docker{}
|
||||
)
|
||||
@@ -1,43 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDocker(t *testing.T) {
|
||||
// ctx := context.Background()
|
||||
// cli, err := CreateClient(ctx)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// name := "helloworld"
|
||||
// bindings := []types.PortBinding{
|
||||
// types.PortBinding{
|
||||
// ServiceBindingPort: 80,
|
||||
// ContainerExposePort: 3000,
|
||||
// },
|
||||
// types.PortBinding{
|
||||
// ServiceBindingPort: 443,
|
||||
// ContainerExposePort: 3000,
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// fn := types.Func{
|
||||
// Language: "node",
|
||||
// Source: `
|
||||
// module.exports = (ctx) => {
|
||||
// ctx.body = 'hello world'
|
||||
// }
|
||||
// `,
|
||||
// }
|
||||
// if err := cli.Deploy(ctx, fn, name, name, bindings); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// time.Sleep(1 * time.Second)
|
||||
//
|
||||
// if err := cli.Destroy(ctx, name); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package k3s
|
||||
|
||||
// ConfigMap is the key to function docker project source code in configmap
|
||||
var ConfigMap = struct {
|
||||
AppMetaEnvName string
|
||||
}{
|
||||
AppMetaEnvName: "APP_META",
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package k3s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/metrue/fx/types"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func generateDeploymentSpec(
|
||||
name string,
|
||||
image string,
|
||||
bindPorts []types.PortBinding,
|
||||
replicas int32,
|
||||
selector map[string]string,
|
||||
) *appsv1.Deployment {
|
||||
ports := []apiv1.ContainerPort{}
|
||||
for index, binding := range bindPorts {
|
||||
ports = append(ports, apiv1.ContainerPort{
|
||||
Name: fmt.Sprintf("fx-container-%d", index),
|
||||
ContainerPort: binding.ContainerExposePort,
|
||||
})
|
||||
}
|
||||
|
||||
container := apiv1.Container{
|
||||
Name: "fx-placeholder-container-name",
|
||||
Image: image,
|
||||
Ports: ports,
|
||||
ImagePullPolicy: v1.PullIfNotPresent,
|
||||
}
|
||||
return &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Replicas: &replicas,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: selector,
|
||||
},
|
||||
Template: apiv1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: selector,
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{container},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetDeployment get a deployment
|
||||
func (k *K3S) GetDeployment(namespace string, name string) (*appsv1.Deployment, error) {
|
||||
return k.AppsV1().Deployments(namespace).Get(name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// CreateDeployment create a deployment
|
||||
func (k *K3S) CreateDeployment(
|
||||
namespace string,
|
||||
name string,
|
||||
image string,
|
||||
ports []types.PortBinding,
|
||||
replicas int32,
|
||||
selector map[string]string,
|
||||
) (*appsv1.Deployment, error) {
|
||||
deployment := generateDeploymentSpec(name, image, ports, replicas, selector)
|
||||
return k.AppsV1().Deployments(namespace).Create(deployment)
|
||||
}
|
||||
|
||||
// UpdateDeployment update a deployment
|
||||
func (k *K3S) UpdateDeployment(
|
||||
namespace string,
|
||||
name string,
|
||||
image string,
|
||||
ports []types.PortBinding,
|
||||
replicas int32,
|
||||
selector map[string]string,
|
||||
) (*appsv1.Deployment, error) {
|
||||
deployment := generateDeploymentSpec(name, image, ports, replicas, selector)
|
||||
return k.AppsV1().Deployments(namespace).Update(deployment)
|
||||
}
|
||||
|
||||
// DeleteDeployment delete a deployment
|
||||
func (k *K3S) DeleteDeployment(namespace string, name string) error {
|
||||
return k.AppsV1().Deployments(namespace).Delete(name, &metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
// CreateDeploymentWithInitContainer create a deployment which will wait InitContainer to do the image build before function container start
|
||||
func (k *K3S) CreateDeploymentWithInitContainer(
|
||||
namespace string,
|
||||
name string,
|
||||
ports []types.PortBinding,
|
||||
replicas int32,
|
||||
selector map[string]string,
|
||||
) (*appsv1.Deployment, error) {
|
||||
deployment := generateDeploymentSpec(name, name, ports, replicas, selector)
|
||||
updatedDeployment := injectInitContainer(name, deployment)
|
||||
return k.AppsV1().Deployments(namespace).Create(updatedDeployment)
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package k3s
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/metrue/fx/types"
|
||||
)
|
||||
|
||||
func TestDeployment(t *testing.T) {
|
||||
namespace := "default"
|
||||
name := "fx-hello-world"
|
||||
image := "metrue/kube-hello"
|
||||
selector := map[string]string{
|
||||
"app": "fx-app",
|
||||
}
|
||||
|
||||
kubeconfig := os.Getenv("KUBECONFIG")
|
||||
if kubeconfig == "" {
|
||||
t.Skip("skip test since no KUBECONFIG given in environment variable")
|
||||
}
|
||||
|
||||
k8s, err := Create("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := k8s.GetDeployment(namespace, name); err == nil {
|
||||
t.Fatalf("should get not found error")
|
||||
}
|
||||
|
||||
replicas := int32(2)
|
||||
bindings := []types.PortBinding{
|
||||
types.PortBinding{
|
||||
ServiceBindingPort: 80,
|
||||
ContainerExposePort: 3000,
|
||||
},
|
||||
types.PortBinding{
|
||||
ServiceBindingPort: 443,
|
||||
ContainerExposePort: 3000,
|
||||
},
|
||||
}
|
||||
deployment, err := k8s.CreateDeployment(namespace, name, image, bindings, replicas, selector)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if deployment == nil {
|
||||
t.Fatalf("deploymetn should not be %v", nil)
|
||||
}
|
||||
|
||||
if deployment.Name != name {
|
||||
t.Fatalf("should get %s but got %s", name, deployment.Name)
|
||||
}
|
||||
|
||||
if *deployment.Spec.Replicas != replicas {
|
||||
t.Fatalf("should get %v but got %v", replicas, deployment.Spec.Replicas)
|
||||
}
|
||||
|
||||
if err := k8s.DeleteDeployment(namespace, name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user