Merge remote-tracking branch 'origin/master'

This commit is contained in:
bay1ts 2017-11-16 12:00:10 +08:00
commit f943fffb69
90 changed files with 1287 additions and 413 deletions

45
.gitignore vendored Normal file
View 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/

View File

@ -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.

View File

@ -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}

View File

@ -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)

View File

@ -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
View File

View 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
View File

View 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
View File

@ -0,0 +1,12 @@
# 云帮解决用户痛点
---
## 微服务架构落地
“微服务架构”概念自提出以来,深受业界喜爱。各类历史架构纷纷转向微服务架构。企业如何快速解决传统应用服务化,服务治理,服务发现,服务监控等等难题从而获得微服务架构带来的种种优势?云帮,原生的微服务管理平台,无需任何设置,默认提供微服务高效管理服务。也可以通过云帮强大的应用插件体系带来丰富多彩的微服务治理扩展,使企业微服务架构落地完全无障碍。
## 持续集成/持续交付落地
云帮作为以应用为中心的交付平台,支持各类流行开发语言一键部署,自动部署。商业应用对接好雨云市平台,应用提供商可以快速交付生产级应用环境。对于企业自研应用,云帮助你持续构建应用,平滑升级应用,无需人工干预。
## 复杂架构快速上云
云帮平台运行各类应用数据库消息中间件Web应用大数据处理应用人工智能应用等等你的各类型复杂的架构经过简单得改造即可运行于云帮平台即可实现云端扩展。
## 高并发应用快速伸缩
云帮借助Docker,Kubernetes等容器技术作为应用的运行载体赋予能够应用快速伸缩得特性。云帮应用级实时性能分析系统提供数据参考实现应用按需自动伸缩。或者用户指定伸缩策略例如指定高峰时间段实现半自动应用伸缩。
## 海量应用高效便捷管理
云帮作为以应用为中心的跨数据中心应用管理平台一套平台具有海量的计算能力。云帮智能化自动化的资源管理系统让用户无需投入时间精力关注物理资源。海量的应用同一个管理方式。使用一个简单的操作UI,即可对海量的应用实现多元化的控制。

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

28
hack/build-rpm.sh Executable file
View 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

View 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

View File

@ -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

View 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
View File

View 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)

View File

@ -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

View File

@ -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")

View File

@ -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",
}

View File

@ -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":"",

View File

@ -330,7 +330,7 @@ type DeletePluginSetStruct struct {
}
//GetPluginEnvStruct GetPluginEnvStruct
//swagger:parameters getPluginEnv
//swagger:parameters getPluginEnv, getPluginDefaultEnv
type GetPluginEnvStruct struct {
// in: path
// required: true

View File

@ -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"

View File

@ -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}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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"
)

View File

@ -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 {

View File

@ -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 任务状态

View File

@ -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
}

View File

@ -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)\$\{.*\}`)

View File

@ -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

View File

@ -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

View File

@ -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 ""
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}
}

View 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)
}

View File

@ -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:

View File

@ -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
View 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
View File

@ -0,0 +1 @@
echo "{\"inner\":{\"HOME_PATH\":\"/home\"},\"type\":\"check\",\"status\":[{\"next_tasks\":[\"echo2\"]}]}" >&2

1
test/shell/echo2.sh Normal file
View File

@ -0,0 +1 @@
echo "{\"inner\":{\"HOME_PATH\":\"/home\"},\"type\":\"install\",\"status\":[{\"condition_type\":\"INIT_ECHO\",\"condition_status\":\"True\"}]}" >&2

View File

@ -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",

View File

@ -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,

View 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"
}
}]
}