Compare commits

..

7 Commits

Author SHA1 Message Date
Minghe Huang
91fd5dc59f bump version 2019-10-14 16:49:29 +08:00
Changxin Miao
184235acb2 Automatically notify user of new release (#317)
* Automatically notify user of new release

Signed-off-by: Changxin Miao <mcx_221@foxmail.com>

* Update naming convention
2019-10-14 13:38:07 +08:00
Minghe
aa49a59feb * kuberntes has some limitation on naming,By convention, the names of Kubernetes resources should be up to maximum length of 253 characters and consist of lower case alphanumeric characters, -, and ., but certain resources have more specific restrictions. (#322)
* skip run deploy when KUBECONFIG, DOCKER_USERNAME, and DOCKER_PASSWORD is not ready
2019-10-14 13:21:27 +08:00
Minghe
c9d382d903 Skip test when no credentials ready (#320)
* Since fork PR build could not read secrets of GitHub action,
https://github.community/t5/GitHub-Actions/Allow-secrets-to-be-shared-with-trusted-Actions/td-p/34278
So skip test when its credentials are nod ready

* skip deploy test when no DOCKER_USERNAME and DOCKER_PASSWORD found
2019-10-14 12:37:06 +08:00
Changxin Miao
81e18e5b0d Deployment selector should be immutable (#316)
Signed-off-by: Changxin Miao <mcx_221@foxmail.com>
2019-10-14 11:55:08 +08:00
Minghe
3882f843bf fix lint issue (#319) 2019-10-14 10:20:12 +08:00
Minghe
293481f081 release 0.7.3 (#311) 2019-10-12 21:41:49 +08:00
13 changed files with 93 additions and 49 deletions

View File

@@ -1,4 +1,4 @@
on: push
on: [push, pull_request]
name: ci
jobs:
Test:
@@ -61,9 +61,14 @@ jobs:
run: |
export KUBECONFIG=${HOME}/.kube/aks
echo ${AKS_KUBECONFIG} | base64 -d > $KUBECONFIG
DEBUG=true ./build/fx deploy -n hello -p 12345 examples/functions/JavaScript/func.js
./build/fx destroy hello
rm ${KUBECONFIG}
if [[ -z "$DOCKER_USERNAME" || -z "$DOCKER_PASSWORD" || -z "$AKS_KUBECONFIG" ]];then
echo "skip deploy test since no DOCKER_USERNAME and DOCKER_PASSWORD set"
else
DEBUG=true ./build/fx deploy -n hello -p 12345 examples/functions/JavaScript/func.js
./build/fx destroy hello
rm ${KUBECONFIG}
fi
Installation:
runs-on: ${{ matrix.os }}
needs: [Test]

View File

@@ -1,5 +1,4 @@
run:
concurrency: 4
deadline: 10m
timeout: 10m
issues-exit-code: 1
@@ -7,21 +6,25 @@ run:
skip-dirs:
- examples
- api/images
- test
# skip-files:
- test/functions
linters:
enable:
- megacheck
- govet
- deadcode
# - gocyclo
- golint
- varcheck
- structcheck
- errcheck
- dupl
- ineffassign
- goimports
- stylecheck
- gosec
- interfacer
- unconvert
enable-all: false
- goconst
- gocyclo
- misspell
- unparam
issues:
exclude-rules:
- path: _test\.go
linters:
- gocyclo
- goconst
- errcheck
- dupl
- gosec

View File

@@ -63,7 +63,7 @@ func (c *Config) Init() error {
}
if err := viper.ReadInConfig(); err != nil {
return fmt.Errorf("Fatal error config file: %s", err)
return fmt.Errorf("fatal error config file: %s", err)
}
return nil
}

View File

@@ -69,10 +69,9 @@ func (d *Docker) BuildImage(ctx context.Context, workdir string, name string) er
if err != nil {
return err
}
defer resp.Body.Close()
if os.Getenv("DEBUG") != "" {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
@@ -88,10 +87,10 @@ func (d *Docker) PushImage(ctx context.Context, name string) (string, error) {
username := os.Getenv("DOCKER_USERNAME")
password := os.Getenv("DOCKER_PASSWORD")
if username == "" || password == "" {
return "", fmt.Errorf("DOCKER_USERNAME and DOCKER_PASSWORD required for push image to registy")
return "", fmt.Errorf("DOCKER_USERNAME and DOCKER_PASSWORD required for push image to registry")
}
// TODO support private registy, like Azure Container registry
// TODO support private registry, like Azure Container registry
authConfig := dockerTypes.AuthConfig{
Username: username,
Password: password,

View File

@@ -45,7 +45,7 @@ func TestDocker(t *testing.T) {
username := os.Getenv("DOCKER_USERNAME")
password := os.Getenv("DOCKER_PASSWORD")
if username == "" || password == "" {
t.Skip("Skip push image test since DOCKER_USERNAME and DOCKER_PASSWORD not set in enviroment variable")
t.Skip("Skip push image test since DOCKER_USERNAME and DOCKER_PASSWORD not set in environment variable")
}
img, err := cli.PushImage(ctx, name)

View File

@@ -2,10 +2,7 @@ package kubernetes
import (
"context"
"fmt"
"os"
"github.com/google/uuid"
runtime "github.com/metrue/fx/container_runtimes/docker/sdk"
"github.com/metrue/fx/deploy"
"k8s.io/client-go/kubernetes"
@@ -17,14 +14,11 @@ type K8S struct {
*kubernetes.Clientset
}
const namespace = "default"
// Create a k8s cluster client
func Create() (*K8S, error) {
kubeconfig := os.Getenv("KUBECONFIG")
if kubeconfig == "" {
return nil, fmt.Errorf("KUBECONFIG not given")
}
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
config, err := clientcmd.BuildConfigFromKubeconfigGetter("", clientcmd.NewDefaultClientConfigLoadingRules().Load)
if err != nil {
return nil, err
}
@@ -43,8 +37,6 @@ func (k *K8S) Deploy(
name string,
ports []int32,
) error {
namespace := "default"
dockerClient, err := runtime.CreateClient(ctx)
if err != nil {
return err
@@ -61,7 +53,7 @@ func (k *K8S) Deploy(
// be created automatically, then incoming traffic to Service will be forward to Pod.
// Then we have no need to create Endpoint manually anymore.
selector := map[string]string{
"app": "fx-app-" + uuid.New().String(),
"app": "fx-app-" + name,
}
const replicas = int32(3)
@@ -121,7 +113,6 @@ func (k *K8S) Update(ctx context.Context, name string) error {
// Destroy a service
func (k *K8S) Destroy(ctx context.Context, name string) error {
const namespace = "default"
if err := k.DeleteService(namespace, name); err != nil {
return err
}

View File

@@ -6,13 +6,15 @@ import (
"testing"
)
func TestK8SRunner(t *testing.T) {
func TestK8SDeployer(t *testing.T) {
workdir := "./fixture"
name := "hello"
ports := []int32{32300}
kubeconfig := os.Getenv("KUBECONFIG")
if kubeconfig == "" {
t.Skip("skip test since no KUBECONFIG given in environment variable")
username := os.Getenv("DOCKER_USERNAME")
password := os.Getenv("DOCKER_PASSWORD")
if kubeconfig == "" || username == "" || password == "" {
t.Skip("skip test since no KUBECONFIG, DOCKER_USERNAME and DOCKER_PASSWORD given in environment variable")
}
k8s, err := Create()
if err != nil {

38
fx.go
View File

@@ -1,8 +1,12 @@
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"path"
"regexp"
"github.com/apex/log"
"github.com/google/uuid"
@@ -11,9 +15,12 @@ import (
"github.com/urfave/cli"
)
const version = "0.7.4"
var cfg *config.Config
func init() {
go checkForUpdate()
configDir := path.Join(os.Getenv("HOME"), ".fx")
cfg := config.New(configDir)
@@ -23,11 +30,40 @@ func init() {
}
}
func checkForUpdate() {
const releaseURL = "https://api.github.com/repos/metrue/fx/releases/latest"
resp, err := http.Get(releaseURL)
if err != nil {
log.Debugf("Failed to fetch Github release page, error %v", err)
return
}
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
var releaseJSON struct {
Tag string `json:"tag_name"`
URL string `json:"html_url"`
}
if err := decoder.Decode(&releaseJSON); err != nil {
log.Debugf("Failed to decode Github release page JSON, error %v", err)
return
}
if matched, err := regexp.MatchString(`^(\d+\.)(\d+\.)(\d+)$`, releaseJSON.Tag); err != nil || !matched {
log.Debugf("Unofficial release %s?", releaseJSON.Tag)
return
}
log.Debugf("Latest release tag is %s", releaseJSON.Tag)
if releaseJSON.Tag != version {
fmt.Fprintf(os.Stderr, "\nfx %s is available (you're using %s), get the latest release from: %s\n",
releaseJSON.Tag, version, releaseJSON.URL)
}
}
func main() {
app := cli.NewApp()
app.Name = "fx"
app.Usage = "makes function as a service"
app.Version = "0.7.2"
app.Version = version
app.Commands = []cli.Command{
{

View File

@@ -33,7 +33,7 @@ func Deploy(cfg config.Configer) HandleFunc {
}
if err != nil {
log.Fatalf("deploy function %s (%s) failed: %v", err)
log.Fatalf("deploy function %s (%s) failed: %v", name, funcFile, err)
}
log.Infof("function %s (%s) deployed successfully", name, funcFile)
}()

View File

@@ -14,7 +14,7 @@ type DockerPacker struct {
box packr.Box
}
func isHandler(lang string, name string) bool {
func isHandler(name string) bool {
basename := filepath.Base(name)
nameWithoutExt := strings.TrimSuffix(basename, filepath.Ext(basename))
return nameWithoutExt == "fx" ||
@@ -39,7 +39,7 @@ func (p *DockerPacker) Pack(serviceName string, fn types.ServiceFunctionSource)
}
// if preset's file is handler function of project, replace it with give one
if isHandler(fn.Language, name) {
if isHandler(name) {
files = append(files, types.ProjectSourceFile{
Path: strings.Replace(name, prefix, "", 1),
Body: fn.Source,

View File

@@ -42,8 +42,10 @@ func (l *LocalRunner) Run(script string) ([]byte, error) {
params := strings.Split(script, " ")
var cmd *exec.Cmd
if len(params) > 1 {
// nolint: gosec
cmd = exec.Command(params[0], params[1:]...)
} else {
// nolint: gosec
cmd = exec.Command(params[0])
}
return cmd.CombinedOutput()

View File

@@ -16,9 +16,13 @@ run() {
deploy() {
local lang=$1
local port=$2
$fx deploy --name ${service}_${lang} --port ${port} test/functions/func.${lang}
docker ps
$fx destroy ${service}_${lang}
if [[ -z "$DOCKER_USERNAME" || -z "$DOCKER_PASSWORD" ]];then
echo "skip deploy test since no DOCKER_USERNAME and DOCKER_PASSWORD set"
else
$fx deploy --name ${service}-${lang} --port ${port} test/functions/func.${lang}
docker ps
$fx destroy ${service}-${lang}
fi
}
build_image() {

View File

@@ -22,6 +22,7 @@ func Download(filepath string, url string) (err error) {
}
defer out.Close()
// nolint: gosec
resp, err := http.Get(url)
if err != nil {
return err
@@ -48,6 +49,7 @@ func Unzip(source string, target string) (err error) {
}
for _, file := range reader.File {
//nolint: gosec
path := filepath.Join(target, file.Name)
if file.FileInfo().IsDir() {
if err := os.MkdirAll(path, file.Mode()); err != nil {
@@ -262,7 +264,7 @@ func PairsToParams(pairs []string) map[string]string {
func OutputJSON(v interface{}) error {
bytes, err := json.MarshalIndent(v, "", "\t")
if err != nil {
return fmt.Errorf("Could marshal %v : %v", v, err)
return fmt.Errorf("could marshal %v : %v", v, err)
}
fmt.Println(string(bytes))