mirror of
https://gitee.com/rainbond/Rainbond.git
synced 2024-11-30 10:48:15 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
f943fffb69
45
.gitignore
vendored
Normal file
45
.gitignore
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
||||
|
||||
# Godeps workspace
|
||||
/Godeps/_workspace
|
||||
|
||||
.DS_Store
|
||||
tags
|
||||
|
||||
# direnv .envrc files
|
||||
.envrc
|
||||
|
||||
# make-related metadata
|
||||
/.make/
|
||||
|
||||
# precommit temporary directories created by ./hack/verify-generated-docs.sh and ./hack/lib/util.sh
|
||||
/_tmp/
|
||||
/doc_tmp/
|
||||
|
||||
# User cluster configs
|
||||
.kubeconfig
|
||||
|
||||
#Emacs
|
||||
.#*
|
||||
venv/
|
||||
|
||||
# VS-code
|
||||
.vscode/
|
||||
|
||||
# Pycharm
|
||||
.idea/
|
||||
|
||||
# Eclipse files
|
||||
.classpath
|
||||
.project
|
||||
.settings/**
|
||||
|
||||
|
||||
# OSX leaves these everywhere on SMB shares
|
||||
._*
|
||||
|
||||
# build for rainbond
|
||||
|
||||
.release/
|
4
LICENSE
4
LICENSE
@ -1,4 +1,4 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
@ -162,4 +162,4 @@ General Public License ever published by the Free Software Foundation.
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
Library.
|
||||
|
22
Makefile
22
Makefile
@ -2,17 +2,13 @@ GO_LDFLAGS=-ldflags " -w"
|
||||
VERSION=3.4
|
||||
WORK_DIR=/go/src/github.com/goodrain/rainbond
|
||||
BASE_NAME=rainbond
|
||||
build-mq:
|
||||
go build ${GO_LDFLAGS} -o ./build/mq/${BASE_NAME}_mq ./cmd/mq
|
||||
build-worker:
|
||||
go build ${GO_LDFLAGS} -o ./build/worker/${BASE_NAME}_worker ./cmd/worker
|
||||
clean:
|
||||
@rm -rf ./build/mq/${BASE_NAME}_mq
|
||||
@rm -rf ./build/worker/${BASE_NAME}_worker
|
||||
@rm -rf ./build/api/${BASE_NAME}_api
|
||||
@rm -rf ./build/node/${BASE_NAME}_node
|
||||
build-in-container:
|
||||
@docker run -v `pwd`:/go/src/${BASE_NAME}_worker -w /go/src/${BASE_NAME}_worker -it golang:1.7.3 bash
|
||||
@rm -rf ./build/builder/${BASE_NAME}_builder
|
||||
|
||||
run-api:build-api
|
||||
./build/api/${BASE_NAME}_api --log-level=debug --mysql="admin:admin@tcp(127.0.0.1:3306)/region" --kube-config="`PWD`/admin.kubeconfig"
|
||||
run-mq:build-mq
|
||||
@ -35,14 +31,18 @@ run-eventlog:build-eventlog
|
||||
--docker.log.homepath="/Users/qingguo/tmp"
|
||||
run-node:build-node
|
||||
./build/node/${BASE_NAME}_node \
|
||||
--run-mode=master --kube-conf=`pwd`/admin.kubeconfig \
|
||||
--nodeid-file=`pwd`/host_id.conf \
|
||||
--run-mode=master --kube-conf=`pwd`/test/admin.kubeconfig \
|
||||
--nodeid-file=`pwd`/test/host_id.conf \
|
||||
--static-task-path=`pwd`/test/tasks \
|
||||
--log-level=debug
|
||||
|
||||
doc:
|
||||
@cd cmd/api && swagger generate spec -o ../../build/api/html/swagger.json
|
||||
|
||||
all: build-builder build-node build-entrance build-eventlog build-grctl build-api
|
||||
build-mq:
|
||||
go build ${GO_LDFLAGS} -o ./build/mq/${BASE_NAME}_mq ./cmd/mq
|
||||
build-worker:
|
||||
go build ${GO_LDFLAGS} -o ./build/builder/${BASE_NAME}_worker ./cmd/worker
|
||||
build-builder:
|
||||
go build ${GO_LDFLAGS} -o ./build/builder/${BASE_NAME}_builder ./cmd/builder
|
||||
build-mqcli:
|
||||
@ -57,7 +57,8 @@ build-grctl:
|
||||
go build ${GO_LDFLAGS} -o ./build/grctl/${BASE_NAME}_grctl ./cmd/grctl
|
||||
build-api:
|
||||
go build ${GO_LDFLAGS} -o ./build/api/${BASE_NAME}_api ./cmd/api
|
||||
|
||||
|
||||
all-image: build-image-worker build-image-mq build-image-builder build-image-entrance build-image-eventlog build-image-api
|
||||
build-image-worker:
|
||||
@echo "🐳 $@"
|
||||
@docker run -v `pwd`:${WORK_DIR} -w ${WORK_DIR} -it golang:1.8.3 go build ${GO_LDFLAGS} -o ./build/worker/${BASE_NAME}_worker ./cmd/worker
|
||||
@ -96,7 +97,6 @@ build-image-api:
|
||||
@docker run -v `pwd`:${WORK_DIR} -w ${WORK_DIR} -it golang:1.8.3 go build ${GO_LDFLAGS} -o ./build/api/${BASE_NAME}_api ./cmd/api
|
||||
@docker build -t hub.goodrain.com/dc-deploy/${BASE_NAME}_api:${VERSION} ./build/api
|
||||
@rm -f ./build/api/${BASE_NAME}_api
|
||||
build-image:build-image-worker build-image-mq build-image-builder build-image-eventlog build-image-entrance build-image-node
|
||||
push-image:
|
||||
docker push hub.goodrain.com/dc-deploy/${BASE_NAME}_eventlog:${VERSION}
|
||||
docker push hub.goodrain.com/dc-deploy/${BASE_NAME}_entrance:${VERSION}
|
||||
|
74
README.md
74
README.md
@ -0,0 +1,74 @@
|
||||
# Rainbond
|
||||
|
||||
<img src="https://github.com/goodrain/rainbond/blob/master/docs/rainbond_logo.png" width="30%">
|
||||
|
||||
----
|
||||
Rainbond is the first opensource enterprise application management platform (serverless PaaS) in China. It integrates CI/CD automation application building system, microservice architecture application management system and fully-automatic computing resource management system, to provide best practice of application-centic philosophy.
|
||||
|
||||
Rainbond is cutting edge application management platform with complete ecosystem, based on [Kubernetes](https://github.com/kubernetes/kubernetes) and [Docker](https://github.com/moby/moby), has been optimized and verified for 5 five years.
|
||||
|
||||
We choose to open source and embrace the community, absorbing best ideas and practices to further improve and enhance Rainbond, enabling more enterprise and individuals to enjoy "application-centric" experience.
|
||||
|
||||
----
|
||||
## [中文Readme](https://github.com/goodrain/rainbond/blob/master/docs/Readme_cn.md)
|
||||
## Quick Start
|
||||
|
||||
1. [Install Rainbond Data Center]()
|
||||
2. [Install Rainbond Application Console]()
|
||||
3. [Build Your First Application]()
|
||||
|
||||
## Quick Build
|
||||
|
||||
Quickly build Rainbond components in two ways:
|
||||
|
||||
##### Golang
|
||||
|
||||
```
|
||||
$go get -d github.com/goodrain/rainbond
|
||||
$cd $GOPATH/src/github.com/goodrain/rainbond
|
||||
$make all
|
||||
```
|
||||
##### Docker
|
||||
|
||||
```
|
||||
$git clone github.com/goodrain/rainbond
|
||||
$cd rainbond
|
||||
$make all-image
|
||||
```
|
||||
##### BUG Submission
|
||||
|
||||
Bug found in learning and using, please visit [ISSUES](https://github.com/goodrain/rainbond/issues) to find similar Bug and solutions. If there is no similar result, please create a new issure.
|
||||
|
||||
## [Rainbond Architecture]()
|
||||
|
||||
### Architecture
|
||||
|
||||
<img src="https://github.com/goodrain/rainbond/blob/master/docs/rainbond_architecture.png" >
|
||||
|
||||
### Rainbond Structure
|
||||
|
||||
Rainbond consisted of [Rainbond Data Center](https://github.com/goodrain/rainbond) and [Rainbond Resource Console](https://github.com/goodrain/rainbond-ui)(Enterprise edition available), seamlessly docked with 好雨云市, enabling hyper-converged computing pools.
|
||||
|
||||
* [Rainbond Data Center]()
|
||||
|
||||
Rainbond Data Center consisted of [a series of distributed components](), enabling resource-oriented Rainbond node abstraction, application-oriented storage, network and computing resources. With plug-in, distributed and software-defined principles, Rainbond can build unified application runtime environment on any computing environment, includes public cloud, private cloud, IDC and industry computing cloud.
|
||||
|
||||
* [Rainbond Application Console]()
|
||||
|
||||
Rainbond Application Console is Web console that interfaces with multiple Rainbond Data Centers, to provide application lifecycle management capabilities.
|
||||
|
||||
## Community
|
||||
|
||||
### Rainbond QQ Group
|
||||
|
||||
- 477016432(Group 1)
|
||||
- 453475798(Group 2)
|
||||
- 419331946(Group 3)
|
||||
|
||||
### Documentation
|
||||
|
||||
- [Development](http://doc.goodrain.com/cloudbang-community-install/247616)
|
||||
- [Installation](http://doc.goodrain.com/cloudbang-community-install/247616)
|
||||
- [Manual](http://doc.goodrain.com/usage)
|
||||
- [Maintenance](http://doc.goodrain.com/cloudbang-community-install/215655)
|
||||
- [Enterprise Edition Feature](http://doc.goodrain.com/cloudbang-enterprise)
|
@ -1,26 +0,0 @@
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: http://139.224.234.115:8181
|
||||
name: default-cluster
|
||||
- cluster:
|
||||
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR2akNDQXFhZ0F3SUJBZ0lVSEpSd2FKWlBNaGJwbitBenFnOFUvT05SaUVVd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1pURUxNQWtHQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFVcHBibWN4RURBT0JnTlZCQWNUQjBKbAphVXBwYm1jeEREQUtCZ05WQkFvVEEyczRjekVQTUEwR0ExVUVDeE1HVTNsemRHVnRNUk13RVFZRFZRUURFd3ByCmRXSmxjbTVsZEdWek1CNFhEVEUzTURZd09UQTRNemd3TUZvWERUSXlNRFl3T0RBNE16Z3dNRm93WlRFTE1Ba0cKQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFVcHBibWN4RURBT0JnTlZCQWNUQjBKbGFVcHBibWN4RERBSwpCZ05WQkFvVEEyczRjekVQTUEwR0ExVUVDeE1HVTNsemRHVnRNUk13RVFZRFZRUURFd3ByZFdKbGNtNWxkR1Z6Ck1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBMEZDKzFsNlRXbjdIWTNhY05kTlMKYVpMNE41UnNIZ3hHTGNzaU9hUGlsRi96V2FmR0FDc21KS1Vvc25jb0dNZlhQNUxhd25Tek1TQ3IrU0RNU3lwZQorbG1jTzk4SHFWTFJlRzJVV0RiZGloTnBydHpMWmE3TGRFTXlReFdjRUs2Yit2VTZoVHM0RGI0MnZLa05MbFRxCjF0VkNxNHJkNWNQN0NhQUFGWnQwU0ROTEszc1Rpd0tpcVR5RFhGWUJPaysxNG1kL0s4OHo2OVJXS0NkcEUxeGUKUEJqNFV6bTB6R3pxMzNEZmpFbmdTRkh1RVpkbGk5dFA0ejR4SG90d2JXdTJkQldpMi9YNk14czdVbnU4dDVXZwpJMWdHdzZsWlliSm1qWHJiL0ljNk5IK2JNQ2ZZNlA4UTBvSGVxY0w4NkZ6SjYzbTROQlhqVjVMK0hiZm4zc2V4CkF3SURBUUFCbzJZd1pEQU9CZ05WSFE4QkFmOEVCQU1DQVFZd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFqQWQKQmdOVkhRNEVGZ1FVOGVIYXM5TVUvOXZIa2lrSmJWNkRncUl2VDJNd0h3WURWUjBqQkJnd0ZvQVU4ZUhhczlNVQovOXZIa2lrSmJWNkRncUl2VDJNd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLcFg5bS95MkpuQklLVEJyR2pNCnhOeTRMcW5BTkViRjVFczV6SlpRZ2VuUDFjeElCNGNtby9qUERZN0Z3ZThlN2I0WE52TlFGN0dQbWgwRHFsQSsKVFRwaFFmRmdieUoxWUhVUWFJYVJ5MUI4RUE0Njc1dnVWUTA4NU1wQ0VaR2ladmFyNVBCSEhJZUJyZjRCdEtLaApmZnJsYlplWHNsREFVTUc0T1Q4OXNQeGE4NE40TktuMmlyY0I0dEZyeDlubTZrWEcvVmdUWEYxUVBwdTJZeVVSCnloSzZDK0IydXZTUjRsOGVwQkVmclR6MTJ1bTlaRWprL0dHeXM5aFgzRmpDZlNOTDlESEtwampCZmlnVFV6TzMKUVVaK09FRzgxREYxT0Zuc0p2SmFKZmZhbXNyZXFZUjE5NHA1ZmQrWHEwT01Jdk8vMXdZc3NLNmpKOG82aWdGbwpUM289Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||
server: https://kubeapi.goodrain.me:6443
|
||||
name: kubernetes
|
||||
contexts:
|
||||
- context:
|
||||
cluster: default-cluster
|
||||
user: ""
|
||||
name: default
|
||||
- context:
|
||||
cluster: kubernetes
|
||||
user: admin
|
||||
name: kubernetes
|
||||
current-context: kubernetes
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: admin
|
||||
user:
|
||||
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQzVENDQXNXZ0F3SUJBZ0lVY0U1YUg0NXh2YU5saXNSNEFCTFFCUVVLSXN3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1pURUxNQWtHQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFVcHBibWN4RURBT0JnTlZCQWNUQjBKbAphVXBwYm1jeEREQUtCZ05WQkFvVEEyczRjekVQTUEwR0ExVUVDeE1HVTNsemRHVnRNUk13RVFZRFZRUURFd3ByCmRXSmxjbTVsZEdWek1CNFhEVEUzTURZd09UQTRORFF3TUZvWERUSTNNRFl3TnpBNE5EUXdNRm93YXpFTE1Ba0cKQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFVcHBibWN4RURBT0JnTlZCQWNUQjBKbGFVcHBibWN4RnpBVgpCZ05WQkFvVERuTjVjM1JsYlRwdFlYTjBaWEp6TVE4d0RRWURWUVFMRXdaVGVYTjBaVzB4RGpBTUJnTlZCQU1UCkJXRmtiV2x1TUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUE4dEtQRFM3VCsyNmYKUWpWR1U1ckNDdXUxOCtwU0dJcTdHbHJEVElmTEhCV2loMDBUL0tvc0Z3VkI0YXp4Mjk0ZkQ1R2MyWng5Q2QxQQpaMTVNY2p0WVNtZFF4REpzQ3BnazNDL0ZPWkU0aWJ4a2Y2cUprOURjWjU2NDBoTEl2d1drUE5SMjNZdUJNVU5pCkdoNWdzSlhqYmY5dEdzcTdIbktQNTAvSnFDVzZWdzBCK3lpL2FIYUhwdUVSRURVenJINGVqRkJHR25KdFlkSE0KcUxsbUpvUC9wVDlHbys1TGhONzQrOGVwSTZ6Q3B1RlA5ajJ5NGlhNnJiTlQvYjFOZGFvUkJZd0hpMlBVeUNwZApPeWFWRTdJYkZER1o4WVc1UVlIdmUvcGxiRkJWanRVQWY3ajMxeHRKakJ6cjFxQk15RWFheU5sWmN2VUt3TU80CmJ1dkxUdkFNblFJREFRQUJvMzh3ZlRBT0JnTlZIUThCQWY4RUJBTUNCYUF3SFFZRFZSMGxCQll3RkFZSUt3WUIKQlFVSEF3RUdDQ3NHQVFVRkJ3TUNNQXdHQTFVZEV3RUIvd1FDTUFBd0hRWURWUjBPQkJZRUZLNmRiN2k4ZUFqSwpVemtTVGR1Y3hRZGhvYjVyTUI4R0ExVWRJd1FZTUJhQUZQSGgyclBURlAvYng1SXBDVzFlZzRLaUwwOWpNQTBHCkNTcUdTSWIzRFFFQkN3VUFBNElCQVFCdVRrNVlvNld4UE9qZHk5b2ZWNDhLM24zUG0rdWZuWUxzRFJ0V3A4K1MKMFVBaW95UC9CbFErOW9ZY0hMV3NqM1FQTjVNUW02NzQ3N1hHZ21sRGQzRmcrWGViRGswd0F2L2Y3cUxqRHYxWQpiY1N4enhTaFdpVnRPdEJuREJMeUc1SzlDaEVQWUQrcFlyZDR0QVlEY2tmV1c0VUwwUm5QQXJqRzFIbXhUZXljClkxNUU4RnVUcmhOWitiOFBISkJLNzhOTWduS2kvQzIxLzBLNVh0NWF2WE1scHNwckZCRndZak92VXl2MGl3UTUKWExnbFRVbkZoM1RMZ2VwYm1HeHY4RUhsT0xpVEdxNnNtd1ZzQVVmT3Frc2VIaFlJeXJRbGFzZG0yTnZYSVlwNwpGaTgxQkh3blR2M1ZPaW1wbUVhZW1CUDhONDJ0Yjd5YllHd1VRZDkrNnpOUQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
|
||||
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBOHRLUERTN1QrMjZmUWpWR1U1ckNDdXUxOCtwU0dJcTdHbHJEVElmTEhCV2loMDBUCi9Lb3NGd1ZCNGF6eDI5NGZENUdjMlp4OUNkMUFaMTVNY2p0WVNtZFF4REpzQ3BnazNDL0ZPWkU0aWJ4a2Y2cUoKazlEY1o1NjQwaExJdndXa1BOUjIzWXVCTVVOaUdoNWdzSlhqYmY5dEdzcTdIbktQNTAvSnFDVzZWdzBCK3lpLwphSGFIcHVFUkVEVXpySDRlakZCR0duSnRZZEhNcUxsbUpvUC9wVDlHbys1TGhONzQrOGVwSTZ6Q3B1RlA5ajJ5CjRpYTZyYk5UL2IxTmRhb1JCWXdIaTJQVXlDcGRPeWFWRTdJYkZER1o4WVc1UVlIdmUvcGxiRkJWanRVQWY3ajMKMXh0SmpCenIxcUJNeUVhYXlObFpjdlVLd01PNGJ1dkxUdkFNblFJREFRQUJBb0lCQUhoMEZ0NVZRbmJSYzFNbQpsbEpXekxjYUlsSnpCSEtFTHpodG1iL1hCTnhUcHlJekRCMGtWV2ErQnVacUlqZ05RWjg1Zm5NOGU1SnZITW1xCkw0WGpCbk15T1JCNmFybitxeHBHNERFa1pzVjhuT1h2dFB1TWVpazB4VDRBYjNEQzNhd0hRVWU1TWtjN3craHMKOVUzanJNUWVGd05aV2VWS3N6UHRrbjFFNmVEUDdhbytsOFZDTVRlS2Y4dXUwK3FZbnFyL1lycDhZM0JZaWVaWgpxMXhzSEdxeDVLczJkRk1pOFc0dllDQ1ZTRTh3Nk5kVlU3QUdkT2U5dVF5U3pGZHJtOXl0TWdzNWthT0FPS2VICnllS1NsRHJyTm0rdzVYTGkvVnpieFhMMlNBQ3AxN2lyVExHc0VGbjRrT0JyTkpkUWxsTk5BOUhuMlFnbkJLSk0KU0lnWVhvRUNnWUVBL2dGL0twVnliSCtBVk9XdVFYNzBiUEh6blkybG04UEpPUUlZZDNqN2Z1ZmtlSVFJOFp2Zwp3ZkQ2ejYvbDIzUGIzWXExa3Bmc1QrWU5pTHZ2aDhObUlRL05kZVJzcmc2SU1YZ3NsUzhDYWxCVll6YUczckQrCnJSTUFHQlhXYzlXazNMUmNpU29UTFlNMnhlMkJDRDVqKzlmTjQ4V29FOUhtVCtvczFMZm9ReEVDZ1lFQTlMcVYKN1MxbVhEOGRKVUg4L09oUkdQK0llV29NaWpTZ0tMRkRPbUh4TWhVelA2cUtqOWFDYkdRVExwZDRCbTBENXo2RgpFQkxXZTN6dDd4VnNvQTMwMmZ6aTJWYWQrVC9VOGNxZ2lMdklVTXRza3Y4cGxtdVg5NmFZT3d3a2wrSjNweXNqCmNSUHQvMXNGMkJwU1JWSnNlWVhZdFNOZm5xandYU2MwbGdPYzJNMENnWUVBckRrdmxCd0luWEJGLzBwTVFMUm4KM29hZSs3RFRIUzQrL1p4aUluK3habmFzL3RubmhvcCtkb0dDVGRlaUxvMzhBZGJQRGpwY1RFQmI1TjRvcHhEaAo2b0RnZXBNSzdXbUZCcVhJOU00UEFTNis5cW8rQURoYU5kZXdOS1I1NThod0pBVld5Tm55YnVXTStkN3pvamRMCmE3TURNdkVONllKa2VzTDQ2ckpYbFVFQ2dZRUFqU2N4WW1OVkxzK1lWK3ovcGorNHh0cGNOT3RkNERrS1IyNDIKZ0c0TlYvMnlXWDViL1NxVWYwQVpjRDRkRkZlOGNKdzIwMWFLTHgrWGZOYTJtSHAwUjJiODBLNk0zejVaN0R3YQo2OEtqZnpaVm9WTmJBVTk5ajNHZlRDOXN5ODNyaFpmMEVUeVZnVHRXMjVZUzdiamtxQkx6TTBiNzE5OUpBL0dnCit3V0thZUVDZ1lBc2lRYVJFTjRlUjR3NGhRaDdnWEdNeGNxNVp0NWh6akxuejcxcHdCTHVud2Rod1J0RjlxVVgKaXdaNHFKb0NqdUFlZVFsUEg3VldDa21HY3ArcStYS2VpaEdrcWRCTEprVE04eEtkYklOYml1ak5OTndqbkZ1aQpzenNWWVVpc0d1Q2hhbjZFVkZNZDVCMFRWalZucjdheWtXZmNma2tkN0xxd3dEQ1RGYS9qS3c9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
|
0
build/README.md
Normal file
0
build/README.md
Normal file
@ -1678,6 +1678,30 @@
|
||||
}
|
||||
},
|
||||
"/v2/tenants/{tenant_name}/plugin/{plugin_id}/default-env": {
|
||||
"get": {
|
||||
"description": "get plugin env",
|
||||
"consumes": [
|
||||
"application/json",
|
||||
"application/x-protobuf"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/xml"
|
||||
],
|
||||
"tags": [
|
||||
"v2"
|
||||
],
|
||||
"summary": "获取插件默认设定的env",
|
||||
"operationId": "getPluginDefaultEnv",
|
||||
"responses": {
|
||||
"default": {
|
||||
"description": "统一返回格式",
|
||||
"schema": {
|
||||
"$ref": "#/responses/commandResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "update default env",
|
||||
"consumes": [
|
||||
@ -1847,48 +1871,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/tenants/{tenant_name}/plugin/{plugin_id}/envs": {
|
||||
"get": {
|
||||
"description": "get plugin env",
|
||||
"consumes": [
|
||||
"application/json",
|
||||
"application/x-protobuf"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/xml"
|
||||
],
|
||||
"tags": [
|
||||
"v2"
|
||||
],
|
||||
"summary": "获取插件设定的env",
|
||||
"operationId": "getPluginEnv",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"x-go-name": "TenantName",
|
||||
"name": "tenant_name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"x-go-name": "PluginID",
|
||||
"name": "plugin_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"default": {
|
||||
"description": "统一返回格式",
|
||||
"schema": {
|
||||
"$ref": "#/responses/commandResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/tenants/{tenant_name}/resources": {
|
||||
"get": {
|
||||
"description": "get tenant resources",
|
||||
|
0
cmd/README.md
Normal file
0
cmd/README.md
Normal file
@ -22,8 +22,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/goodrain/rainbond/cmd/node/option"
|
||||
"github.com/goodrain/rainbond/pkg/db"
|
||||
"github.com/goodrain/rainbond/pkg/db/config"
|
||||
"github.com/goodrain/rainbond/pkg/node/api/controller"
|
||||
"github.com/goodrain/rainbond/pkg/node/core/job"
|
||||
"github.com/goodrain/rainbond/pkg/node/core/k8s"
|
||||
@ -88,17 +86,6 @@ func Run(c *option.Conf) error {
|
||||
}
|
||||
event.On(event.EXIT, ms.Stop)
|
||||
}
|
||||
//mysql init
|
||||
dbconfig := config.Config{
|
||||
DBType: c.DBType,
|
||||
MysqlConnectionInfo: c.DBConnectionInfo,
|
||||
}
|
||||
if err := db.CreateManager(dbconfig); err != nil {
|
||||
logrus.Warnf("create db manager error, %v", err)
|
||||
logrus.Warnf("Ignore this db error for node main functions, but discover services in this node may be influenced.")
|
||||
//return err
|
||||
}
|
||||
defer db.CloseManager()
|
||||
//启动API服务
|
||||
apiManager := api.NewManager(*s.Conf, s.HostNode, ms)
|
||||
apiManager.Start(errChan)
|
||||
|
12
docs/Pain_points.md
Normal file
12
docs/Pain_points.md
Normal file
@ -0,0 +1,12 @@
|
||||
# 云帮解决用户痛点
|
||||
---
|
||||
## 微服务架构落地
|
||||
“微服务架构”概念自提出以来,深受业界喜爱。各类历史架构纷纷转向微服务架构。企业如何快速解决传统应用服务化,服务治理,服务发现,服务监控等等难题从而获得微服务架构带来的种种优势?云帮,原生的微服务管理平台,无需任何设置,默认提供微服务高效管理服务。也可以通过云帮强大的应用插件体系带来丰富多彩的微服务治理扩展,使企业微服务架构落地完全无障碍。
|
||||
## 持续集成/持续交付落地
|
||||
云帮作为以应用为中心的交付平台,支持各类流行开发语言一键部署,自动部署。商业应用对接好雨云市平台,应用提供商可以快速交付生产级应用环境。对于企业自研应用,云帮助你持续构建应用,平滑升级应用,无需人工干预。
|
||||
## 复杂架构快速上云
|
||||
云帮平台运行各类应用(数据库,消息中间件,Web应用,大数据处理应用,人工智能应用等等),你的各类型复杂的架构经过简单得改造即可运行于云帮平台,即可实现云端扩展。
|
||||
## 高并发应用快速伸缩
|
||||
云帮借助Docker,Kubernetes等容器技术作为应用的运行载体,赋予能够应用快速伸缩得特性。云帮应用级实时性能分析系统提供数据参考,实现应用按需自动伸缩。或者用户指定伸缩策略(例如指定高峰时间段)实现半自动应用伸缩。
|
||||
## 海量应用高效便捷管理
|
||||
云帮作为以应用为中心的跨数据中心应用管理平台,一套平台,具有海量的计算能力。云帮智能化,自动化的资源管理系统,让用户无需投入时间精力关注物理资源。海量的应用,同一个管理方式。使用一个简单的操作UI,即可对海量的应用实现多元化的控制。
|
@ -1,11 +1,13 @@
|
||||
# 云帮
|
||||
|
||||
<img src="https://github.com/goodrain/rainbond/raw/master/docs/logo.png">
|
||||
<img src="https://github.com/goodrain/rainbond/blob/master/docs/rainbond_logo.png">
|
||||
|
||||
----
|
||||
云帮是国内第一个开源的企业级应用管理平台(无服务器PaaS),集CI/CD自动化应用构建系统,原生支持微服务架构的应用管理系统和全自动的计算资源管理系统于一身。是以应用为中心理念的最佳实践。
|
||||
云帮(Rainbond)是国内首个开源企业级应用管理平台(无服务器PaaS),集CI/CD自动化应用构建系统、微服务架构应用管理系统、全自动计算资源管理系统于一身,提供“以应用为中心”理念的最佳实践。
|
||||
|
||||
云帮得益于[Kubernetes](https://github.com/kubernetes/kubernetes),[Docker](https://github.com/moby/moby)等容器生态开源项目,结合好雨云多年的公有云生产运营经验。成为国内理念最新的云应用平台。我们选择开源,拥抱社区。吸收社区最好的想法和实践打造云帮项目,让更多的用户和企业拥抱以应用为中心的云计算平台。
|
||||
云帮深度整合[Kubernetes](https://github.com/kubernetes/kubernetes)、 [Docker](https://github.com/moby/moby)等顶级容器生态开源项目,并历经超过五年的生产运营打磨和验证,形成目前理念最新、生态最完整的应用管理平台。
|
||||
|
||||
如今,我们选择开源、拥抱社区,期望吸收最好的想法和实践,进一步完善和提升云帮,让更多企业和个人用户享受“以应用为中心”的技术体验。
|
||||
|
||||
----
|
||||
|
||||
@ -16,15 +18,17 @@
|
||||
3. [创建你的第一个应用]().
|
||||
|
||||
## 快速构建
|
||||
如果你想马上构建云帮组件,你有两种方式:
|
||||
##### 你有Golang开发环境
|
||||
|
||||
通过两种方式快速构建云帮组件:
|
||||
|
||||
##### Golang开发环境
|
||||
|
||||
```
|
||||
$go get -d github.com/goodrain/rainbond
|
||||
$cd $GOPATH/src/github.com/goodrain/rainbond
|
||||
$make all
|
||||
```
|
||||
##### 你有Docker环境
|
||||
##### Docker环境
|
||||
|
||||
```
|
||||
$git clone github.com/goodrain/rainbond
|
||||
@ -32,33 +36,40 @@ $cd rainbond
|
||||
$make all-image
|
||||
```
|
||||
##### BUG提交
|
||||
使用或者学习云帮过程中遇到BUG,请异步[ISSUES](https://github.com/goodrain/rainbond/issues),首先查找类似BUG及其修复方案,若无类似问题你可以创建Issue。
|
||||
|
||||
在学习和使用中发现Bug,请移步[ISSUES](https://github.com/goodrain/rainbond/issues),查找类似Bug及其修复方案。若无类似问题,请新建Issue。
|
||||
|
||||
## [云帮架构]()
|
||||
|
||||
### 架构总图
|
||||
<img src="./rainbond_architecture.png" href="">
|
||||
|
||||
<img src="https://github.com/goodrain/rainbond/blob/master/docs/rainbond_architecture.png" href="">
|
||||
|
||||
### 云帮构成
|
||||
云帮由[云帮数据中心](https://github.com/goodrain/rainbond) [云帮应用控制台](https://github.com/goodrain/rainbond-ui) 云帮资源控制台(企业版提供)构成。并与好雨云市进行无缝对接,实现超融合计算池。
|
||||
|
||||
云帮由[云帮数据中心](https://github.com/goodrain/rainbond) 和[云帮应用控制台](https://github.com/goodrain/rainbond-ui) 云帮资源控制台(企业版提供)构成,并无缝对接好雨云市,以此实现超融合计算池。
|
||||
|
||||
* [云帮数据中心]()
|
||||
云帮数据中心由[一系列分布式组件]()构成,面向资源抽象云帮节点,面向应用抽象存储,网络以及计算资源。本着插件化,分布式,软件定义一切的设计原则,在任何计算环境(公有云,私有云,IDC,行业计算)之上构建统一的应用运行环境。
|
||||
* [云帮应用控制台]()
|
||||
|
||||
云帮数据中心由[一系列分布式组件]()构成,面向资源抽象云帮节点,面向应用抽象存储、网络以及计算资源。本着插件化、分布式、软件定义一切的设计原则,云帮可在任何计算环境(公有云,私有云,IDC,行业计算)之上构建统一的应用运行环境。
|
||||
|
||||
* [云帮应用控制台]()
|
||||
|
||||
云帮应用控制台是一个Web控制台,对接多个云帮数据中心,提供应用的全生命周期管理功能。
|
||||
|
||||
## 社区支持
|
||||
### 云帮用户交流群:
|
||||
|
||||
### 云帮用户交流群(QQ):
|
||||
|
||||
- 477016432(1群)
|
||||
- 453475798(2群)
|
||||
- 419331946(3群)
|
||||
|
||||
### 云帮微信群
|
||||
> 云帮小秘书可以拉您进云帮微信群:
|
||||
|
||||
<img src="http://ojfzu47n9.bkt.clouddn.com/2017032214901508126968.jpg" width="30%" />
|
||||
|
||||
### 文档支持
|
||||
|
||||
- [云帮开发文档](http://doc.goodrain.com/cloudbang-community-install/247616)
|
||||
- [安装文档](http://doc.goodrain.com/cloudbang-community-install/247616)
|
||||
- [使用文档](http://doc.goodrain.com/usage)
|
||||
- [平台维护](http://doc.goodrain.com/cloudbang-community-install/215655)
|
||||
- [企业版功能介绍](http://doc.goodrain.com/cloudbang-enterprise)
|
||||
- [企业版功能介绍](http://doc.goodrain.com/cloudbang-enterprise)
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 36 KiB |
BIN
docs/rainbond_logo.png
Normal file
BIN
docs/rainbond_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
28
hack/build-rpm.sh
Executable file
28
hack/build-rpm.sh
Executable file
@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
set -x
|
||||
|
||||
(
|
||||
rpmbuild_root=${releasedir}/rpm
|
||||
for release_dir in $(find hack/rpm/* -maxdepth 0 -type d)
|
||||
do
|
||||
release=${release_dir##*/}
|
||||
RELEASE_PATH=$rpmbuild_root/$release
|
||||
rm -rf $RELEASE_PATH
|
||||
mkdir -p $RELEASE_PATH/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
||||
SOURCE_TARGET=$rpmbuild_root/$release/SOURCES
|
||||
SPEC_TARGET=$rpmbuild_root/$release/SPECS
|
||||
|
||||
cp -a $release_dir/SPECS/* $SPEC_TARGET
|
||||
|
||||
rsync -a $release_dir/files/ $SOURCE_TARGET/${PROGRAM}-${VERSION}
|
||||
cp -a $distdir/* $SOURCE_TARGET/${PROGRAM}-${VERSION}/
|
||||
cd $SOURCE_TARGET && tar zcf ${PROGRAM}-${VERSION}.tar.gz ${PROGRAM}-${VERSION} && cd -
|
||||
|
||||
BUILD_IMAGE=inner.goodrain.com/rpm-build:$release
|
||||
for file in $(find $SPEC_TARGET -name '*.spec')
|
||||
do
|
||||
docker run --rm -v $PWD/$RELEASE_PATH:/root/rpmbuild -e rpmRelease=$buildRelease -e VERSION=$VERSION $BUILD_IMAGE SPECS/${file##*/}
|
||||
done
|
||||
done
|
||||
) 2>&1
|
64
hack/rpm/centos-7/SPECS/rainbond-node.spec
Normal file
64
hack/rpm/centos-7/SPECS/rainbond-node.spec
Normal file
@ -0,0 +1,64 @@
|
||||
Summary: rainbond-node
|
||||
Name: gr-rainbond-node
|
||||
Version: %{_version}
|
||||
Release: %{_release}
|
||||
License: GPL
|
||||
Group: goodrain
|
||||
Source: gr-rainbond-node-%{version}.tar.gz
|
||||
Packager: ysicing
|
||||
BuildRoot: /root/rpmbuild
|
||||
|
||||
%description
|
||||
rainbond-node
|
||||
|
||||
%prep
|
||||
%setup -n gr-rainbond-node-%{version}
|
||||
|
||||
%build
|
||||
|
||||
%install
|
||||
install -d %{buildroot}/usr/share/gr-rainbond-node/gaops/
|
||||
install -d %{buildroot}/usr/local/bin/
|
||||
install -d %{buildroot}/usr/lib/systemd/system/
|
||||
install -d %{buildroot}/usr/share/gr-rainbond-node/scripts/
|
||||
|
||||
install -p -m 755 usr/local/bin/rainbond-node %{buildroot}/usr/local/bin/rainbond-node
|
||||
install -p -m 644 usr/lib/systemd/system/rainbond-node.service %{buildroot}/usr/lib/systemd/system/rainbond-node.service
|
||||
install -p -m 755 usr/share/gr-rainbond-node/scripts/start-node.sh %{buildroot}/usr/share/gr-rainbond-node/scripts/start-node.sh
|
||||
install -p -m 755 usr/share/gr-rainbond-node/gaops/gaops.tgz %{buildroot}/usr/share/gr-rainbond-node/gaops/
|
||||
|
||||
|
||||
%pre
|
||||
[ -d "/etc/goodrain/envs" ] || mkdir -p /etc/goodrain/envs
|
||||
[ -f "/etc/goodrain/envs/rainbond-node.sh" ] && rm /etc/goodrain/envs/rainbond-node.sh
|
||||
[ -f "/etc/goodrain/envs/ip.sh" ] && (
|
||||
grep "MANAGE" /etc/goodrain/envs/ip.sh
|
||||
if [ $? -eq 0 ];then
|
||||
echo "NODE_TYPE=compute" >> /etc/goodrain/envs/rainbond-node.sh
|
||||
else
|
||||
echo "NODE_TYPE=" >> /etc/goodrain/envs/rainbond-node.sh
|
||||
fi
|
||||
) || (
|
||||
echo "NODE_TYPE=" >> /etc/goodrain/envs/rainbond-node.sh
|
||||
)
|
||||
|
||||
|
||||
%post
|
||||
%systemd_post rainbond-node
|
||||
[ -L "/usr/bin/rainbond-node" ] || ln -s /usr/local/bin/rainbond-node /usr/bin/rainbond-node
|
||||
[ -f "/usr/share/gr-rainbond-node/gaops/gaops.tgz" ] && (
|
||||
tar xf /usr/share/gr-rainbond-node/gaops/gaops.tgz -C /usr/share/gr-rainbond-node/gaops/
|
||||
)
|
||||
|
||||
%preun
|
||||
%systemd_preun rainbond-node
|
||||
|
||||
%postun
|
||||
%systemd_postun_with_restart rainbond-node
|
||||
[ -L "/usr/bin/rainbond-node" ] || rm -f /usr/bin/rainbond-node
|
||||
|
||||
%files
|
||||
/usr/share/gr-rainbond-node/gaops/
|
||||
/usr/local/bin/rainbond-node
|
||||
/usr/lib/systemd/system/rainbond-node.service
|
||||
/usr/share/gr-rainbond-node/scripts/start-node.sh
|
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=goodrain rainbond-node
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
EnvironmentFile=/etc/goodrain/envs/rainbond-node.sh
|
||||
PermissionsStartOnly=true
|
||||
ExecStart=/usr/share/gr-rainbond-node/scripts/start-node.sh
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
14
hack/rpm/centos-7/files/usr/share/gr-rainbond-node/scripts/start-node.sh
Executable file
14
hack/rpm/centos-7/files/usr/share/gr-rainbond-node/scripts/start-node.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
ETCD_ADDR=$(cat /etc/goodrain/envs/etcd.sh | awk -F '=' '{print $2}')
|
||||
|
||||
if [ -z $NODE_TYPE ];then
|
||||
eval $(ssh-agent) > /dev/null
|
||||
eval $(ssh-add) > /dev/null
|
||||
#eval $(ssh-add /path/key) > /dev/null
|
||||
ACP_NODE_OPTS="--static-task-path=/usr/share/gaops/tasks/ --etcd=http://$ETCD_ADDR:2379 --kube-conf=/etc/goodrain/kubernetes/kubeconfig --run-mode master --noderule manage"
|
||||
else
|
||||
ACP_NODE_OPTS='--log-level=debug'
|
||||
fi
|
||||
|
||||
exec /usr/local/bin/rainbond-node $ACP_NODE_OPTS
|
0
pkg/README.md
Normal file
0
pkg/README.md
Normal file
@ -21,6 +21,7 @@ package controller
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/goodrain/rainbond/pkg/api/handler"
|
||||
"github.com/goodrain/rainbond/pkg/api/middleware"
|
||||
|
||||
@ -566,9 +567,9 @@ func (t *TenantStruct) DeletePluginRelation(w http.ResponseWriter, r *http.Reque
|
||||
|
||||
//GetPluginDefaultEnvs GetPluginDefaultEnvs
|
||||
func (t *TenantStruct) GetPluginDefaultEnvs(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation GET /v2/tenants/{tenant_name}/plugin/{plugin_id}/envs v2 getPluginEnv
|
||||
// swagger:operation GET /v2/tenants/{tenant_name}/plugin/{plugin_id}/default-env v2 getPluginDefaultEnv
|
||||
//
|
||||
// 获取插件设定的env
|
||||
// 获取插件默认设定的env
|
||||
//
|
||||
// get plugin env
|
||||
//
|
||||
@ -619,6 +620,7 @@ func (t *TenantStruct) GePluginEnvWhichCanBeSet(w http.ResponseWriter, r *http.R
|
||||
// description: 统一返回格式
|
||||
serviceID := r.Context().Value(middleware.ContextKey("service_id")).(string)
|
||||
pluginID := chi.URLParam(r, "plugin_id")
|
||||
logrus.Debugf("plugin_Id is %s", pluginID)
|
||||
envs, err := handler.GetPluginManager().GetEnvsWhichCanBeSet(serviceID, pluginID)
|
||||
if err != nil {
|
||||
err.Handle(r, w)
|
||||
|
@ -1,27 +1,24 @@
|
||||
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package handler
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@ -53,22 +50,7 @@ var key = []byte("qa123zxswe3532crfvtg123bnhymjuki")
|
||||
|
||||
//decrypt 解密算法
|
||||
func decrypt(key []byte, encrypted string) ([]byte, error) {
|
||||
ciphertext, err := base64.RawURLEncoding.DecodeString(encrypted)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ciphertext) < aes.BlockSize {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
ciphertext = ciphertext[aes.BlockSize:]
|
||||
cfb := cipher.NewCFBDecrypter(block, iv)
|
||||
cfb.XORKeyStream(ciphertext, ciphertext)
|
||||
return ciphertext, nil
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
//ReadLicenseFromFile 从文件获取license
|
||||
|
@ -1,19 +1,18 @@
|
||||
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
@ -25,7 +24,7 @@ import (
|
||||
)
|
||||
|
||||
func TestLicense(t *testing.T) {
|
||||
licenseStr := "0QFGbwit-t5nOn9Eq1NssMSAkiL99iHYbYCOAHFHDEm5u0BB4PUxm3WfwxMEPYPg6hbEiaR803ponhc3cLnxIPNiUU72-RtNdCKy-gssxLF03f8L6UVraaUvasVOz3vjtdbT8u7Cq99whd6C_wk7D-Wh-VjQhkeprPbaJtiUiy8_P48sWVBEET0l3_NwbOvYRmFS6zFeJ5JxC0DLNb0ajkCyIK9RWsZLVlSi2Qp4SCCG4CDf3oUTwxd63njjrPhuqZEaRFEyKzmZXDuxCYVDidCFWMhMKXGP"
|
||||
licenseStr := "0QFGbwit-"
|
||||
text, err := decrypt(key, licenseStr)
|
||||
if err != nil {
|
||||
fmt.Println("decrypt err")
|
||||
@ -34,7 +33,7 @@ func TestLicense(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestToken(t *testing.T) {
|
||||
licenseStr := "0QFGbwit-t5nOn9Eq1NssMSAkiL99iHYbYCOAHFHDEm5u0BB4PUxm3WfwxMEPYPg6hbEiaR803ponhc3cLnxIPNiUU72-RtNdCKy-gssxLF03f8L6UVraaUvasVOz3vjtdbT8u7Cq99whd6C_wk7D-Wh-VjQhkeprPbaJtiUiy8_P48sWVBEET0l3_NwbOvYRmFS6zFeJ5JxC0DLNb0ajkCyIK9RWsZLVlSi2Qp4SCCG4CDf3oUTwxd63njjrPhuqZEaRFEyKzmZXDuxCYVDidCFWMhMKXGP"
|
||||
licenseStr := "0QFGbwit-"
|
||||
token, err := BasePack([]byte(licenseStr))
|
||||
if err != nil {
|
||||
fmt.Printf("error")
|
||||
|
@ -97,6 +97,18 @@ func (p *PluginAction) CreatePluginAct(cps *api_model.CreatePluginStruct) *util.
|
||||
return util.CreateAPIHandleErrorFromDBError(fmt.Sprintf("add default env %s", env.ENVName), err)
|
||||
}
|
||||
}
|
||||
//添加默认plugin model env
|
||||
vis := &dbmodel.TenantPluginDefaultENV{
|
||||
PluginID: cps.Body.PluginID,
|
||||
ENVName: "PLUGIN_MOEL",
|
||||
ENVValue: cps.Body.PluginModel,
|
||||
IsChange: false,
|
||||
}
|
||||
err = db.GetManager().TenantPluginDefaultENVDaoTransactions(tx).AddModel(vis)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return util.CreateAPIHandleErrorFromDBError("add default env PLUGIN_MOEL", err)
|
||||
}
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
tx.Rollback()
|
||||
return util.CreateAPIHandleErrorFromDBError("commit create plugin transactions", err)
|
||||
@ -222,7 +234,7 @@ func (p *PluginAction) GetEnvsWhichCanBeSet(serviceID, pluginID string) (interfa
|
||||
if len(envs) > 0 {
|
||||
return envs, nil
|
||||
}
|
||||
envD, errD := db.GetManager().TenantPluginDefaultENVDao().GetDefaultENVSByPluginIDCantBeSet(pluginID)
|
||||
envD, errD := db.GetManager().TenantPluginDefaultENVDao().GetDefaultEnvWhichCanBeSetByPluginID(pluginID)
|
||||
if errD != nil {
|
||||
return nil, util.CreateAPIHandleErrorFromDBError("get envs which can be set", errD)
|
||||
}
|
||||
@ -345,6 +357,7 @@ func (p *PluginAction) DockerfileBuildPlugin(b *api_model.BuildPluginStruct, plu
|
||||
Kind: b.Body.Kind,
|
||||
Repo: b.Body.RepoURL,
|
||||
GitURL: b.Body.GitURL,
|
||||
Info: b.Body.Info,
|
||||
BuildTime: time.Now().Format(time.RFC3339),
|
||||
Status: "building",
|
||||
}
|
||||
|
@ -1,36 +1,36 @@
|
||||
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package handler
|
||||
|
||||
import (
|
||||
api_model "github.com/goodrain/rainbond/pkg/api/model"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
api_model "github.com/goodrain/rainbond/pkg/api/model"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/pquerna/ffjson/ffjson"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
func TestService(t *testing.T) {
|
||||
func TestABCService(t *testing.T) {
|
||||
mm := `{
|
||||
"comment":"",
|
||||
"container_env":"",
|
||||
|
@ -330,7 +330,7 @@ type DeletePluginSetStruct struct {
|
||||
}
|
||||
|
||||
//GetPluginEnvStruct GetPluginEnvStruct
|
||||
//swagger:parameters getPluginEnv
|
||||
//swagger:parameters getPluginEnv, getPluginDefaultEnv
|
||||
type GetPluginEnvStruct struct {
|
||||
// in: path
|
||||
// required: true
|
||||
|
@ -514,8 +514,11 @@ var LabelKeyServiceAntyAffinity = "service-anti-affinity"
|
||||
//InitPlugin 初始化插件
|
||||
var InitPlugin = "init-plugin"
|
||||
|
||||
//NetPlugin 网络插件
|
||||
var NetPlugin = "net-plugin"
|
||||
//UpNetPlugin 上游网络插件
|
||||
var UpNetPlugin = "upnet-plugin"
|
||||
|
||||
//DownNetPlugin 下游网络插件
|
||||
var DownNetPlugin = "downnet-plugin"
|
||||
|
||||
//GeneralPlugin 一般插件,默认分类,优先级最低
|
||||
var GeneralPlugin = "general-plugin"
|
||||
|
@ -33,11 +33,12 @@ import (
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
grpcserver "github.com/goodrain/rainbond/pkg/mq/api/grpc/server"
|
||||
_ "net/http/pprof"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
restful "github.com/emicklei/go-restful"
|
||||
swagger "github.com/emicklei/go-restful-swagger12"
|
||||
grpcserver "github.com/goodrain/rainbond/pkg/mq/api/grpc/server"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/prometheus/common/version"
|
||||
@ -101,6 +102,11 @@ func NewManager(c option.Config) (*Manager, error) {
|
||||
conf: c,
|
||||
actionMQ: actionMQ,
|
||||
}
|
||||
go func() {
|
||||
if err := http.ListenAndServe(":6301", nil); err != nil {
|
||||
logrus.Error("mq pprof listen error.", err.Error())
|
||||
}
|
||||
}()
|
||||
if c.RunMode == "http" {
|
||||
wsContainer := restful.NewContainer()
|
||||
server := &http.Server{Addr: c.APIAddr, Handler: wsContainer}
|
||||
|
@ -40,7 +40,9 @@ func Init(c *option.Conf, ms *masterserver.MasterServer) {
|
||||
taskTempService = service.CreateTaskTempService(c)
|
||||
taskGroupService = service.CreateTaskGroupService(c, ms)
|
||||
appService = service.CreateAppService(c)
|
||||
nodeService = service.CreateNodeService(c, ms.Cluster)
|
||||
if ms != nil {
|
||||
nodeService = service.CreateNodeService(c, ms.Cluster)
|
||||
}
|
||||
discoverService = service.CreateDiscoverActionManager(c)
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ package controller
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/goodrain/rainbond/pkg/api/util"
|
||||
"github.com/pquerna/ffjson/ffjson"
|
||||
@ -46,9 +47,9 @@ func ServiceDiscover(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
//ListenerDiscover ListenerDiscover
|
||||
func ListenerDiscover(w http.ResponseWriter, r *http.Request) {
|
||||
tenantName := chi.URLParam(r, "tenant_id")
|
||||
tenantService := chi.URLParam(r, "tenant_service")
|
||||
serviceNodes := chi.URLParam(r, "service_nodes")
|
||||
lds, err := discoverService.DiscoverListeners(tenantName, serviceNodes)
|
||||
lds, err := discoverService.DiscoverListeners(tenantService, serviceNodes)
|
||||
if err != nil {
|
||||
err.Handle(r, w)
|
||||
return
|
||||
@ -64,9 +65,9 @@ func ListenerDiscover(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
//ClusterDiscover ClusterDiscover
|
||||
func ClusterDiscover(w http.ResponseWriter, r *http.Request) {
|
||||
tenantName := chi.URLParam(r, "tenant_id")
|
||||
tenantService := chi.URLParam(r, "tenant_service")
|
||||
serviceNodes := chi.URLParam(r, "service_nodes")
|
||||
cds, err := discoverService.DiscoverClusters(tenantName, serviceNodes)
|
||||
cds, err := discoverService.DiscoverClusters(tenantService, serviceNodes)
|
||||
if err != nil {
|
||||
err.Handle(r, w)
|
||||
return
|
||||
@ -79,3 +80,12 @@ func ClusterDiscover(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte(cdsJ))
|
||||
}
|
||||
|
||||
//RoutesDiscover RoutesDiscover
|
||||
func RoutesDiscover(w http.ResponseWriter, r *http.Request) {
|
||||
namespace := chi.URLParam(r, "tenant_id")
|
||||
serviceNodes := chi.URLParam(r, "service_nodes")
|
||||
routeConfig := chi.URLParam(r, "route_config")
|
||||
logrus.Debugf("route_config is %s, namespace %s, serviceNodes %s", routeConfig, namespace, serviceNodes)
|
||||
w.WriteHeader(200)
|
||||
}
|
||||
|
@ -43,14 +43,25 @@ type CDS struct {
|
||||
|
||||
//PieceCDS struct
|
||||
type PieceCDS struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
ConnectTimeoutMS int `json:"connect_timeout_ms"`
|
||||
LBType string `json:"lb_type"`
|
||||
ServiceName string `json:"service_name"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
ConnectTimeoutMS int `json:"connect_timeout_ms"`
|
||||
LBType string `json:"lb_type"`
|
||||
ServiceName string `json:"service_name"`
|
||||
CircuitBreakers *CircuitBreakers `json:"circuit_breakers"`
|
||||
//HealthCheck cdsHealthCheckt `json:"health_check"`
|
||||
}
|
||||
|
||||
//MaxConnections circuit
|
||||
type MaxConnections struct {
|
||||
MaxConnections int `json:"max_connections"`
|
||||
}
|
||||
|
||||
//CircuitBreakers circuit
|
||||
type CircuitBreakers struct {
|
||||
Default *MaxConnections `json:"default"`
|
||||
}
|
||||
|
||||
type cdsHealthCheckt struct {
|
||||
Type string `json:"type"`
|
||||
TimeoutMS int `json:"timeout_ms"`
|
||||
@ -118,9 +129,16 @@ type PieceHTTPVirtualHost struct {
|
||||
|
||||
//PieceHTTPRoutes PieceHTTPRoutes
|
||||
type PieceHTTPRoutes struct {
|
||||
TimeoutMS int `json:"timeout_ms"`
|
||||
Prefix string `json:"prefix"`
|
||||
Cluster string `json:"cluster"`
|
||||
TimeoutMS int `json:"timeout_ms"`
|
||||
Prefix string `json:"prefix"`
|
||||
Cluster string `json:"cluster"`
|
||||
Headers []*PieceHeader `json:"headers"`
|
||||
}
|
||||
|
||||
//PieceHeader PieceHeader
|
||||
type PieceHeader struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
//HTTPSingleFileter HttpSingleFileter
|
||||
@ -129,3 +147,14 @@ type HTTPSingleFileter struct {
|
||||
Name string `json:"name"`
|
||||
Config map[string]string `json:"config"`
|
||||
}
|
||||
|
||||
const (
|
||||
//PREFIX PREFIX
|
||||
PREFIX string = "PREFIX"
|
||||
//HEADERS HEADERS
|
||||
HEADERS string = "HEADERS"
|
||||
//DOMAINS DOMAINS
|
||||
DOMAINS string = "DOMAINS"
|
||||
//LIMITS LIMITS
|
||||
LIMITS string = "LIMITS"
|
||||
)
|
||||
|
@ -110,18 +110,20 @@ func (h *HostNode) UpdataCondition(conditions ...NodeCondition) {
|
||||
ready = ConditionFalse
|
||||
}
|
||||
var update bool
|
||||
for i, con := range h.Conditions {
|
||||
if con.Type.Compare(newcon.Type) {
|
||||
h.Conditions[i] = newcon
|
||||
update = true
|
||||
}
|
||||
if con.Type == NodeReady {
|
||||
con.Status = ready
|
||||
con.LastTransitionTime = time.Now()
|
||||
con.LastHeartbeatTime = time.Now()
|
||||
con.Reason = newcon.Reason
|
||||
con.Message = newcon.Message
|
||||
h.Conditions[i] = con
|
||||
if h.Conditions != nil {
|
||||
for i, con := range h.Conditions {
|
||||
if con.Type.Compare(newcon.Type) {
|
||||
h.Conditions[i] = newcon
|
||||
update = true
|
||||
}
|
||||
if con.Type.Compare(NodeReady) {
|
||||
con.Status = ready
|
||||
con.LastTransitionTime = time.Now()
|
||||
con.LastHeartbeatTime = time.Now()
|
||||
con.Reason = newcon.Reason
|
||||
con.Message = newcon.Message
|
||||
h.Conditions[i] = con
|
||||
}
|
||||
}
|
||||
}
|
||||
if !update {
|
||||
|
@ -34,18 +34,30 @@ type TaskTemp struct {
|
||||
Name string `json:"name" validate:"name|required"`
|
||||
ID string `json:"id" validate:"id|uuid"`
|
||||
Shell Shell `json:"shell"`
|
||||
Envs map[string]string `json:"envs"`
|
||||
Input string `json:"input"`
|
||||
Args []string `json:"args"`
|
||||
Depends []string `json:"depends"`
|
||||
Timeout int `json:"timeout|required|numeric"`
|
||||
Envs map[string]string `json:"envs,omitempty"`
|
||||
Input string `json:"input,omitempty"`
|
||||
Args []string `json:"args,omitempty"`
|
||||
Depends []DependStrategy `json:"depends,omitempty"`
|
||||
Timeout int `json:"timeout" validate:"timeout|required|numeric"`
|
||||
//OutPutChan
|
||||
//结果输出通道,错误输出OR标准输出
|
||||
OutPutChan string `json:"out_put_chan" validate:"out_put_chan|required|in:stdout,stderr"`
|
||||
CreateTime time.Time `json:"create_time"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
//DependStrategy 依赖策略
|
||||
type DependStrategy struct {
|
||||
DependTaskID string `json:"depend_task_id"`
|
||||
DetermineStrategy string `json:"strategy"`
|
||||
}
|
||||
|
||||
//AtLeastOnceStrategy 至少已执行一次
|
||||
var AtLeastOnceStrategy = "AtLeastOnce"
|
||||
|
||||
//SameNodeStrategy 相同节点已执行
|
||||
var SameNodeStrategy = "SameNode"
|
||||
|
||||
func (t TaskTemp) String() string {
|
||||
res, _ := ffjson.Marshal(&t)
|
||||
return string(res)
|
||||
@ -88,6 +100,17 @@ func (t Task) String() string {
|
||||
return string(res)
|
||||
}
|
||||
|
||||
//UpdataOutPut 更新状态
|
||||
func (t *Task) UpdataOutPut(output TaskOutPut) {
|
||||
for _, oldOut := range t.OutPut {
|
||||
if oldOut.NodeID == output.NodeID {
|
||||
*oldOut = output
|
||||
return
|
||||
}
|
||||
}
|
||||
t.OutPut = append(t.OutPut, &output)
|
||||
}
|
||||
|
||||
//CanBeDelete 能否被删除
|
||||
func (t Task) CanBeDelete() bool {
|
||||
if t.Status == nil || len(t.Status) == 0 {
|
||||
@ -116,10 +139,12 @@ type TaskOutPut struct {
|
||||
//返回数据类型,检测结果类(check) 执行安装类 (install) 普通类 (common)
|
||||
Type string `json:"type"`
|
||||
Status []TaskOutPutStatus `json:"status"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
//ParseTaskOutPut json parse
|
||||
func ParseTaskOutPut(body string) (t TaskOutPut, err error) {
|
||||
t.Body = body
|
||||
err = ffjson.Unmarshal([]byte(body), &t)
|
||||
return
|
||||
}
|
||||
@ -129,7 +154,8 @@ type TaskOutPutStatus struct {
|
||||
Name string `json:"name"`
|
||||
ConditionType string `json:"condition_type"`
|
||||
ConditionStatus string `json:"condition_status"`
|
||||
NextTask []string `json:"next_tasks"`
|
||||
NextTask []string `json:"next_tasks,omitempty"`
|
||||
NextGroups []string `json:"next_groups,omitempty"`
|
||||
}
|
||||
|
||||
//TaskStatus 任务状态
|
||||
|
@ -31,6 +31,7 @@ func DisconverRoutes() chi.Router {
|
||||
r.Mount("/listeners", ListenersRoutes())
|
||||
r.Mount("/clusters", ClustersRoutes())
|
||||
r.Mount("/registration", RegistrationRoutes())
|
||||
r.Mount("/routes", RoutesRouters())
|
||||
return r
|
||||
}
|
||||
|
||||
@ -39,7 +40,7 @@ func DisconverRoutes() chi.Router {
|
||||
func ListenersRoutes() chi.Router {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/ping", controller.Ping)
|
||||
r.Get("/{tenant_id}/{service_nodes}", controller.ListenerDiscover)
|
||||
r.Get("/{tenant_service}/{service_nodes}", controller.ListenerDiscover)
|
||||
return r
|
||||
}
|
||||
|
||||
@ -48,7 +49,7 @@ func ListenersRoutes() chi.Router {
|
||||
func ClustersRoutes() chi.Router {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/ping", controller.Ping)
|
||||
r.Get("/{tenant_id}/{service_nodes}", controller.ClusterDiscover)
|
||||
r.Get("/{tenant_service}/{service_nodes}", controller.ClusterDiscover)
|
||||
return r
|
||||
}
|
||||
|
||||
@ -60,3 +61,12 @@ func RegistrationRoutes() chi.Router {
|
||||
r.Get("/{service_name}", controller.ServiceDiscover)
|
||||
return r
|
||||
}
|
||||
|
||||
//RoutesRouters rds
|
||||
//GET /v1/routes/(string: route_config_name)/(string: service_cluster)/(string: service_node)
|
||||
func RoutesRouters() chi.Router {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/ping", controller.Ping)
|
||||
r.Get("/{route_config}/{tenant_service}/{service_nodes}", controller.RoutesDiscover)
|
||||
return r
|
||||
}
|
||||
|
@ -20,40 +20,57 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/goodrain/rainbond/cmd/node/option"
|
||||
"github.com/goodrain/rainbond/pkg/node/core/store"
|
||||
)
|
||||
|
||||
//GroupContext 组任务会话
|
||||
type GroupContext struct {
|
||||
configs map[interface{}]interface{}
|
||||
ctx context.Context
|
||||
groupID string
|
||||
}
|
||||
|
||||
//NewGroupContext 创建组配置会话
|
||||
func NewGroupContext() *GroupContext {
|
||||
func NewGroupContext(groupID string) *GroupContext {
|
||||
return &GroupContext{
|
||||
configs: make(map[interface{}]interface{}),
|
||||
ctx: context.Background(),
|
||||
groupID: groupID,
|
||||
}
|
||||
}
|
||||
|
||||
//Add 添加配置项
|
||||
func (g *GroupContext) Add(k, v interface{}) {
|
||||
g.ctx = context.WithValue(g.ctx, k, v)
|
||||
g.configs[k] = v
|
||||
store.DefalutClient.Put(fmt.Sprintf("%s/group/%s/%s", option.Config.ConfigStoragePath, g.groupID, k), v.(string))
|
||||
}
|
||||
|
||||
//Get get
|
||||
func (g *GroupContext) Get(k interface{}) interface{} {
|
||||
return g.ctx.Value(k)
|
||||
if v := g.ctx.Value(k); v != nil {
|
||||
return v
|
||||
}
|
||||
res, _ := store.DefalutClient.Get(fmt.Sprintf("%s/group/%s/%s", option.Config.ConfigStoragePath, g.groupID, k))
|
||||
if res.Count > 0 {
|
||||
return string(res.Kvs[0].Value)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
//GetString get
|
||||
func (g *GroupContext) GetString(k interface{}) string {
|
||||
return g.ctx.Value(k).(string)
|
||||
if v := g.ctx.Value(k); v != nil {
|
||||
return v.(string)
|
||||
}
|
||||
res, _ := store.DefalutClient.Get(fmt.Sprintf("%s/group/%s/%s", option.Config.ConfigStoragePath, g.groupID, k))
|
||||
if res.Count > 0 {
|
||||
return string(res.Kvs[0].Value)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var reg = regexp.MustCompile(`(?U)\$\{.*\}`)
|
||||
|
@ -307,14 +307,13 @@ func (j *JobRule) Valid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(j.Timer) == 0 {
|
||||
return utils.ErrNilRule
|
||||
if len(j.Timer) > 0 {
|
||||
sch, err := cron.Parse(j.Timer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid JobRule[%s], parse err: %s", j.Timer, err.Error())
|
||||
}
|
||||
j.Schedule = sch
|
||||
}
|
||||
sch, err := cron.Parse(j.Timer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid JobRule[%s], parse err: %s", j.Timer, err.Error())
|
||||
}
|
||||
j.Schedule = sch
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -326,19 +325,23 @@ func (j *JobRule) included(node *model.HostNode) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
//是否属于允许节点
|
||||
for _, id := range j.NodeIDs {
|
||||
if id == node.ID {
|
||||
return true
|
||||
if j.NodeIDs != nil && len(j.NodeIDs) > 0 {
|
||||
//是否属于允许节点
|
||||
for _, id := range j.NodeIDs {
|
||||
if id == node.ID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
//是否匹配label
|
||||
for k, v := range j.Labels {
|
||||
if nodev := node.Labels[k]; nodev != v {
|
||||
return false
|
||||
} else {
|
||||
//是否匹配label
|
||||
for k, v := range j.Labels {
|
||||
if nodev := node.Labels[k]; nodev != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
//GetJob get job
|
||||
|
@ -1,19 +1,18 @@
|
||||
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
@ -21,18 +20,16 @@ package k8s
|
||||
|
||||
import (
|
||||
"github.com/goodrain/rainbond/cmd/node/option"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
//"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
var (
|
||||
K8S *K8sClient
|
||||
K8S *Client
|
||||
)
|
||||
|
||||
type K8sClient struct {
|
||||
//Client k8sclient
|
||||
type Client struct {
|
||||
*kubernetes.Clientset
|
||||
}
|
||||
|
||||
@ -46,7 +43,7 @@ func NewK8sClient(cfg *option.Conf) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := K8sClient{
|
||||
client := Client{
|
||||
Clientset: cli,
|
||||
}
|
||||
K8S = &client
|
||||
|
@ -20,11 +20,12 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/goodrain/rainbond/cmd/node/option"
|
||||
"github.com/goodrain/rainbond/pkg/api/util"
|
||||
"github.com/goodrain/rainbond/pkg/db"
|
||||
node_model "github.com/goodrain/rainbond/pkg/node/api/model"
|
||||
"github.com/goodrain/rainbond/pkg/node/core/k8s"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@ -55,7 +56,8 @@ func (d *DiscoverAction) DiscoverService(serviceInfo string) (*node_model.SDS, *
|
||||
//deployVersion := mm[3]
|
||||
|
||||
labelname := fmt.Sprintf("name=%sService", serviceAlias)
|
||||
endpoint, err := k8s.K8S.Core().Endpoints(namespace).List(metav1.ListOptions{LabelSelector: labelname})
|
||||
endpoints, err := k8s.K8S.Core().Endpoints(namespace).List(metav1.ListOptions{LabelSelector: labelname})
|
||||
logrus.Debugf("labelname is %s, endpoints is %v, items is %v", labelname, endpoints, endpoints.Items)
|
||||
if err != nil {
|
||||
return nil, util.CreateAPIHandleError(500, err)
|
||||
}
|
||||
@ -63,11 +65,11 @@ func (d *DiscoverAction) DiscoverService(serviceInfo string) (*node_model.SDS, *
|
||||
if err != nil {
|
||||
return nil, util.CreateAPIHandleError(500, err)
|
||||
}
|
||||
if len(endpoint.Items) == 0 {
|
||||
if len(endpoints.Items) == 0 {
|
||||
return nil, util.CreateAPIHandleError(400, fmt.Errorf("have no endpoints"))
|
||||
}
|
||||
var sdsL []*node_model.PieceSDS
|
||||
for key, item := range endpoint.Items {
|
||||
for key, item := range endpoints.Items {
|
||||
addressList := item.Subsets[0].Addresses
|
||||
if len(addressList) == 0 {
|
||||
addressList = item.Subsets[0].NotReadyAddresses
|
||||
@ -92,7 +94,18 @@ func (d *DiscoverAction) DiscoverService(serviceInfo string) (*node_model.SDS, *
|
||||
}
|
||||
|
||||
//DiscoverListeners DiscoverListeners
|
||||
func (d *DiscoverAction) DiscoverListeners(namespace, serviceCluster string) (*node_model.LDS, *util.APIHandleError) {
|
||||
func (d *DiscoverAction) DiscoverListeners(tenantService, serviceCluster string) (*node_model.LDS, *util.APIHandleError) {
|
||||
nn := strings.Split(tenantService, "_")
|
||||
if len(nn) != 2 {
|
||||
return nil, util.CreateAPIHandleError(400,
|
||||
fmt.Errorf("namesapces and service_alias not in good format"))
|
||||
}
|
||||
namespace := nn[0]
|
||||
serviceAlias := nn[1]
|
||||
envs, err := d.ToolsGetMainPodEnvs(namespace, serviceAlias)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mm := strings.Split(serviceCluster, "_")
|
||||
if len(mm) == 0 {
|
||||
return nil, util.CreateAPIHandleError(400, fmt.Errorf("service_name is not in good format"))
|
||||
@ -112,32 +125,25 @@ func (d *DiscoverAction) DiscoverListeners(namespace, serviceCluster string) (*n
|
||||
continue
|
||||
}
|
||||
for _, service := range services.Items {
|
||||
//protocol, ok := service.Labels["protocol"]
|
||||
// if ok && protocol == "http" {
|
||||
// //TODO: HTTP inner的protocol添加资源时需要label
|
||||
// hsf := &node_model.HTTPSingleFileter{
|
||||
// Type: "decoder",
|
||||
// Name: "router",
|
||||
// }
|
||||
// prs := &node_model.PieceHTTPRoutes{
|
||||
// TimeoutMS: 0,
|
||||
// Prefix: "/",
|
||||
// Cluster: "",
|
||||
// }
|
||||
// continue
|
||||
// }
|
||||
//TODO: TCP
|
||||
for _, port := range service.Spec.Ports {
|
||||
switch port.Protocol {
|
||||
case "TCP":
|
||||
//TODO: HTTP inner的protocol添加资源时需要label
|
||||
inner, ok := service.Labels["service_type"]
|
||||
if !ok || inner != "inner" {
|
||||
continue
|
||||
}
|
||||
port := service.Spec.Ports[0].Port
|
||||
portProtocol, ok := service.Labels["port_protocol"]
|
||||
if ok {
|
||||
logrus.Debugf("port protocol is %s", portProtocol)
|
||||
switch portProtocol {
|
||||
case "stream":
|
||||
ptr := &node_model.PieceTCPRoute{
|
||||
Cluster: fmt.Sprintf("%s_%s_%v", namespace, serviceAlias, port.Port),
|
||||
Cluster: fmt.Sprintf("%s_%s_%d", namespace, serviceAlias, port),
|
||||
}
|
||||
lrs := &node_model.LDSTCPRoutes{
|
||||
Routes: []*node_model.PieceTCPRoute{ptr},
|
||||
}
|
||||
lcg := &node_model.LDSTCPConfig{
|
||||
StatPrefix: fmt.Sprintf("%s_%s_%v", namespace, serviceAlias, port.Port),
|
||||
StatPrefix: fmt.Sprintf("%s_%s_%d", namespace, serviceAlias, port),
|
||||
RouteConfig: lrs,
|
||||
}
|
||||
lfs := &node_model.LDSFilters{
|
||||
@ -145,29 +151,48 @@ func (d *DiscoverAction) DiscoverListeners(namespace, serviceCluster string) (*n
|
||||
Config: lcg,
|
||||
}
|
||||
plds := &node_model.PieceLDS{
|
||||
Name: fmt.Sprintf("%s_%s_%v", namespace, serviceAlias, port.Port),
|
||||
Address: fmt.Sprintf("tcp://0.0.0.0:%v", port.Port),
|
||||
Name: fmt.Sprintf("%s_%s_%d", namespace, serviceAlias, port),
|
||||
Address: fmt.Sprintf("tcp://0.0.0.0:%d", port),
|
||||
Filters: []*node_model.LDSFilters{lfs},
|
||||
}
|
||||
ldsL = append(ldsL, plds)
|
||||
continue
|
||||
case "HTTP":
|
||||
case "http":
|
||||
hsf := &node_model.HTTPSingleFileter{
|
||||
Type: "decoder",
|
||||
Name: "router",
|
||||
Type: "decoder",
|
||||
Name: "router",
|
||||
Config: make(map[string]string),
|
||||
}
|
||||
prs := &node_model.PieceHTTPRoutes{
|
||||
TimeoutMS: 0,
|
||||
Prefix: "/",
|
||||
Cluster: fmt.Sprintf("%s_%s_%v", namespace, serviceAlias, port.Port),
|
||||
Prefix: d.ToolsGetRouterItem(serviceAlias, node_model.PREFIX, envs),
|
||||
Cluster: fmt.Sprintf("%s_%s_%d", namespace, serviceAlias, port),
|
||||
Headers: []*node_model.PieceHeader{},
|
||||
}
|
||||
domain, ok := service.Labels["domain"]
|
||||
if !ok {
|
||||
domain = "*"
|
||||
envHeaders := d.ToolsGetRouterItem(serviceAlias, node_model.HEADERS, envs)
|
||||
var headers []*node_model.PieceHeader
|
||||
if envHeaders != "" {
|
||||
mm := strings.Split(envHeaders, ",")
|
||||
for _, h := range mm {
|
||||
nn := strings.Split(h, ":")
|
||||
header := &node_model.PieceHeader{
|
||||
Name: nn[0],
|
||||
Value: nn[1],
|
||||
}
|
||||
headers = append(headers, header)
|
||||
}
|
||||
} else {
|
||||
header := &node_model.PieceHeader{
|
||||
Name: "default",
|
||||
Value: "default",
|
||||
}
|
||||
headers = append(headers, header)
|
||||
}
|
||||
prs.Headers = headers
|
||||
pvh := &node_model.PieceHTTPVirtualHost{
|
||||
Name: fmt.Sprintf("%s_%s_%v", namespace, serviceAlias, port.Port),
|
||||
Domains: []string{domain},
|
||||
//TODO: 目前支持自定义一个domain
|
||||
Name: fmt.Sprintf("%s_%s_%d", namespace, serviceAlias, port),
|
||||
Domains: []string{d.ToolsGetRouterItem(serviceAlias, node_model.DOMAINS, envs)},
|
||||
Routes: []*node_model.PieceHTTPRoutes{prs},
|
||||
}
|
||||
rcg := &node_model.RouteConfig{
|
||||
@ -184,8 +209,8 @@ func (d *DiscoverAction) DiscoverListeners(namespace, serviceCluster string) (*n
|
||||
Config: lhc,
|
||||
}
|
||||
plds := &node_model.PieceLDS{
|
||||
Name: fmt.Sprintf("%s_%s_%v", namespace, serviceAlias, port.Port),
|
||||
Address: fmt.Sprintf("tcp://0.0.0.0:%v", port.TargetPort),
|
||||
Name: fmt.Sprintf("%s_%s_%d", namespace, serviceAlias, port),
|
||||
Address: fmt.Sprintf("tcp://0.0.0.0:%d", port),
|
||||
Filters: []*node_model.LDSFilters{lfs},
|
||||
}
|
||||
ldsL = append(ldsL, plds)
|
||||
@ -201,7 +226,17 @@ func (d *DiscoverAction) DiscoverListeners(namespace, serviceCluster string) (*n
|
||||
}
|
||||
|
||||
//DiscoverClusters DiscoverClusters
|
||||
func (d *DiscoverAction) DiscoverClusters(namespace, serviceCluster string) (*node_model.CDS, *util.APIHandleError) {
|
||||
func (d *DiscoverAction) DiscoverClusters(tenantService, serviceCluster string) (*node_model.CDS, *util.APIHandleError) {
|
||||
nn := strings.Split(tenantService, "_")
|
||||
if len(nn) != 2 {
|
||||
return nil, util.CreateAPIHandleError(400, fmt.Errorf("namesapces and service_alias not in good format"))
|
||||
}
|
||||
namespace := nn[0]
|
||||
serviceAlias := nn[1]
|
||||
envs, err := d.ToolsGetMainPodEnvs(namespace, serviceAlias)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mm := strings.Split(serviceCluster, "_")
|
||||
if len(mm) == 0 {
|
||||
return nil, util.CreateAPIHandleError(400, fmt.Errorf("service_name is not in good format"))
|
||||
@ -214,17 +249,31 @@ func (d *DiscoverAction) DiscoverClusters(namespace, serviceCluster string) (*no
|
||||
return nil, util.CreateAPIHandleError(500, err)
|
||||
}
|
||||
for _, service := range services.Items {
|
||||
for _, port := range service.Spec.Ports {
|
||||
pcds := &node_model.PieceCDS{
|
||||
Name: fmt.Sprintf("%s_%s_%v", namespace, serviceAlias, port.Port),
|
||||
Type: "sds",
|
||||
ConnectTimeoutMS: 250,
|
||||
LBType: "round_robin",
|
||||
ServiceName: fmt.Sprintf("%s_%s_%v", namespace, serviceAlias, port.Port),
|
||||
}
|
||||
cdsL = append(cdsL, pcds)
|
||||
inner, ok := service.Labels["service_type"]
|
||||
if !ok || inner != "inner" {
|
||||
continue
|
||||
}
|
||||
circuits, errC := strconv.Atoi(d.ToolsGetRouterItem(serviceAlias, node_model.LIMITS, envs))
|
||||
if errC != nil {
|
||||
circuits = 1024
|
||||
logrus.Warnf("strconv circuit error, ignore this error and set circuits to 1024")
|
||||
}
|
||||
cb := &node_model.CircuitBreakers{
|
||||
Default: &node_model.MaxConnections{
|
||||
MaxConnections: circuits,
|
||||
},
|
||||
}
|
||||
port := service.Spec.Ports[0]
|
||||
pcds := &node_model.PieceCDS{
|
||||
Name: fmt.Sprintf("%s_%s_%v", namespace, serviceAlias, port.Port),
|
||||
Type: "sds",
|
||||
ConnectTimeoutMS: 250,
|
||||
LBType: "round_robin",
|
||||
ServiceName: fmt.Sprintf("%s_%s_%v", namespace, serviceAlias, port.Port),
|
||||
CircuitBreakers: cb,
|
||||
}
|
||||
cdsL = append(cdsL, pcds)
|
||||
continue
|
||||
}
|
||||
}
|
||||
cds := &node_model.CDS{
|
||||
@ -233,24 +282,6 @@ func (d *DiscoverAction) DiscoverClusters(namespace, serviceCluster string) (*no
|
||||
return cds, nil
|
||||
}
|
||||
|
||||
//ToolsGetTenantUUID GetTenantUUID
|
||||
func (d *DiscoverAction) ToolsGetTenantUUID(namespace string) (string, error) {
|
||||
tenants, err := db.GetManager().TenantDao().GetTenantIDByName(namespace)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tenants.UUID, nil
|
||||
}
|
||||
|
||||
//ToolsGetServiceID GetServiceID
|
||||
func (d *DiscoverAction) ToolsGetServiceID(uuid, serviceAlias string) (string, error) {
|
||||
services, err := db.GetManager().TenantServiceDao().GetServiceByTenantIDAndServiceAlias(uuid, serviceAlias)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return services.ServiceID, nil
|
||||
}
|
||||
|
||||
//ToolsGetK8SServiceList GetK8SServiceList
|
||||
func (d *DiscoverAction) ToolsGetK8SServiceList(uuid string) (*v1.ServiceList, error) {
|
||||
serviceList, err := k8s.K8S.Core().Services(uuid).List(metav1.ListOptions{})
|
||||
@ -259,3 +290,80 @@ func (d *DiscoverAction) ToolsGetK8SServiceList(uuid string) (*v1.ServiceList, e
|
||||
}
|
||||
return serviceList, nil
|
||||
}
|
||||
|
||||
//ToolsGetMainPodEnvs ToolsGetMainPodEnvs
|
||||
func (d *DiscoverAction) ToolsGetMainPodEnvs(namespace, serviceAlias string) (*[]v1.EnvVar, *util.APIHandleError) {
|
||||
labelname := fmt.Sprintf("name=%s", serviceAlias)
|
||||
pods, err := k8s.K8S.Core().Pods(namespace).List(metav1.ListOptions{LabelSelector: labelname})
|
||||
logrus.Debugf("service_alias %s pod is %v", serviceAlias, pods)
|
||||
if err != nil {
|
||||
return nil, util.CreateAPIHandleError(500, err)
|
||||
}
|
||||
if len(pods.Items) == 0 {
|
||||
return nil,
|
||||
util.CreateAPIHandleError(404, fmt.Errorf("have no pod for discover"))
|
||||
}
|
||||
if len(pods.Items[0].Spec.Containers) < 2 {
|
||||
return nil,
|
||||
util.CreateAPIHandleError(404, fmt.Errorf("have no net plugins for discover"))
|
||||
}
|
||||
for _, c := range pods.Items[0].Spec.Containers {
|
||||
for _, e := range c.Env {
|
||||
if e.Name == "PLUGIN_MOEL" && strings.Contains(e.Value, "net-plugin") {
|
||||
return &c.Env, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, util.CreateAPIHandleError(404, fmt.Errorf("have no envs for plugin"))
|
||||
}
|
||||
|
||||
//ToolsBuildPieceLDS ToolsBuildPieceLDS
|
||||
func (d *DiscoverAction) ToolsBuildPieceLDS() {}
|
||||
|
||||
//ToolsGetRouterItem ToolsGetRouterItem
|
||||
func (d *DiscoverAction) ToolsGetRouterItem(destAlias, kind string, envs *[]v1.EnvVar) string {
|
||||
switch kind {
|
||||
case node_model.PREFIX:
|
||||
ename := fmt.Sprintf("PREFIX_%s", destAlias)
|
||||
for _, e := range *envs {
|
||||
if e.Name == ename {
|
||||
return e.Value
|
||||
}
|
||||
}
|
||||
return "/"
|
||||
case node_model.LIMITS:
|
||||
ename := fmt.Sprintf("LIMIT_%s", destAlias)
|
||||
for _, e := range *envs {
|
||||
if e.Name == ename {
|
||||
return e.Value
|
||||
}
|
||||
}
|
||||
return "1024"
|
||||
case node_model.HEADERS:
|
||||
ename := fmt.Sprintf("HEADER_%s", destAlias)
|
||||
for _, e := range *envs {
|
||||
if e.Name == ename {
|
||||
return e.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
case node_model.DOMAINS:
|
||||
ename := fmt.Sprintf("DOMAIN_%s", destAlias)
|
||||
for _, e := range *envs {
|
||||
if e.Name == ename {
|
||||
return e.Value
|
||||
}
|
||||
}
|
||||
return "*"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getEnvValue(ename string, envs *[]v1.EnvVar) string {
|
||||
for _, e := range *envs {
|
||||
if e.Name == ename {
|
||||
return e.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -170,7 +170,7 @@ func (ts *TaskService) ExecTask(taskID string) *utils.APIHandleError {
|
||||
// }
|
||||
return utils.CreateAPIHandleError(400, fmt.Errorf("Single task exec can not have depend task"))
|
||||
}
|
||||
ts.ms.TaskEngine.ScheduleTask(t.ID)
|
||||
ts.ms.TaskEngine.ScheduleTask(nil, t)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -351,6 +351,6 @@ func (ts *TaskGroupService) ExecTaskGroup(taskGroupID string) *utils.APIHandleEr
|
||||
return err
|
||||
}
|
||||
//TODO:增加执行判断
|
||||
ts.ms.TaskEngine.ScheduleGroup(t.ID)
|
||||
ts.ms.TaskEngine.ScheduleGroup(nil, t)
|
||||
return nil
|
||||
}
|
||||
|
@ -108,19 +108,26 @@ func (n *NodeCluster) loadNodes() error {
|
||||
}
|
||||
}
|
||||
//加载k8s节点信息
|
||||
list, err := n.k8sClient.Core().Nodes().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("load k8s nodes from k8s api error:%s", err.Error())
|
||||
}
|
||||
for _, node := range list.Items {
|
||||
if cn, ok := n.nodes[node.Name]; ok {
|
||||
cn.NodeStatus = &node.Status
|
||||
cn.UpdataK8sCondition(node.Status.Conditions)
|
||||
n.UpdateNode(cn)
|
||||
} else {
|
||||
logrus.Warningf("k8s node %s can not exist in rainbond cluster.", node.Name)
|
||||
go func() {
|
||||
for {
|
||||
list, err := n.k8sClient.Core().Nodes().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
logrus.Warnf("load k8s nodes from k8s api error:%s", err.Error())
|
||||
time.Sleep(time.Second * 3)
|
||||
continue
|
||||
}
|
||||
for _, node := range list.Items {
|
||||
if cn, ok := n.nodes[node.Name]; ok {
|
||||
cn.NodeStatus = &node.Status
|
||||
cn.UpdataK8sCondition(node.Status.Conditions)
|
||||
n.UpdateNode(cn)
|
||||
} else {
|
||||
logrus.Warningf("k8s node %s can not exist in rainbond cluster.", node.Name)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -139,6 +146,9 @@ func (n *NodeCluster) worker() {
|
||||
|
||||
//UpdateNode 更新节点信息
|
||||
func (n *NodeCluster) UpdateNode(node *model.HostNode) {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
n.nodes[node.ID] = node
|
||||
n.client.Put(option.Config.NodePath+"/"+node.ID, node.String())
|
||||
}
|
||||
func (n *NodeCluster) getNodeFromKV(kv *mvccpb.KeyValue) *model.HostNode {
|
||||
@ -160,6 +170,8 @@ func (n *NodeCluster) getNodeFromKey(key string) *model.HostNode {
|
||||
|
||||
//GetNode 从缓存获取节点信息
|
||||
func (n *NodeCluster) GetNode(id string) *model.HostNode {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
if node, ok := n.nodes[id]; ok {
|
||||
return node
|
||||
}
|
||||
@ -210,10 +222,15 @@ func (n *NodeCluster) watchK8sNodes() {
|
||||
for {
|
||||
wc, err := n.k8sClient.Core().Nodes().Watch(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
logrus.Error("watch k8s node error.", err.Error())
|
||||
logrus.Warningf("watch k8s node error.", err.Error())
|
||||
time.Sleep(time.Second * 5)
|
||||
continue
|
||||
}
|
||||
defer wc.Stop()
|
||||
defer func() {
|
||||
if wc != nil {
|
||||
wc.Stop()
|
||||
}
|
||||
}()
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
@ -225,6 +242,7 @@ func (n *NodeCluster) watchK8sNodes() {
|
||||
switch {
|
||||
case event.Type == watch.Added, event.Type == watch.Modified:
|
||||
if node, ok := event.Object.(*v1.Node); ok {
|
||||
//k8s node name is rainbond node id
|
||||
if rbnode := n.GetNode(node.Name); rbnode != nil {
|
||||
rbnode.NodeStatus = &node.Status
|
||||
rbnode.UpdataK8sCondition(node.Status.Conditions)
|
||||
@ -318,6 +336,9 @@ func (n *NodeCluster) RemoveNode(node *model.HostNode) {
|
||||
//UpdateNodeCondition 更新节点状态
|
||||
func (n *NodeCluster) UpdateNodeCondition(nodeID, ctype, cvalue string) {
|
||||
node := n.GetNode(nodeID)
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
node.UpdataCondition(model.NodeCondition{
|
||||
Type: model.NodeConditionType(ctype),
|
||||
Status: model.ConditionStatus(cvalue),
|
||||
@ -326,4 +347,5 @@ func (n *NodeCluster) UpdateNodeCondition(nodeID, ctype, cvalue string) {
|
||||
Message: "",
|
||||
Reason: "",
|
||||
})
|
||||
n.UpdateNode(node)
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ package masterserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
@ -35,6 +36,7 @@ import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
|
||||
client "github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/mvcc/mvccpb"
|
||||
"github.com/goodrain/rainbond/cmd/node/option"
|
||||
"github.com/goodrain/rainbond/pkg/node/api/model"
|
||||
"github.com/goodrain/rainbond/pkg/node/core/job"
|
||||
@ -47,35 +49,33 @@ type TaskEngine struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
config *option.Conf
|
||||
statics map[string]*model.Task
|
||||
staticLock sync.Mutex
|
||||
tasks map[string]*model.Task
|
||||
tasksLock sync.Mutex
|
||||
dataCenterConfig *config.DataCenterConfig
|
||||
groups map[string]*model.TaskGroup
|
||||
groupContexts map[string]*config.GroupContext
|
||||
groupLock sync.Mutex
|
||||
nodeCluster *NodeCluster
|
||||
}
|
||||
|
||||
//CreateTaskEngine 创建task管理引擎
|
||||
func CreateTaskEngine(nodeCluster *NodeCluster) *TaskEngine {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
return &TaskEngine{
|
||||
task := &TaskEngine{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
statics: make(map[string]*model.Task),
|
||||
tasks: make(map[string]*model.Task),
|
||||
config: option.Config,
|
||||
dataCenterConfig: config.GetDataCenterConfig(),
|
||||
groups: make(map[string]*model.TaskGroup),
|
||||
groupContexts: make(map[string]*config.GroupContext),
|
||||
nodeCluster: nodeCluster,
|
||||
}
|
||||
task.loadTask()
|
||||
task.LoadStaticTask()
|
||||
return task
|
||||
}
|
||||
|
||||
//Start 启动
|
||||
func (t *TaskEngine) Start() {
|
||||
logrus.Info("task engine start")
|
||||
go t.LoadStaticTask()
|
||||
go t.HandleJobRecord()
|
||||
go t.watchTasks()
|
||||
}
|
||||
|
||||
//Stop 启动
|
||||
@ -83,6 +83,71 @@ func (t *TaskEngine) Stop() {
|
||||
t.cancel()
|
||||
}
|
||||
|
||||
//loadTask 加载所有task
|
||||
func (t *TaskEngine) loadTask() error {
|
||||
//加载节点信息
|
||||
res, err := store.DefalutClient.Get("/store/tasks/", client.WithPrefix())
|
||||
if err != nil {
|
||||
return fmt.Errorf("load tasks error:%s", err.Error())
|
||||
}
|
||||
for _, kv := range res.Kvs {
|
||||
if task := t.getTaskFromKV(kv); task != nil {
|
||||
t.CacheTask(task)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//watchTasks watchTasks
|
||||
func (t *TaskEngine) watchTasks() {
|
||||
ch := store.DefalutClient.Watch("/store/tasks/", client.WithPrefix())
|
||||
for {
|
||||
select {
|
||||
case <-t.ctx.Done():
|
||||
return
|
||||
case event := <-ch:
|
||||
for _, ev := range event.Events {
|
||||
switch {
|
||||
case ev.IsCreate(), ev.IsModify():
|
||||
if task := t.getTaskFromKV(ev.Kv); task != nil {
|
||||
t.CacheTask(task)
|
||||
}
|
||||
case ev.Type == client.EventTypeDelete:
|
||||
if task := t.getTaskFromKey(string(ev.Kv.Key)); task != nil {
|
||||
t.RemoveTask(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func (t *TaskEngine) getTaskFromKey(key string) *model.Task {
|
||||
index := strings.LastIndex(key, "/")
|
||||
if index < 0 {
|
||||
return nil
|
||||
}
|
||||
id := key[index+1:]
|
||||
return t.GetTask(id)
|
||||
}
|
||||
|
||||
//RemoveTask 从缓存移除task
|
||||
func (t *TaskEngine) RemoveTask(task *model.Task) {
|
||||
t.tasksLock.Lock()
|
||||
defer t.tasksLock.Unlock()
|
||||
if _, ok := t.tasks[task.ID]; ok {
|
||||
delete(t.tasks, task.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TaskEngine) getTaskFromKV(kv *mvccpb.KeyValue) *model.Task {
|
||||
var task model.Task
|
||||
if err := ffjson.Unmarshal(kv.Value, &task); err != nil {
|
||||
logrus.Error("parse task info error:", err.Error())
|
||||
return nil
|
||||
}
|
||||
return &task
|
||||
}
|
||||
|
||||
//LoadStaticTask 从文件加载task
|
||||
//TODO:动态加载
|
||||
func (t *TaskEngine) LoadStaticTask() {
|
||||
@ -115,34 +180,64 @@ func (t *TaskEngine) loadFile(path string) {
|
||||
logrus.Errorf("read static task file %s error.%s", path, err.Error())
|
||||
return
|
||||
}
|
||||
var task model.Task
|
||||
if err := ffjson.Unmarshal(taskBody, &task); err != nil {
|
||||
logrus.Errorf("unmarshal static task file %s error.%s", path, err.Error())
|
||||
return
|
||||
var filename string
|
||||
index := strings.LastIndex(path, "/")
|
||||
if index < 0 {
|
||||
filename = path
|
||||
}
|
||||
if task.ID == "" {
|
||||
task.ID = task.Name
|
||||
filename = path[index+1:]
|
||||
if strings.Contains(filename, "group") {
|
||||
var group model.TaskGroup
|
||||
if err := ffjson.Unmarshal(taskBody, &group); err != nil {
|
||||
logrus.Errorf("unmarshal static task file %s error.%s", path, err.Error())
|
||||
return
|
||||
}
|
||||
if group.ID == "" {
|
||||
group.ID = group.Name
|
||||
}
|
||||
if group.Name == "" {
|
||||
logrus.Errorf("task group name can not be empty. file %s", path)
|
||||
return
|
||||
}
|
||||
if group.Tasks == nil {
|
||||
logrus.Errorf("task group tasks can not be empty. file %s", path)
|
||||
return
|
||||
}
|
||||
t.ScheduleGroup(nil, &group)
|
||||
logrus.Infof("Load a static group %s.", group.Name)
|
||||
}
|
||||
if task.Name == "" {
|
||||
logrus.Errorf("task name can not be empty. file %s", path)
|
||||
return
|
||||
if strings.Contains(filename, "task") {
|
||||
var task model.Task
|
||||
if err := ffjson.Unmarshal(taskBody, &task); err != nil {
|
||||
logrus.Errorf("unmarshal static task file %s error.%s", path, err.Error())
|
||||
return
|
||||
}
|
||||
if task.ID == "" {
|
||||
task.ID = task.Name
|
||||
}
|
||||
if task.Name == "" {
|
||||
logrus.Errorf("task name can not be empty. file %s", path)
|
||||
return
|
||||
}
|
||||
if task.Temp == nil {
|
||||
logrus.Errorf("task [%s] temp can not be empty.", task.Name)
|
||||
return
|
||||
}
|
||||
if task.Temp.ID == "" {
|
||||
task.Temp.ID = task.Temp.Name
|
||||
}
|
||||
t.AddTask(&task)
|
||||
logrus.Infof("Load a static task %s.", task.Name)
|
||||
}
|
||||
if task.Temp == nil {
|
||||
logrus.Errorf("task [%s] temp can not be empty.", task.Name)
|
||||
return
|
||||
}
|
||||
if task.Temp.ID == "" {
|
||||
task.Temp.ID = task.Temp.Name
|
||||
}
|
||||
t.staticLock.Lock()
|
||||
defer t.staticLock.Unlock()
|
||||
t.statics[task.Name] = &task
|
||||
t.AddTask(&task)
|
||||
logrus.Infof("Load a static task %s.", task.Name)
|
||||
}
|
||||
|
||||
//GetTask gettask
|
||||
func (t *TaskEngine) GetTask(taskID string) *model.Task {
|
||||
t.tasksLock.Lock()
|
||||
defer t.tasksLock.Unlock()
|
||||
if task, ok := t.tasks[taskID]; ok {
|
||||
return task
|
||||
}
|
||||
res, err := store.DefalutClient.Get("/store/tasks/" + taskID)
|
||||
if err != nil {
|
||||
return nil
|
||||
@ -171,9 +266,20 @@ func (t *TaskEngine) StopTask(task *model.Task) {
|
||||
logrus.Errorf("stop task %s error.%s", task.Name, err.Error())
|
||||
}
|
||||
}
|
||||
_, err := store.DefalutClient.Delete(t.config.ExecutionRecordPath+"/"+task.JobID, client.WithPrefix())
|
||||
if err != nil {
|
||||
logrus.Errorf("delete execution record for task %s error.%s", task.Name, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//CacheTask 缓存task
|
||||
func (t *TaskEngine) CacheTask(task *model.Task) {
|
||||
t.tasksLock.Lock()
|
||||
defer t.tasksLock.Unlock()
|
||||
t.tasks[task.ID] = task
|
||||
}
|
||||
|
||||
//AddTask 添加task
|
||||
func (t *TaskEngine) AddTask(task *model.Task) error {
|
||||
oldTask := t.GetTask(task.ID)
|
||||
@ -182,8 +288,12 @@ func (t *TaskEngine) AddTask(task *model.Task) error {
|
||||
task.JobID = oldTask.JobID
|
||||
task.Status = oldTask.Status
|
||||
task.OutPut = oldTask.OutPut
|
||||
task.EventID = oldTask.EventID
|
||||
}
|
||||
}
|
||||
if task.EventID == "" {
|
||||
task.EventID = task.ID
|
||||
}
|
||||
task.Status = map[string]model.TaskStatus{}
|
||||
for _, n := range task.Nodes {
|
||||
task.Status[n] = model.TaskStatus{
|
||||
@ -194,24 +304,36 @@ func (t *TaskEngine) AddTask(task *model.Task) error {
|
||||
if task.Scheduler.Mode == "" {
|
||||
task.Scheduler.Mode = "Passive"
|
||||
}
|
||||
t.CacheTask(task)
|
||||
_, err := store.DefalutClient.Put("/store/tasks/"+task.ID, task.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if task.Scheduler.Mode == "Intime" {
|
||||
t.ScheduleTask(task.ID)
|
||||
t.ScheduleTask(nil, task)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//UpdateTask 更新task
|
||||
func (t *TaskEngine) UpdateTask(task *model.Task) {
|
||||
t.tasksLock.Lock()
|
||||
defer t.tasksLock.Unlock()
|
||||
t.tasks[task.ID] = task
|
||||
_, err := store.DefalutClient.Put("/store/tasks/"+task.ID, task.String())
|
||||
if err != nil {
|
||||
logrus.Errorf("update task error,%s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
//UpdateGroup 更新taskgroup
|
||||
func (t *TaskEngine) UpdateGroup(group *model.TaskGroup) {
|
||||
_, err := store.DefalutClient.Put("/store/taskgroups/"+group.ID, group.String())
|
||||
if err != nil {
|
||||
logrus.Errorf("update taskgroup error,%s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
//GetTaskGroup 获取taskgroup
|
||||
func (t *TaskEngine) GetTaskGroup(taskGroupID string) *model.TaskGroup {
|
||||
res, err := store.DefalutClient.Get("/store/taskgroups/" + taskGroupID)
|
||||
@ -234,114 +356,265 @@ func (t *TaskEngine) handleJobRecord(er *job.ExecutionRecord) {
|
||||
if task == nil {
|
||||
return
|
||||
}
|
||||
output, err := model.ParseTaskOutPut(er.Output)
|
||||
if err != nil {
|
||||
er.CompleteHandle()
|
||||
return
|
||||
}
|
||||
if output.Global != nil && len(output.Global) > 0 {
|
||||
for k, v := range output.Global {
|
||||
err := t.dataCenterConfig.PutConfig(&model.ConfigUnit{
|
||||
Name: strings.ToUpper(k),
|
||||
Value: v,
|
||||
ValueType: "string",
|
||||
IsConfigurable: false,
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Errorf("save datacenter config %s=%s error.%s", k, v, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
//groupID不为空,处理group连环操作
|
||||
if output.Inner != nil && len(output.Inner) > 0 && task.GroupID != "" {
|
||||
t.AddGroupConfig(task.GroupID, output.Inner)
|
||||
}
|
||||
for _, status := range output.Status {
|
||||
//install or check类型结果写入节点
|
||||
if output.Type == "install" || output.Type == "check" {
|
||||
t.nodeCluster.UpdateNodeCondition(er.Node, status.ConditionType, status.ConditionStatus)
|
||||
if status.NextTask != nil && len(status.NextTask) > 0 {
|
||||
t.ScheduleTask(status.NextTask...)
|
||||
}
|
||||
}
|
||||
}
|
||||
task.OutPut = append(task.OutPut, &output)
|
||||
//更新task信息
|
||||
defer t.UpdateTask(task)
|
||||
taskStatus := model.TaskStatus{
|
||||
StartTime: er.BeginTime,
|
||||
EndTime: er.EndTime,
|
||||
CompleStatus: "",
|
||||
}
|
||||
if er.Output != "" {
|
||||
output, err := model.ParseTaskOutPut(er.Output)
|
||||
if err != nil {
|
||||
taskStatus.Status = "Parse task output error"
|
||||
logrus.Warning("parse task output error:", err.Error())
|
||||
output.NodeID = er.Node
|
||||
|
||||
} else {
|
||||
output.NodeID = er.Node
|
||||
if output.Global != nil && len(output.Global) > 0 {
|
||||
for k, v := range output.Global {
|
||||
err := t.dataCenterConfig.PutConfig(&model.ConfigUnit{
|
||||
Name: strings.ToUpper(k),
|
||||
Value: v,
|
||||
ValueType: "string",
|
||||
IsConfigurable: false,
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Errorf("save datacenter config %s=%s error.%s", k, v, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
//groupID不为空,处理group连环操作
|
||||
if output.Inner != nil && len(output.Inner) > 0 && task.GroupID != "" {
|
||||
t.AddGroupConfig(task.GroupID, output.Inner)
|
||||
}
|
||||
for _, status := range output.Status {
|
||||
//install or check类型结果写入节点
|
||||
if output.Type == "install" || output.Type == "check" {
|
||||
if status.ConditionType != "" && status.ConditionStatus != "" {
|
||||
t.nodeCluster.UpdateNodeCondition(er.Node, status.ConditionType, status.ConditionStatus)
|
||||
}
|
||||
if status.NextTask != nil && len(status.NextTask) > 0 {
|
||||
for _, taskID := range status.NextTask {
|
||||
task := t.GetTask(taskID)
|
||||
if task == nil {
|
||||
continue
|
||||
}
|
||||
//由哪个节点发起的执行请求,当前task只在此节点执行
|
||||
t.ScheduleTask([]string{output.NodeID}, task)
|
||||
}
|
||||
}
|
||||
if status.NextGroups != nil && len(status.NextGroups) > 0 {
|
||||
for _, groupID := range status.NextGroups {
|
||||
group := t.GetTaskGroup(groupID)
|
||||
if group == nil {
|
||||
continue
|
||||
}
|
||||
t.ScheduleGroup([]string{output.NodeID}, group)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
task.UpdataOutPut(output)
|
||||
}
|
||||
if er.Success {
|
||||
taskStatus.CompleStatus = "Success"
|
||||
} else {
|
||||
taskStatus.CompleStatus = "Failure"
|
||||
}
|
||||
task.Status[output.NodeID] = taskStatus
|
||||
t.UpdateTask(task)
|
||||
er.CompleteHandle()
|
||||
if task.Status == nil {
|
||||
task.Status = make(map[string]model.TaskStatus)
|
||||
}
|
||||
task.Status[er.Node] = taskStatus
|
||||
//如果是is_once的任务,处理完成后删除job
|
||||
if task.IsOnce {
|
||||
task.CompleteTime = time.Now()
|
||||
t.StopTask(task)
|
||||
} else { //如果是一次性任务,执行记录已经被删除,无需更新
|
||||
er.CompleteHandle()
|
||||
}
|
||||
}
|
||||
func (t *TaskEngine) waitScheduleTask(nodes []string, task *model.Task) {
|
||||
canRun := func() bool {
|
||||
defer t.UpdateTask(task)
|
||||
if task.Temp.Depends != nil && len(task.Temp.Depends) > 0 {
|
||||
var result = true
|
||||
for _, dep := range task.Temp.Depends {
|
||||
if depTask := t.GetTask(dep.DependTaskID); depTask != nil {
|
||||
if depTask.Scheduler.Mode == "Passive" && depTask.Scheduler.Status == "" {
|
||||
t.ScheduleTask(nodes, depTask)
|
||||
}
|
||||
if dep.DetermineStrategy == model.AtLeastOnceStrategy {
|
||||
if len(depTask.Status) > 0 {
|
||||
result = result && true
|
||||
}
|
||||
}
|
||||
if dep.DetermineStrategy == model.SameNodeStrategy {
|
||||
if depTask.Status == nil || len(depTask.Status) < 1 {
|
||||
result = result && false
|
||||
task.Scheduler.Message = fmt.Sprintf("depend task %s is not complete", depTask.ID)
|
||||
task.Scheduler.Status = "Waiting"
|
||||
return false
|
||||
}
|
||||
if nodes != nil {
|
||||
for _, node := range nodes {
|
||||
if nodestatus, ok := depTask.Status[node]; !ok || nodestatus.EndTime.IsZero() {
|
||||
result = result && false
|
||||
task.Scheduler.Message = fmt.Sprintf("depend task %s is not complete in node %s", depTask.ID, node)
|
||||
task.Scheduler.Status = "Waiting"
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
task.Scheduler.Message = fmt.Sprintf("depend task %s is not found", depTask.ID)
|
||||
task.Scheduler.Status = "Failure"
|
||||
result = result && false
|
||||
return false
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return true
|
||||
}
|
||||
for {
|
||||
logrus.Infof("task %s can not be run .waiting depend tasks complete", task.Name)
|
||||
if canRun() {
|
||||
j, err := job.CreateJobFromTask(task, nil)
|
||||
if err != nil {
|
||||
task.Scheduler.Status = "Failure"
|
||||
task.Scheduler.Message = err.Error()
|
||||
t.UpdateTask(task)
|
||||
logrus.Errorf("run task %s error.%s", task.Name, err.Error())
|
||||
return
|
||||
}
|
||||
//如果指定nodes
|
||||
if nodes != nil {
|
||||
for _, rule := range j.Rules {
|
||||
rule.NodeIDs = nodes
|
||||
}
|
||||
}
|
||||
if j.IsOnce {
|
||||
if err := job.PutOnce(j); err != nil {
|
||||
task.Scheduler.Status = "Failure"
|
||||
task.Scheduler.Message = err.Error()
|
||||
t.UpdateTask(task)
|
||||
logrus.Errorf("run task %s error.%s", task.Name, err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := job.AddJob(j); err != nil {
|
||||
task.Scheduler.Status = "Failure"
|
||||
task.Scheduler.Message = err.Error()
|
||||
t.UpdateTask(task)
|
||||
logrus.Errorf("run task %s error.%s", task.Name, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
task.JobID = j.ID
|
||||
task.StartTime = time.Now()
|
||||
task.Scheduler.Status = "Success"
|
||||
task.Scheduler.Message = "scheduler success"
|
||||
t.UpdateTask(task)
|
||||
return
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
//ScheduleTask 调度执行指定task
|
||||
func (t *TaskEngine) ScheduleTask(nextTask ...string) {
|
||||
for _, taskID := range nextTask {
|
||||
task := t.GetTask(taskID)
|
||||
func (t *TaskEngine) ScheduleTask(nodes []string, nextTask ...*model.Task) {
|
||||
for _, task := range nextTask {
|
||||
if task == nil {
|
||||
continue
|
||||
}
|
||||
if task.JobID != "" {
|
||||
t.StopTask(task)
|
||||
}
|
||||
j, err := job.CreateJobFromTask(task, nil)
|
||||
if err != nil {
|
||||
task.Scheduler.Status = "Failure"
|
||||
task.Scheduler.Message = err.Error()
|
||||
t.UpdateTask(task)
|
||||
logrus.Errorf("run task %s error.%s", task.Name, err.Error())
|
||||
return
|
||||
if task.Temp == nil {
|
||||
continue
|
||||
}
|
||||
if j.IsOnce {
|
||||
if err := job.PutOnce(j); err != nil {
|
||||
task.Scheduler.Status = "Failure"
|
||||
task.Scheduler.Message = err.Error()
|
||||
t.UpdateTask(task)
|
||||
logrus.Errorf("run task %s error.%s", task.Name, err.Error())
|
||||
return
|
||||
}
|
||||
if nodes == nil {
|
||||
nodes = task.Nodes
|
||||
}
|
||||
if task.Temp.Depends != nil && len(task.Temp.Depends) > 0 {
|
||||
go t.waitScheduleTask(nodes, task)
|
||||
task.Scheduler.Status = "Waiting"
|
||||
} else {
|
||||
if err := job.AddJob(j); err != nil {
|
||||
logrus.Infof("scheduler a task %s", task.Name)
|
||||
j, err := job.CreateJobFromTask(task, nil)
|
||||
if err != nil {
|
||||
task.Scheduler.Status = "Failure"
|
||||
task.Scheduler.Message = err.Error()
|
||||
t.UpdateTask(task)
|
||||
logrus.Errorf("run task %s error.%s", task.Name, err.Error())
|
||||
return
|
||||
}
|
||||
//如果指定nodes
|
||||
if nodes != nil {
|
||||
for _, rule := range j.Rules {
|
||||
rule.NodeIDs = nodes
|
||||
}
|
||||
}
|
||||
if j.IsOnce {
|
||||
if err := job.PutOnce(j); err != nil {
|
||||
task.Scheduler.Status = "Failure"
|
||||
task.Scheduler.Message = err.Error()
|
||||
t.UpdateTask(task)
|
||||
logrus.Errorf("run task %s error.%s", task.Name, err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := job.AddJob(j); err != nil {
|
||||
task.Scheduler.Status = "Failure"
|
||||
task.Scheduler.Message = err.Error()
|
||||
t.UpdateTask(task)
|
||||
logrus.Errorf("run task %s error.%s", task.Name, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
task.JobID = j.ID
|
||||
task.StartTime = time.Now()
|
||||
task.Scheduler.Status = "Success"
|
||||
task.Scheduler.Message = "scheduler success"
|
||||
}
|
||||
task.JobID = j.ID
|
||||
t.UpdateTask(task)
|
||||
}
|
||||
}
|
||||
|
||||
//ScheduleGroup 调度执行指定task
|
||||
func (t *TaskEngine) ScheduleGroup(nextGroups ...string) {
|
||||
for _, groupID := range nextGroups {
|
||||
_ = t.GetTaskGroup(groupID)
|
||||
func (t *TaskEngine) ScheduleGroup(nodes []string, nextGroups ...*model.TaskGroup) {
|
||||
for _, group := range nextGroups {
|
||||
if group.Tasks == nil || len(group.Tasks) < 1 {
|
||||
group.Status = &model.TaskGroupStatus{
|
||||
StartTime: time.Now(),
|
||||
EndTime: time.Now(),
|
||||
Status: "NotDefineTask",
|
||||
}
|
||||
t.UpdateGroup(group)
|
||||
}
|
||||
for _, task := range group.Tasks {
|
||||
task.GroupID = group.ID
|
||||
t.AddTask(task)
|
||||
}
|
||||
group.Status = &model.TaskGroupStatus{
|
||||
StartTime: time.Now(),
|
||||
Status: "Start",
|
||||
}
|
||||
t.UpdateGroup(group)
|
||||
}
|
||||
}
|
||||
|
||||
//AddGroupConfig 添加组会话配置
|
||||
func (t *TaskEngine) AddGroupConfig(groupID string, configs map[string]string) {
|
||||
t.groupLock.Lock()
|
||||
defer t.groupLock.Unlock()
|
||||
if ctx, ok := t.groupContexts[groupID]; ok {
|
||||
for k, v := range configs {
|
||||
ctx.Add(k, v)
|
||||
}
|
||||
} else {
|
||||
ctx := config.NewGroupContext()
|
||||
for k, v := range configs {
|
||||
ctx.Add(k, v)
|
||||
}
|
||||
t.groupContexts[groupID] = ctx
|
||||
ctx := config.NewGroupContext(groupID)
|
||||
for k, v := range configs {
|
||||
ctx.Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
|
41
pkg/node/masterserver/task_engine_test.go
Normal file
41
pkg/node/masterserver/task_engine_test.go
Normal file
@ -0,0 +1,41 @@
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package masterserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goodrain/rainbond/pkg/node/api/model"
|
||||
)
|
||||
|
||||
func TestGroupWorker(t *testing.T) {
|
||||
taskEngine := CreateTaskEngine(nil)
|
||||
group := &model.TaskGroup{
|
||||
Tasks: []*model.Task{
|
||||
&model.Task{ID: "1", Temp: &model.TaskTemp{Depends: []model.DependStrategy{model.DependStrategy{DependTaskID: "5"}}}},
|
||||
&model.Task{ID: "2", Temp: &model.TaskTemp{Depends: []model.DependStrategy{model.DependStrategy{DependTaskID: "5"}}}},
|
||||
&model.Task{ID: "3", Temp: &model.TaskTemp{Depends: []model.DependStrategy{model.DependStrategy{DependTaskID: "5"}}}},
|
||||
&model.Task{ID: "4", Temp: &model.TaskTemp{Depends: []model.DependStrategy{}}},
|
||||
&model.Task{ID: "5", Temp: &model.TaskTemp{}},
|
||||
&model.Task{ID: "6", Temp: &model.TaskTemp{}},
|
||||
&model.Task{ID: "7", Temp: &model.TaskTemp{}},
|
||||
},
|
||||
}
|
||||
taskEngine.ScheduleGroup(nil, group)
|
||||
}
|
@ -705,7 +705,7 @@ func (p *PodTemplateSpecBuild) createPluginsContainer(mainEnvs *[]v1.EnvVar) ([]
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
envs, err := p.createPluginEnvs(pluginR.PluginID)
|
||||
envs, err := p.createPluginEnvs(pluginR.PluginID, mainEnvs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -729,7 +729,7 @@ func (p *PodTemplateSpecBuild) createPluginsContainer(mainEnvs *[]v1.EnvVar) ([]
|
||||
initContainers = append(initContainers, pc)
|
||||
continue
|
||||
}
|
||||
if pluginModel == model.NetPlugin {
|
||||
if pluginModel == model.UpNetPlugin || pluginModel == model.DownNetPlugin {
|
||||
netPlugin = true
|
||||
}
|
||||
containers = append(containers, pc)
|
||||
@ -773,7 +773,7 @@ func (p *PodTemplateSpecBuild) createPluginArgs(pluginID string) ([]string, erro
|
||||
}
|
||||
|
||||
//container envs
|
||||
func (p *PodTemplateSpecBuild) createPluginEnvs(pluginID string) (*[]v1.EnvVar, error) {
|
||||
func (p *PodTemplateSpecBuild) createPluginEnvs(pluginID string, mainEnvs *[]v1.EnvVar) (*[]v1.EnvVar, error) {
|
||||
defaultEnvs, err := p.dbmanager.TenantPluginDefaultENVDao().GetDefaultENVSByPluginIDCantBeSet(pluginID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -789,6 +789,9 @@ func (p *PodTemplateSpecBuild) createPluginEnvs(pluginID string) (*[]v1.EnvVar,
|
||||
for _, e := range versionEnvs {
|
||||
envs = append(envs, v1.EnvVar{Name: e.EnvName, Value: e.EnvValue})
|
||||
}
|
||||
for _, e := range *mainEnvs {
|
||||
envs = append(envs, e)
|
||||
}
|
||||
//TODO: 在哪些情况下需要注入主容器的环境变量
|
||||
return &envs, nil
|
||||
}
|
||||
@ -822,8 +825,10 @@ func (p *PodTemplateSpecBuild) sortPlugins() ([]string, error) {
|
||||
|
||||
func (p *PodTemplateSpecBuild) pluginWeight(pluginModel string) int {
|
||||
switch pluginModel {
|
||||
case model.NetPlugin:
|
||||
case model.UpNetPlugin:
|
||||
return 9
|
||||
case model.DownNetPlugin:
|
||||
return 8
|
||||
case model.GeneralPlugin:
|
||||
return 1
|
||||
default:
|
||||
|
@ -116,8 +116,9 @@ func (k *K8sServiceBuild) createInnerService(port *model.TenantServicesPort) *v1
|
||||
var service v1.Service
|
||||
service.Name = fmt.Sprintf("service-%d-%d", port.ID, port.ContainerPort)
|
||||
service.Labels = map[string]string{
|
||||
"service_type": "inner",
|
||||
"name": k.service.ServiceAlias + "Service",
|
||||
"service_type": "inner",
|
||||
"name": k.service.ServiceAlias + "Service",
|
||||
"port_protocol": port.Protocol,
|
||||
}
|
||||
var servicePort v1.ServicePort
|
||||
if port.Protocol == "udp" {
|
||||
|
66
release.sh
Executable file
66
release.sh
Executable file
@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
set -o errexit
|
||||
|
||||
# define package name
|
||||
PROGRAM="gr-rainbond-node"
|
||||
WORK_DIR=/go/src/github.com/goodrain/rainbond
|
||||
BASE_NAME=rainbond
|
||||
releasedir=./.release
|
||||
distdir=${releasedir}/dist
|
||||
gaops='git@code.goodrain.com:goodrain/gaops.git'
|
||||
|
||||
|
||||
gitDescribe=$(git describe --tag|sed 's/^v//')
|
||||
describe_items=($(echo $gitDescribe | tr '-' ' '))
|
||||
describe_len=${#describe_items[@]}
|
||||
VERSION=${describe_items[0]}
|
||||
git_commit=$(git log -n 1 --pretty --format=%h)
|
||||
if [ $describe_len -ge 3 ];then
|
||||
buildRelease=${describe_items[-2]}.${describe_items[-1]}
|
||||
else
|
||||
buildRelease=0.$git_commit
|
||||
fi
|
||||
if [ -z "$VERSION" ];then
|
||||
VERSION=3.4
|
||||
fi
|
||||
|
||||
function prepare() {
|
||||
rm -rf $releasedir
|
||||
mkdir -pv $releasedir/{tmp,dist}
|
||||
path=$PWD
|
||||
git clone $gaops $releasedir/tmp
|
||||
[ ! -d "$distdir/usr/local/" ] && mkdir -p $distdir/usr/local/bin
|
||||
[ ! -d "$distdir/usr/share/gr-rainbond-node/gaops/" ] && mkdir -pv $distdir/usr/share/gr-rainbond-node/gaops
|
||||
cd $releasedir/tmp
|
||||
rm -rf .git
|
||||
|
||||
tar zcvf ../dist/usr/share/gr-rainbond-node/gaops/gaops.tgz ./
|
||||
cd $path
|
||||
rm -rf $releasedir/tmp
|
||||
}
|
||||
|
||||
function build() {
|
||||
echo "---> build image"
|
||||
docker run -v `pwd`:${WORK_DIR} -w ${WORK_DIR} -it golang:1.8.3 go build -ldflags '-w -s' -o ./build/node/${BASE_NAME}-node ./cmd/node
|
||||
mv $PWD/build/node/${BASE_NAME}-node $releasedir/dist/usr/local/bin/
|
||||
}
|
||||
|
||||
function build::rpm() {
|
||||
echo "---> Make Build RPM"
|
||||
source "hack/build-rpm.sh"
|
||||
}
|
||||
|
||||
case $1 in
|
||||
build)
|
||||
prepare
|
||||
build
|
||||
;;
|
||||
rpm)
|
||||
build::rpm
|
||||
;;
|
||||
*)
|
||||
prepare
|
||||
build
|
||||
build::rpm
|
||||
;;
|
||||
esac
|
1
test/shell/echo1.sh
Normal file
1
test/shell/echo1.sh
Normal file
@ -0,0 +1 @@
|
||||
echo "{\"inner\":{\"HOME_PATH\":\"/home\"},\"type\":\"check\",\"status\":[{\"next_tasks\":[\"echo2\"]}]}" >&2
|
1
test/shell/echo2.sh
Normal file
1
test/shell/echo2.sh
Normal file
@ -0,0 +1 @@
|
||||
echo "{\"inner\":{\"HOME_PATH\":\"/home\"},\"type\":\"install\",\"status\":[{\"condition_type\":\"INIT_ECHO\",\"condition_status\":\"True\"}]}" >&2
|
@ -4,7 +4,7 @@
|
||||
"temp": {
|
||||
"name": "echo-temp",
|
||||
"shell": {
|
||||
"cmd": ["echo", "\"mysql host is:${MYSQL_HOST}\""]
|
||||
"cmd": ["echo", "\"mysql host is:${MYSQL_HOST} dependsss\""]
|
||||
}
|
||||
},
|
||||
"event_id": "xxx",
|
@ -5,7 +5,11 @@
|
||||
"name": "echo-once-temp",
|
||||
"shell": {
|
||||
"cmd": ["echo", "\"mysql host is:${MYSQL_HOST}\""]
|
||||
}
|
||||
},
|
||||
"depends": [{
|
||||
"depend_task_id": "echo",
|
||||
"strategy": "AtLeastOnce"
|
||||
}]
|
||||
},
|
||||
"event_id": "xyxyxyxyxy",
|
||||
"is_once": true,
|
45
test/tasks/echo_group.json
Normal file
45
test/tasks/echo_group.json
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "echo-group",
|
||||
"id": "echo-group",
|
||||
"tasks": [{
|
||||
"name": "echo2",
|
||||
"id": "echo2",
|
||||
"temp": {
|
||||
"name": "echo-temp",
|
||||
"shell": {
|
||||
"cmd": ["sh", "/Users/qingguo/gopath/src/github.com/goodrain/rainbond/test/shell/echo2.sh"]
|
||||
}
|
||||
},
|
||||
"event_id": "xxx",
|
||||
"is_once": true
|
||||
}, {
|
||||
"name": "echo1",
|
||||
"id": "echo1",
|
||||
"temp": {
|
||||
"name": "echo-temp",
|
||||
"shell": {
|
||||
"cmd": ["sh", "/Users/qingguo/gopath/src/github.com/goodrain/rainbond/test/shell/echo1.sh"]
|
||||
}
|
||||
},
|
||||
"event_id": "xxx",
|
||||
"is_once": true
|
||||
}, {
|
||||
"name": "echo-once",
|
||||
"id": "echo-once",
|
||||
"temp": {
|
||||
"name": "echo-once-temp",
|
||||
"shell": {
|
||||
"cmd": ["echo", "XXXXX"]
|
||||
},
|
||||
"depends": [{
|
||||
"depend_task_id": "echo1",
|
||||
"strategy": "AtLeastOnce"
|
||||
}]
|
||||
},
|
||||
"event_id": "xyxyxyxyxy",
|
||||
"is_once": true,
|
||||
"scheduler": {
|
||||
"mode": "Intime"
|
||||
}
|
||||
}]
|
||||
}
|
0
vendor/gopkg.in/mgo.v2/bson/LICENSE → vendor/gopkg.in.o/mgo.v2/bson/LICENSE
generated
vendored
0
vendor/gopkg.in/mgo.v2/bson/LICENSE → vendor/gopkg.in.o/mgo.v2/bson/LICENSE
generated
vendored
0
vendor/gopkg.in/mgo.v2/bson/bson.go → vendor/gopkg.in.o/mgo.v2/bson/bson.go
generated
vendored
0
vendor/gopkg.in/mgo.v2/bson/bson.go → vendor/gopkg.in.o/mgo.v2/bson/bson.go
generated
vendored
0
vendor/gopkg.in/mgo.v2/bson/decimal.go → vendor/gopkg.in.o/mgo.v2/bson/decimal.go
generated
vendored
0
vendor/gopkg.in/mgo.v2/bson/decimal.go → vendor/gopkg.in.o/mgo.v2/bson/decimal.go
generated
vendored
0
vendor/gopkg.in/mgo.v2/bson/decode.go → vendor/gopkg.in.o/mgo.v2/bson/decode.go
generated
vendored
0
vendor/gopkg.in/mgo.v2/bson/decode.go → vendor/gopkg.in.o/mgo.v2/bson/decode.go
generated
vendored
0
vendor/gopkg.in/mgo.v2/bson/encode.go → vendor/gopkg.in.o/mgo.v2/bson/encode.go
generated
vendored
0
vendor/gopkg.in/mgo.v2/bson/encode.go → vendor/gopkg.in.o/mgo.v2/bson/encode.go
generated
vendored
0
vendor/gopkg.in/mgo.v2/bson/json.go → vendor/gopkg.in.o/mgo.v2/bson/json.go
generated
vendored
0
vendor/gopkg.in/mgo.v2/bson/json.go → vendor/gopkg.in.o/mgo.v2/bson/json.go
generated
vendored
Loading…
Reference in New Issue
Block a user